/*========================================================================= Program: KWSys - Kitware System Library Module: $RCSfile$ Copyright (c) Kitware, Inc., Insight Consortium. All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "kwsysPrivate.h" #include KWSYS_HEADER(Process.h) /* Work-around CMake dependency scanning limitation. This must duplicate the above list of headers. */ #if 0 # include "Process.h.in" #endif #include #include #include #if defined(_WIN32) # include #else # include #endif int runChild(const char* cmd[], int state, int exception, int value, int share, int output, int delay, double timeout, int poll, int repeat); int test1(int argc, const char* argv[]) { (void)argc; (void)argv; fprintf(stdout, "Output on stdout from test returning 0.\n"); fprintf(stderr, "Output on stderr from test returning 0.\n"); return 0; } int test2(int argc, const char* argv[]) { (void)argc; (void)argv; fprintf(stdout, "Output on stdout from test returning 123.\n"); fprintf(stderr, "Output on stderr from test returning 123.\n"); return 123; } int test3(int argc, const char* argv[]) { (void)argc; (void)argv; fprintf(stdout, "Output before sleep on stdout from timeout test.\n"); fprintf(stderr, "Output before sleep on stderr from timeout test.\n"); fflush(stdout); fflush(stderr); #if defined(_WIN32) Sleep(15000); #else sleep(15); #endif fprintf(stdout, "Output after sleep on stdout from timeout test.\n"); fprintf(stderr, "Output after sleep on stderr from timeout test.\n"); return 0; } int test4(int argc, const char* argv[]) { #if defined(_WIN32) /* Avoid error diagnostic popups since we are crashing on purpose. */ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #endif (void)argc; (void)argv; fprintf(stdout, "Output before crash on stdout from crash test.\n"); fprintf(stderr, "Output before crash on stderr from crash test.\n"); fflush(stdout); fflush(stderr); *(int*)0 = 0; fprintf(stdout, "Output after crash on stdout from crash test.\n"); fprintf(stderr, "Output after crash on stderr from crash test.\n"); return 0; } int test5(int argc, const char* argv[]) { int r; const char* cmd[4]; (void)argc; cmd[0] = argv[0]; cmd[1] = "run"; cmd[2] = "4"; cmd[3] = 0; fprintf(stdout, "Output on stdout before recursive test.\n"); fprintf(stderr, "Output on stderr before recursive test.\n"); fflush(stdout); fflush(stderr); r = runChild(cmd, kwsysProcess_State_Exception, kwsysProcess_Exception_Fault, 1, 1, 1, 0, 15, 0, 1); fprintf(stdout, "Output on stdout after recursive test.\n"); fprintf(stderr, "Output on stderr after recursive test.\n"); fflush(stdout); fflush(stderr); return r; } #define TEST6_SIZE (4096*2) void test6(int argc, const char* argv[]) { int i; char runaway[TEST6_SIZE+1]; (void)argc; (void)argv; for(i=0;i < TEST6_SIZE;++i) { runaway[i] = '.'; } runaway[TEST6_SIZE] = '\n'; /* Generate huge amounts of output to test killing. */ for(;;) { fwrite(runaway, 1, TEST6_SIZE+1, stdout); fflush(stdout); } } /* Define MINPOLL to be one more than the number of times output is written. Define MAXPOLL to be the largest number of times a loop delaying 1/10th of a second should ever have to poll. */ #define MINPOLL 5 #define MAXPOLL 20 int test7(int argc, const char* argv[]) { (void)argc; (void)argv; fprintf(stdout, "Output on stdout before sleep.\n"); fprintf(stderr, "Output on stderr before sleep.\n"); fflush(stdout); fflush(stderr); /* Sleep for 1 second. */ #if defined(_WIN32) Sleep(1000); #else sleep(1); #endif fprintf(stdout, "Output on stdout after sleep.\n"); fprintf(stderr, "Output on stderr after sleep.\n"); fflush(stdout); fflush(stderr); return 0; } int runChild2(kwsysProcess* kp, const char* cmd[], int state, int exception, int value, int share, int output, int delay, double timeout, int poll) { int result = 0; char* data = 0; int length = 0; double userTimeout = 0; double* pUserTimeout = 0; kwsysProcess_SetCommand(kp, cmd); if(timeout >= 0) { kwsysProcess_SetTimeout(kp, timeout); } if(share) { kwsysProcess_SetPipeShared(kp, kwsysProcess_Pipe_STDOUT, 1); kwsysProcess_SetPipeShared(kp, kwsysProcess_Pipe_STDERR, 1); } kwsysProcess_Execute(kp); if(poll) { pUserTimeout = &userTimeout; } if(!share) { int p; while((p = kwsysProcess_WaitForData(kp, &data, &length, pUserTimeout))) { if(output) { if(poll && p == kwsysProcess_Pipe_Timeout) { fprintf(stdout, "WaitForData timeout reached.\n"); fflush(stdout); /* Count the number of times we polled without getting data. If it is excessive then kill the child and fail. */ if(++poll >= MAXPOLL) { fprintf(stdout, "Poll count reached limit %d.\n", MAXPOLL); kwsysProcess_Kill(kp); } } else { fwrite(data, 1, length, stdout); fflush(stdout); } } if(poll) { /* Delay to avoid busy loop during polling. */ #if defined(_WIN32) Sleep(100); #else usleep(100000); #endif } if(delay) { /* Purposely sleeping only on Win32 to let pipe fill up. */ #if defined(_WIN32) Sleep(100); #endif } } } kwsysProcess_WaitForExit(kp, 0); switch (kwsysProcess_GetState(kp)) { case kwsysProcess_State_Starting: printf("No process has been executed.\n"); break; case kwsysProcess_State_Executing: printf("The process is still executing.\n"); break; case kwsysProcess_State_Expired: printf("Child was killed when timeout expired.\n"); break; case kwsysProcess_State_Exited: printf("Child exited with value = %d\n", kwsysProcess_GetExitValue(kp)); result = ((exception != kwsysProcess_GetExitException(kp)) || (value != kwsysProcess_GetExitValue(kp))); break; case kwsysProcess_State_Killed: printf("Child was killed by parent.\n"); break; case kwsysProcess_State_Exception: printf("Child terminated abnormally: %s\n", kwsysProcess_GetExceptionString(kp)); result = ((exception != kwsysProcess_GetExitException(kp)) || (value != kwsysProcess_GetExitValue(kp))); break; case kwsysProcess_State_Error: printf("Error in administrating child process: [%s]\n", kwsysProcess_GetErrorString(kp)); break; }; if(result) { if(exception != kwsysProcess_GetExitException(kp)) { fprintf(stderr, "Mismatch in exit exception. " "Should have been %d, was %d.\n", exception, kwsysProcess_GetExitException(kp)); } if(value != kwsysProcess_GetExitValue(kp)) { fprintf(stderr, "Mismatch in exit value. " "Should have been %d, was %d.\n", value, kwsysProcess_GetExitValue(kp)); } } if(kwsysProcess_GetState(kp) != state) { fprintf(stderr, "Mismatch in state. " "Should have been %d, was %d.\n", state, kwsysProcess_GetState(kp)); result = 1; } /* We should have polled more times than there were data if polling was enabled. */ if(poll && poll < MINPOLL) { fprintf(stderr, "Poll count is %d, which is less than %d.\n", poll, MINPOLL); result = 1; } return result; } int runChild(const char* cmd[], int state, int exception, int value, int share, int output, int delay, double timeout, int poll, int repeat) { int result = 1; kwsysProcess* kp = kwsysProcess_New(); if(!kp) { fprintf(stderr, "kwsysProcess_New returned NULL!\n"); return 1; } while(repeat-- > 0) { result = runChild2(kp, cmd, state, exception, value, share, output, delay, timeout, poll); } kwsysProcess_Delete(kp); return result; } int main(int argc, const char* argv[]) { int n = 0; #if 0 { HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); DuplicateHandle(GetCurrentProcess(), out, GetCurrentProcess(), &out, 0, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); SetStdHandle(STD_OUTPUT_HANDLE, out); } { HANDLE out = GetStdHandle(STD_ERROR_HANDLE); DuplicateHandle(GetCurrentProcess(), out, GetCurrentProcess(), &out, 0, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); SetStdHandle(STD_ERROR_HANDLE, out); } #endif if(argc == 2) { n = atoi(argv[1]); } else if(argc == 3) { n = atoi(argv[2]); } /* Check arguments. */ if(n < 1 || n > 7 || (argc == 3 && strcmp(argv[1], "run") != 0)) { fprintf(stdout, "Usage: %s \n", argv[0]); return 1; } if(argc == 3) { switch (n) { case 1: return test1(argc, argv); case 2: return test2(argc, argv); case 3: return test3(argc, argv); case 4: return test4(argc, argv); case 5: return test5(argc, argv); case 6: test6(argc, argv); return 0; case 7: return test7(argc, argv); } fprintf(stderr, "Invalid test number %d.\n", n); return 1; } if(n >= 0 && n <= 7) { int states[7] = { kwsysProcess_State_Exited, kwsysProcess_State_Exited, kwsysProcess_State_Expired, kwsysProcess_State_Exception, kwsysProcess_State_Exited, kwsysProcess_State_Expired, kwsysProcess_State_Exited }; int exceptions[7] = { kwsysProcess_Exception_None, kwsysProcess_Exception_None, kwsysProcess_Exception_None, kwsysProcess_Exception_Fault, kwsysProcess_Exception_None, kwsysProcess_Exception_None, kwsysProcess_Exception_None }; int values[7] = {0, 123, 1, 1, 0, 0, 0}; int outputs[7] = {1, 1, 1, 1, 1, 0, 1}; int delays[7] = {0, 0, 0, 0, 0, 1, 0}; double timeouts[7] = {10, 10, 10, 10, 30, 10, -1}; int polls[7] = {0, 0, 0, 0, 0, 0, 1}; int repeat[7] = {2, 1, 1, 1, 1, 1, 1}; int r; const char* cmd[4]; #ifdef _WIN32 char* argv0 = 0; if(n == 0 && (argv0 = strdup(argv[0]))) { /* Try converting to forward slashes to see if it works. */ char* c; for(c=argv0; *c; ++c) { if(*c == '\\') { *c = '/'; } } cmd[0] = argv0; } else { cmd[0] = argv[0]; } #else cmd[0] = argv[0]; #endif cmd[1] = "run"; cmd[2] = argv[1]; cmd[3] = 0; fprintf(stdout, "Output on stdout before test %d.\n", n); fprintf(stderr, "Output on stderr before test %d.\n", n); fflush(stdout); fflush(stderr); r = runChild(cmd, states[n-1], exceptions[n-1], values[n-1], 0, outputs[n-1], delays[n-1], timeouts[n-1], polls[n-1], repeat[n-1]); fprintf(stdout, "Output on stdout after test %d.\n", n); fprintf(stderr, "Output on stderr after test %d.\n", n); fflush(stdout); fflush(stderr); #if _WIN32 if(argv0) { free(argv0); } #endif return r; } else { fprintf(stderr, "Test number out of range\n"); return 1; } }