1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
3 #include "kwsysPrivate.h"
4 #include KWSYS_HEADER(Process.h)
5 #include KWSYS_HEADER(Encoding.h)
6 
7 /* Work-around CMake dependency scanning limitation.  This must
8    duplicate the above list of headers.  */
9 #if 0
10 #  include "Encoding.h.in"
11 #  include "Process.h.in"
12 #endif
13 
14 #include <assert.h>
15 #include <limits.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #if defined(_WIN32)
21 #  include <windows.h>
22 #else
23 #  include <signal.h>
24 #  include <unistd.h>
25 #endif
26 
27 /* Platform-specific sleep functions. */
28 
29 #if defined(__BEOS__) && !defined(__ZETA__)
30 /* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */
31 #  include <be/kernel/OS.h>
testProcess_usleep(unsigned int usec)32 static inline void testProcess_usleep(unsigned int usec)
33 {
34   snooze(usec);
35 }
36 #elif defined(_WIN32)
37 /* Windows can only sleep in millisecond intervals. */
testProcess_usleep(unsigned int usec)38 static void testProcess_usleep(unsigned int usec)
39 {
40   Sleep(usec / 1000);
41 }
42 #else
43 #  define testProcess_usleep usleep
44 #endif
45 
46 #if defined(_WIN32)
testProcess_sleep(unsigned int sec)47 static void testProcess_sleep(unsigned int sec)
48 {
49   Sleep(sec * 1000);
50 }
51 #else
testProcess_sleep(unsigned int sec)52 static void testProcess_sleep(unsigned int sec)
53 {
54   sleep(sec);
55 }
56 #endif
57 
58 int runChild(const char* cmd[], int state, int exception, int value, int share,
59              int output, int delay, double timeout, int poll, int repeat,
60              int disown, int createNewGroup, unsigned int interruptDelay);
61 
test1(int argc,const char * argv[])62 static int test1(int argc, const char* argv[])
63 {
64   /* This is a very basic functional test of kwsysProcess.  It is repeated
65      numerous times to verify that there are no resource leaks in kwsysProcess
66      that eventually lead to an error.  Many versions of OS X will fail after
67      256 leaked file handles, so 257 iterations seems to be a good test.  On
68      the other hand, too many iterations will cause the test to time out -
69      especially if the test is instrumented with e.g. valgrind.
70 
71      If you have problems with this test timing out on your system, or want to
72      run more than 257 iterations, you can change the number of iterations by
73      setting the KWSYS_TEST_PROCESS_1_COUNT environment variable.  */
74   (void)argc;
75   (void)argv;
76   fprintf(stdout, "Output on stdout from test returning 0.\n");
77   fprintf(stderr, "Output on stderr from test returning 0.\n");
78   return 0;
79 }
80 
test2(int argc,const char * argv[])81 static int test2(int argc, const char* argv[])
82 {
83   (void)argc;
84   (void)argv;
85   fprintf(stdout, "Output on stdout from test returning 123.\n");
86   fprintf(stderr, "Output on stderr from test returning 123.\n");
87   return 123;
88 }
89 
test3(int argc,const char * argv[])90 static int test3(int argc, const char* argv[])
91 {
92   (void)argc;
93   (void)argv;
94   fprintf(stdout, "Output before sleep on stdout from timeout test.\n");
95   fprintf(stderr, "Output before sleep on stderr from timeout test.\n");
96   fflush(stdout);
97   fflush(stderr);
98   testProcess_sleep(15);
99   fprintf(stdout, "Output after sleep on stdout from timeout test.\n");
100   fprintf(stderr, "Output after sleep on stderr from timeout test.\n");
101   return 0;
102 }
103 
test4(int argc,const char * argv[])104 static int test4(int argc, const char* argv[])
105 {
106 #ifndef CRASH_USING_ABORT
107   /* Prepare a pointer to an invalid address.  Don't use null, because
108   dereferencing null is undefined behaviour and compilers are free to
109   do whatever they want. ex: Clang will warn at compile time, or even
110   optimize away the write. We hope to 'outsmart' them by using
111   'volatile' and a slightly larger address, based on a runtime value. */
112   volatile int* invalidAddress = 0;
113   invalidAddress += argc ? 1 : 2;
114 #endif
115 
116 #if defined(_WIN32)
117   /* Avoid error diagnostic popups since we are crashing on purpose.  */
118   SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
119 #elif defined(__BEOS__) || defined(__HAIKU__)
120   /* Avoid error diagnostic popups since we are crashing on purpose.  */
121   disable_debugger(1);
122 #endif
123   (void)argc;
124   (void)argv;
125   fprintf(stdout, "Output before crash on stdout from crash test.\n");
126   fprintf(stderr, "Output before crash on stderr from crash test.\n");
127   fflush(stdout);
128   fflush(stderr);
129 #ifdef CRASH_USING_ABORT
130   abort();
131 #else
132   assert(invalidAddress); /* Quiet Clang scan-build. */
133   /* Provoke deliberate crash by writing to the invalid address. */
134   *invalidAddress = 0;
135 #endif
136   fprintf(stdout, "Output after crash on stdout from crash test.\n");
137   fprintf(stderr, "Output after crash on stderr from crash test.\n");
138   return 0;
139 }
140 
test5(int argc,const char * argv[])141 static int test5(int argc, const char* argv[])
142 {
143   int r;
144   const char* cmd[4];
145   (void)argc;
146   cmd[0] = argv[0];
147   cmd[1] = "run";
148   cmd[2] = "4";
149   cmd[3] = 0;
150   fprintf(stdout, "Output on stdout before recursive test.\n");
151   fprintf(stderr, "Output on stderr before recursive test.\n");
152   fflush(stdout);
153   fflush(stderr);
154   r = runChild(cmd, kwsysProcess_State_Exception,
155 #ifdef CRASH_USING_ABORT
156                kwsysProcess_Exception_Other,
157 #else
158                kwsysProcess_Exception_Fault,
159 #endif
160                1, 1, 1, 0, 15, 0, 1, 0, 0, 0);
161   fprintf(stdout, "Output on stdout after recursive test.\n");
162   fprintf(stderr, "Output on stderr after recursive test.\n");
163   fflush(stdout);
164   fflush(stderr);
165   return r;
166 }
167 
168 #define TEST6_SIZE (4096 * 2)
test6(int argc,const char * argv[])169 static void test6(int argc, const char* argv[])
170 {
171   int i;
172   char runaway[TEST6_SIZE + 1];
173   (void)argc;
174   (void)argv;
175   for (i = 0; i < TEST6_SIZE; ++i) {
176     runaway[i] = '.';
177   }
178   runaway[TEST6_SIZE] = '\n';
179 
180   /* Generate huge amounts of output to test killing.  */
181   for (;;) {
182     fwrite(runaway, 1, TEST6_SIZE + 1, stdout);
183     fflush(stdout);
184   }
185 }
186 
187 /* Define MINPOLL to be one more than the number of times output is
188    written.  Define MAXPOLL to be the largest number of times a loop
189    delaying 1/10th of a second should ever have to poll.  */
190 #define MINPOLL 5
191 #define MAXPOLL 20
test7(int argc,const char * argv[])192 static int test7(int argc, const char* argv[])
193 {
194   (void)argc;
195   (void)argv;
196   fprintf(stdout, "Output on stdout before sleep.\n");
197   fprintf(stderr, "Output on stderr before sleep.\n");
198   fflush(stdout);
199   fflush(stderr);
200   /* Sleep for 1 second.  */
201   testProcess_sleep(1);
202   fprintf(stdout, "Output on stdout after sleep.\n");
203   fprintf(stderr, "Output on stderr after sleep.\n");
204   fflush(stdout);
205   fflush(stderr);
206   return 0;
207 }
208 
test8(int argc,const char * argv[])209 static int test8(int argc, const char* argv[])
210 {
211   /* Create a disowned grandchild to test handling of processes
212      that exit before their children.  */
213   int r;
214   const char* cmd[4];
215   (void)argc;
216   cmd[0] = argv[0];
217   cmd[1] = "run";
218   cmd[2] = "108";
219   cmd[3] = 0;
220   fprintf(stdout, "Output on stdout before grandchild test.\n");
221   fprintf(stderr, "Output on stderr before grandchild test.\n");
222   fflush(stdout);
223   fflush(stderr);
224   r = runChild(cmd, kwsysProcess_State_Disowned, kwsysProcess_Exception_None,
225                1, 1, 1, 0, 10, 0, 1, 1, 0, 0);
226   fprintf(stdout, "Output on stdout after grandchild test.\n");
227   fprintf(stderr, "Output on stderr after grandchild test.\n");
228   fflush(stdout);
229   fflush(stderr);
230   return r;
231 }
232 
test8_grandchild(int argc,const char * argv[])233 static int test8_grandchild(int argc, const char* argv[])
234 {
235   (void)argc;
236   (void)argv;
237   fprintf(stdout, "Output on stdout from grandchild before sleep.\n");
238   fprintf(stderr, "Output on stderr from grandchild before sleep.\n");
239   fflush(stdout);
240   fflush(stderr);
241   /* TODO: Instead of closing pipes here leave them open to make sure
242      the grandparent can stop listening when the parent exits.  This
243      part of the test cannot be enabled until the feature is
244      implemented.  */
245   fclose(stdout);
246   fclose(stderr);
247   testProcess_sleep(15);
248   return 0;
249 }
250 
test9(int argc,const char * argv[])251 static int test9(int argc, const char* argv[])
252 {
253   /* Test Ctrl+C behavior: the root test program will send a Ctrl+C to this
254      process.  Here, we start a child process that sleeps for a long time
255      while ignoring signals.  The test is successful if this process waits
256      for the child to return before exiting from the Ctrl+C handler.
257 
258      WARNING:  This test will falsely pass if the share parameter of runChild
259      was set to 0 when invoking the test9 process.  */
260   int r;
261   const char* cmd[4];
262   (void)argc;
263   cmd[0] = argv[0];
264   cmd[1] = "run";
265   cmd[2] = "109";
266   cmd[3] = 0;
267   fprintf(stdout, "Output on stdout before grandchild test.\n");
268   fprintf(stderr, "Output on stderr before grandchild test.\n");
269   fflush(stdout);
270   fflush(stderr);
271   r = runChild(cmd, kwsysProcess_State_Exited, kwsysProcess_Exception_None, 0,
272                1, 1, 0, 30, 0, 1, 0, 0, 0);
273   /* This sleep will avoid a race condition between this function exiting
274      normally and our Ctrl+C handler exiting abnormally after the process
275      exits.  */
276   testProcess_sleep(1);
277   fprintf(stdout, "Output on stdout after grandchild test.\n");
278   fprintf(stderr, "Output on stderr after grandchild test.\n");
279   fflush(stdout);
280   fflush(stderr);
281   return r;
282 }
283 
284 #if defined(_WIN32)
test9_grandchild_handler(DWORD dwCtrlType)285 static BOOL WINAPI test9_grandchild_handler(DWORD dwCtrlType)
286 {
287   /* Ignore all Ctrl+C/Break signals.  We must use an actual handler function
288      instead of using SetConsoleCtrlHandler(NULL, TRUE) so that we can also
289      ignore Ctrl+Break in addition to Ctrl+C.  */
290   (void)dwCtrlType;
291   return TRUE;
292 }
293 #endif
294 
test9_grandchild(int argc,const char * argv[])295 static int test9_grandchild(int argc, const char* argv[])
296 {
297   /* The grandchild just sleeps for a few seconds while ignoring signals.  */
298   (void)argc;
299   (void)argv;
300 #if defined(_WIN32)
301   if (!SetConsoleCtrlHandler(test9_grandchild_handler, TRUE)) {
302     return 1;
303   }
304 #else
305   struct sigaction sa;
306   memset(&sa, 0, sizeof(sa));
307   sa.sa_handler = SIG_IGN;
308   sigemptyset(&sa.sa_mask);
309   if (sigaction(SIGINT, &sa, 0) < 0) {
310     return 1;
311   }
312 #endif
313   fprintf(stdout, "Output on stdout from grandchild before sleep.\n");
314   fprintf(stderr, "Output on stderr from grandchild before sleep.\n");
315   fflush(stdout);
316   fflush(stderr);
317   /* Sleep for 9 seconds.  */
318   testProcess_sleep(9);
319   fprintf(stdout, "Output on stdout from grandchild after sleep.\n");
320   fprintf(stderr, "Output on stderr from grandchild after sleep.\n");
321   fflush(stdout);
322   fflush(stderr);
323   return 0;
324 }
325 
test10(int argc,const char * argv[])326 static int test10(int argc, const char* argv[])
327 {
328   /* Test Ctrl+C behavior: the root test program will send a Ctrl+C to this
329      process.  Here, we start a child process that sleeps for a long time and
330      processes signals normally.  However, this grandchild is created in a new
331      process group - ensuring that Ctrl+C we receive is sent to our process
332      groups.  We make sure it exits anyway.  */
333   int r;
334   const char* cmd[4];
335   (void)argc;
336   cmd[0] = argv[0];
337   cmd[1] = "run";
338   cmd[2] = "110";
339   cmd[3] = 0;
340   fprintf(stdout, "Output on stdout before grandchild test.\n");
341   fprintf(stderr, "Output on stderr before grandchild test.\n");
342   fflush(stdout);
343   fflush(stderr);
344   r =
345     runChild(cmd, kwsysProcess_State_Exception,
346              kwsysProcess_Exception_Interrupt, 0, 1, 1, 0, 30, 0, 1, 0, 1, 0);
347   fprintf(stdout, "Output on stdout after grandchild test.\n");
348   fprintf(stderr, "Output on stderr after grandchild test.\n");
349   fflush(stdout);
350   fflush(stderr);
351   return r;
352 }
353 
test10_grandchild(int argc,const char * argv[])354 static int test10_grandchild(int argc, const char* argv[])
355 {
356   /* The grandchild just sleeps for a few seconds and handles signals.  */
357   (void)argc;
358   (void)argv;
359   fprintf(stdout, "Output on stdout from grandchild before sleep.\n");
360   fprintf(stderr, "Output on stderr from grandchild before sleep.\n");
361   fflush(stdout);
362   fflush(stderr);
363   /* Sleep for 6 seconds.  */
364   testProcess_sleep(6);
365   fprintf(stdout, "Output on stdout from grandchild after sleep.\n");
366   fprintf(stderr, "Output on stderr from grandchild after sleep.\n");
367   fflush(stdout);
368   fflush(stderr);
369   return 0;
370 }
371 
runChild2(kwsysProcess * kp,const char * cmd[],int state,int exception,int value,int share,int output,int delay,double timeout,int poll,int disown,int createNewGroup,unsigned int interruptDelay)372 static int runChild2(kwsysProcess* kp, const char* cmd[], int state,
373                      int exception, int value, int share, int output,
374                      int delay, double timeout, int poll, int disown,
375                      int createNewGroup, unsigned int interruptDelay)
376 {
377   int result = 0;
378   char* data = 0;
379   int length = 0;
380   double userTimeout = 0;
381   double* pUserTimeout = 0;
382   kwsysProcess_SetCommand(kp, cmd);
383   if (timeout >= 0) {
384     kwsysProcess_SetTimeout(kp, timeout);
385   }
386   if (share) {
387     kwsysProcess_SetPipeShared(kp, kwsysProcess_Pipe_STDOUT, 1);
388     kwsysProcess_SetPipeShared(kp, kwsysProcess_Pipe_STDERR, 1);
389   }
390   if (disown) {
391     kwsysProcess_SetOption(kp, kwsysProcess_Option_Detach, 1);
392   }
393   if (createNewGroup) {
394     kwsysProcess_SetOption(kp, kwsysProcess_Option_CreateProcessGroup, 1);
395   }
396   kwsysProcess_Execute(kp);
397 
398   if (poll) {
399     pUserTimeout = &userTimeout;
400   }
401 
402   if (interruptDelay) {
403     testProcess_sleep(interruptDelay);
404     kwsysProcess_Interrupt(kp);
405   }
406 
407   if (!share && !disown) {
408     int p;
409     while ((p = kwsysProcess_WaitForData(kp, &data, &length, pUserTimeout))) {
410       if (output) {
411         if (poll && p == kwsysProcess_Pipe_Timeout) {
412           fprintf(stdout, "WaitForData timeout reached.\n");
413           fflush(stdout);
414 
415           /* Count the number of times we polled without getting data.
416              If it is excessive then kill the child and fail.  */
417           if (++poll >= MAXPOLL) {
418             fprintf(stdout, "Poll count reached limit %d.\n", MAXPOLL);
419             kwsysProcess_Kill(kp);
420           }
421         } else {
422           fwrite(data, 1, (size_t)length, stdout);
423           fflush(stdout);
424         }
425       }
426       if (poll) {
427         /* Delay to avoid busy loop during polling.  */
428         testProcess_usleep(100000);
429       }
430       if (delay) {
431 /* Purposely sleeping only on Win32 to let pipe fill up.  */
432 #if defined(_WIN32)
433         testProcess_usleep(100000);
434 #endif
435       }
436     }
437   }
438 
439   if (disown) {
440     kwsysProcess_Disown(kp);
441   } else {
442     kwsysProcess_WaitForExit(kp, 0);
443   }
444 
445   switch (kwsysProcess_GetState(kp)) {
446     case kwsysProcess_State_Starting:
447       printf("No process has been executed.\n");
448       break;
449     case kwsysProcess_State_Executing:
450       printf("The process is still executing.\n");
451       break;
452     case kwsysProcess_State_Expired:
453       printf("Subprocess was killed when timeout expired.\n");
454       break;
455     case kwsysProcess_State_Exited:
456       printf("Subprocess exited with value = %d\n",
457              kwsysProcess_GetExitValue(kp));
458       result = ((exception != kwsysProcess_GetExitException(kp)) ||
459                 (value != kwsysProcess_GetExitValue(kp)));
460       break;
461     case kwsysProcess_State_Killed:
462       printf("Subprocess was killed by parent.\n");
463       break;
464     case kwsysProcess_State_Exception:
465       printf("Subprocess terminated abnormally: %s\n",
466              kwsysProcess_GetExceptionString(kp));
467       result = ((exception != kwsysProcess_GetExitException(kp)) ||
468                 (value != kwsysProcess_GetExitValue(kp)));
469       break;
470     case kwsysProcess_State_Disowned:
471       printf("Subprocess was disowned.\n");
472       break;
473     case kwsysProcess_State_Error:
474       printf("Error in administrating child process: [%s]\n",
475              kwsysProcess_GetErrorString(kp));
476       break;
477   }
478 
479   if (result) {
480     if (exception != kwsysProcess_GetExitException(kp)) {
481       fprintf(stderr,
482               "Mismatch in exit exception.  "
483               "Should have been %d, was %d.\n",
484               exception, kwsysProcess_GetExitException(kp));
485     }
486     if (value != kwsysProcess_GetExitValue(kp)) {
487       fprintf(stderr,
488               "Mismatch in exit value.  "
489               "Should have been %d, was %d.\n",
490               value, kwsysProcess_GetExitValue(kp));
491     }
492   }
493 
494   if (kwsysProcess_GetState(kp) != state) {
495     fprintf(stderr,
496             "Mismatch in state.  "
497             "Should have been %d, was %d.\n",
498             state, kwsysProcess_GetState(kp));
499     result = 1;
500   }
501 
502   /* We should have polled more times than there were data if polling
503      was enabled.  */
504   if (poll && poll < MINPOLL) {
505     fprintf(stderr, "Poll count is %d, which is less than %d.\n", poll,
506             MINPOLL);
507     result = 1;
508   }
509 
510   return result;
511 }
512 
513 /**
514  * Runs a child process and blocks until it returns.  Arguments as follows:
515  *
516  * cmd            = Command line to run.
517  * state          = Expected return value of kwsysProcess_GetState after exit.
518  * exception      = Expected return value of kwsysProcess_GetExitException.
519  * value          = Expected return value of kwsysProcess_GetExitValue.
520  * share          = Whether to share stdout/stderr child pipes with our pipes
521  *                  by way of kwsysProcess_SetPipeShared.  If false, new pipes
522  *                  are created.
523  * output         = If !share && !disown, whether to write the child's stdout
524  *                  and stderr output to our stdout.
525  * delay          = If !share && !disown, adds an additional short delay to
526  *                  the pipe loop to allow the pipes to fill up; Windows only.
527  * timeout        = Non-zero to sets a timeout in seconds via
528  *                  kwsysProcess_SetTimeout.
529  * poll           = If !share && !disown, we count the number of 0.1 second
530  *                  intervals where the child pipes had no new data.  We fail
531  *                  if not in the bounds of MINPOLL/MAXPOLL.
532  * repeat         = Number of times to run the process.
533  * disown         = If set, the process is disowned.
534  * createNewGroup = If set, the process is created in a new process group.
535  * interruptDelay = If non-zero, number of seconds to delay before
536  *                  interrupting the process.  Note that this delay will occur
537  *                  BEFORE any reading/polling of pipes occurs and before any
538  *                  detachment occurs.
539  */
runChild(const char * cmd[],int state,int exception,int value,int share,int output,int delay,double timeout,int poll,int repeat,int disown,int createNewGroup,unsigned int interruptDelay)540 int runChild(const char* cmd[], int state, int exception, int value, int share,
541              int output, int delay, double timeout, int poll, int repeat,
542              int disown, int createNewGroup, unsigned int interruptDelay)
543 {
544   int result = 1;
545   kwsysProcess* kp = kwsysProcess_New();
546   if (!kp) {
547     fprintf(stderr, "kwsysProcess_New returned NULL!\n");
548     return 1;
549   }
550   while (repeat-- > 0) {
551     result = runChild2(kp, cmd, state, exception, value, share, output, delay,
552                        timeout, poll, disown, createNewGroup, interruptDelay);
553     if (result) {
554       break;
555     }
556   }
557   kwsysProcess_Delete(kp);
558   return result;
559 }
560 
main(int argc,const char * argv[])561 int main(int argc, const char* argv[])
562 {
563   int n = 0;
564 
565 #ifdef _WIN32
566   int i;
567   char new_args[10][_MAX_PATH];
568   LPWSTR* w_av = CommandLineToArgvW(GetCommandLineW(), &argc);
569   for (i = 0; i < argc; i++) {
570     kwsysEncoding_wcstombs(new_args[i], w_av[i], _MAX_PATH);
571     argv[i] = new_args[i];
572   }
573   LocalFree(w_av);
574 #endif
575 
576 #if 0
577     {
578     HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
579     DuplicateHandle(GetCurrentProcess(), out,
580                     GetCurrentProcess(), &out, 0, FALSE,
581                     DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
582     SetStdHandle(STD_OUTPUT_HANDLE, out);
583     }
584     {
585     HANDLE out = GetStdHandle(STD_ERROR_HANDLE);
586     DuplicateHandle(GetCurrentProcess(), out,
587                     GetCurrentProcess(), &out, 0, FALSE,
588                     DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
589     SetStdHandle(STD_ERROR_HANDLE, out);
590     }
591 #endif
592   if (argc == 2) {
593     n = atoi(argv[1]);
594   } else if (argc == 3 && strcmp(argv[1], "run") == 0) {
595     n = atoi(argv[2]);
596   }
597   /* Check arguments.  */
598   if (((n >= 1 && n <= 10) || n == 108 || n == 109 || n == 110) && argc == 3) {
599     /* This is the child process for a requested test number.  */
600     switch (n) {
601       case 1:
602         return test1(argc, argv);
603       case 2:
604         return test2(argc, argv);
605       case 3:
606         return test3(argc, argv);
607       case 4:
608         return test4(argc, argv);
609       case 5:
610         return test5(argc, argv);
611       case 6:
612         test6(argc, argv);
613         return 0;
614       case 7:
615         return test7(argc, argv);
616       case 8:
617         return test8(argc, argv);
618       case 9:
619         return test9(argc, argv);
620       case 10:
621         return test10(argc, argv);
622       case 108:
623         return test8_grandchild(argc, argv);
624       case 109:
625         return test9_grandchild(argc, argv);
626       case 110:
627         return test10_grandchild(argc, argv);
628     }
629     fprintf(stderr, "Invalid test number %d.\n", n);
630     return 1;
631   }
632   if (n >= 1 && n <= 10) {
633     /* This is the parent process for a requested test number.  */
634     int states[10] = {
635       kwsysProcess_State_Exited,   kwsysProcess_State_Exited,
636       kwsysProcess_State_Expired,  kwsysProcess_State_Exception,
637       kwsysProcess_State_Exited,   kwsysProcess_State_Expired,
638       kwsysProcess_State_Exited,   kwsysProcess_State_Exited,
639       kwsysProcess_State_Expired,  /* Ctrl+C handler test */
640       kwsysProcess_State_Exception /* Process group test */
641     };
642     int exceptions[10] = {
643       kwsysProcess_Exception_None,  kwsysProcess_Exception_None,
644       kwsysProcess_Exception_None,
645 #ifdef CRASH_USING_ABORT
646       kwsysProcess_Exception_Other,
647 #else
648       kwsysProcess_Exception_Fault,
649 #endif
650       kwsysProcess_Exception_None,  kwsysProcess_Exception_None,
651       kwsysProcess_Exception_None,  kwsysProcess_Exception_None,
652       kwsysProcess_Exception_None,  kwsysProcess_Exception_Interrupt
653     };
654     int values[10] = { 0, 123, 1, 1, 0, 0, 0, 0, 1, 1 };
655     int shares[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
656     int outputs[10] = { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1 };
657     int delays[10] = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 };
658     double timeouts[10] = { 10, 10, 10, 30, 30, 10, -1, 10, 6, 4 };
659     int polls[10] = { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 };
660     int repeat[10] = { 257, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
661     int createNewGroups[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
662     unsigned int interruptDelays[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 };
663     int r;
664     const char* cmd[4];
665 #ifdef _WIN32
666     char* argv0 = 0;
667 #endif
668     char* test1IterationsStr = getenv("KWSYS_TEST_PROCESS_1_COUNT");
669     if (test1IterationsStr) {
670       long int test1Iterations = strtol(test1IterationsStr, 0, 10);
671       if (test1Iterations > 10 && test1Iterations != LONG_MAX) {
672         repeat[0] = (int)test1Iterations;
673       }
674     }
675 #ifdef _WIN32
676     if (n == 0 && (argv0 = strdup(argv[0]))) {
677       /* Try converting to forward slashes to see if it works.  */
678       char* c;
679       for (c = argv0; *c; ++c) {
680         if (*c == '\\') {
681           *c = '/';
682         }
683       }
684       cmd[0] = argv0;
685     } else {
686       cmd[0] = argv[0];
687     }
688 #else
689     cmd[0] = argv[0];
690 #endif
691     cmd[1] = "run";
692     cmd[2] = argv[1];
693     cmd[3] = 0;
694     fprintf(stdout, "Output on stdout before test %d.\n", n);
695     fprintf(stderr, "Output on stderr before test %d.\n", n);
696     fflush(stdout);
697     fflush(stderr);
698     r = runChild(cmd, states[n - 1], exceptions[n - 1], values[n - 1],
699                  shares[n - 1], outputs[n - 1], delays[n - 1], timeouts[n - 1],
700                  polls[n - 1], repeat[n - 1], 0, createNewGroups[n - 1],
701                  interruptDelays[n - 1]);
702     fprintf(stdout, "Output on stdout after test %d.\n", n);
703     fprintf(stderr, "Output on stderr after test %d.\n", n);
704     fflush(stdout);
705     fflush(stderr);
706 #if defined(_WIN32)
707     free(argv0);
708 #endif
709     return r;
710   }
711   if (argc > 2 && strcmp(argv[1], "0") == 0) {
712     /* This is the special debugging test to run a given command
713        line.  */
714     const char** cmd = argv + 2;
715     int state = kwsysProcess_State_Exited;
716     int exception = kwsysProcess_Exception_None;
717     int value = 0;
718     double timeout = 0;
719     int r =
720       runChild(cmd, state, exception, value, 0, 1, 0, timeout, 0, 1, 0, 0, 0);
721     return r;
722   }
723   /* Improper usage.  */
724   fprintf(stdout, "Usage: %s <test number>\n", argv[0]);
725   return 1;
726 }
727