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