2 * Copyright 1993, 1995 Christopher Seiwald.
4 * This file is part of Jam - see jam.c for Copyright information.
8 * Copyright 2001-2004 David Abrahams.
9 * Copyright 2007 Rene Rivera.
10 * Distributed under the Boost Software License, Version 1.0.
11 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
28 #define WIN32_LEAN_AND_MEAN
34 * execnt.c - execute a shell command on Windows NT
36 * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp().
39 * /bin/sh -c % [ on UNIX/AmigaOS ]
40 * cmd.exe /c % [ on Windows NT ]
42 * Each word must be an individual element in a jam variable value.
44 * In $(JAMSHELL), % expands to the command string and ! expands to
45 * the slot number (starting at 1) for multiprocess (-j) invocations.
46 * If $(JAMSHELL) doesn't include a %, it is tacked on as the last
49 * Don't just set JAMSHELL to /bin/sh or cmd.exe - it won't work!
52 * exec_cmd() - launch an async command execution.
53 * exec_wait() - wait and drive at most one execution completion.
56 * onintr() - bump intr to note command interruption.
58 * 04/08/94 (seiwald) - Coherent/386 support added.
59 * 05/04/94 (seiwald) - async multiprocess interface
60 * 01/22/95 (seiwald) - $(JAMSHELL) support
61 * 06/02/97 (gsar) - full async multiprocess support for Win32
64 /* get the maximum command line length according to the OS */
67 /* delete and argv list */
68 static void free_argv(char**);
69 /* Convert a command string into arguments for spawnvp. */
70 static char** string_to_args(const char*);
71 /* bump intr to note command interruption */
72 static void onintr(int);
73 /* If the command is suitable for execution via spawnvp */
74 long can_spawn(char*);
75 /* Add two 64-bit unsigned numbers, h1l1 and h2l2 */
76 static FILETIME add_64(
77 unsigned long h1, unsigned long l1,
78 unsigned long h2, unsigned long l2);
79 static FILETIME add_FILETIME(FILETIME t1, FILETIME t2);
80 static FILETIME negate_FILETIME(FILETIME t);
81 /* Convert a FILETIME to a number of seconds */
82 static double filetime_seconds(FILETIME t);
83 /* record the timing info for the process */
84 static void record_times(HANDLE, timing_info*);
85 /* calc the current running time of an *active* process */
86 static double running_time(HANDLE);
88 DWORD get_process_id(HANDLE);
89 /* terminate the given process, after terminating all its children */
90 static void kill_process_tree(DWORD, HANDLE);
91 /* waits for a command to complete or for the given timeout, whichever is first */
92 static int try_wait(int timeoutMillis);
93 /* reads any pending output for running commands */
94 static void read_output();
95 /* checks if a command ran out of time, and kills it */
96 static int try_kill_one();
98 static double creation_time(HANDLE);
99 /* Recursive check if first process is parent (directly or indirectly) of
101 static int is_parent_child(DWORD, DWORD);
103 static void close_alert(HANDLE);
104 /* close any alerts hanging around */
105 static void close_alerts();
107 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
110 static int cmdsrunning = 0;
111 static void (* istat)( int );
114 /* The list of commands we run. */
117 string action; /* buffer to hold action */
118 string target; /* buffer to hold target */
119 string command; /* buffer to hold command being invoked */
121 /* Temporary batch file used to execute the action when needed. */
124 /* Pipes for communicating with the child process. Parent reads from (0),
125 * child writes to (1).
127 HANDLE pipe_out[ 2 ];
128 HANDLE pipe_err[ 2 ];
130 string buffer_out; /* buffer to hold stdout, if any */
131 string buffer_err; /* buffer to hold stderr, if any */
133 PROCESS_INFORMATION pi; /* running process information */
134 DWORD exit_code; /* executed command's exit code */
135 int exit_reason; /* reason why a command completed */
137 /* Function called when the command completes. */
138 void (* func)( void * closure, int status, timing_info *, char *, char * );
140 /* Opaque data passed back to the 'func' callback called when the command
145 cmdtab[ MAXJOBS ] = { { 0 } };
149 * Execution unit tests.
152 void execnt_unit_test()
154 #if !defined( NDEBUG )
155 /* vc6 preprocessor is broken, so assert with these strings gets confused.
156 * Use a table instead.
158 typedef struct test { char * command; int result; } test;
164 { "echo x > foo.bar", 1 },
165 { "echo x < foo.bar", 1 },
166 { "echo x \">\" foo.bar", 0 },
167 { "echo x \"<\" foo.bar", 0 },
168 { "echo x \\\">\\\" foo.bar", 1 },
169 { "echo x \\\"<\\\" foo.bar", 1 } };
171 for ( i = 0; i < sizeof( tests ) / sizeof( *tests ); ++i )
172 assert( !can_spawn( tests[ i ].command ) == tests[ i ].result );
175 char * long_command = BJAM_MALLOC_ATOMIC( MAXLINE + 10 );
176 assert( long_command != 0 );
177 memset( long_command, 'x', MAXLINE + 9 );
178 long_command[ MAXLINE + 9 ] = 0;
179 assert( can_spawn( long_command ) == MAXLINE + 9 );
180 BJAM_FREE( long_command );
184 /* Work around vc6 bug; it doesn't like escaped string
185 * literals inside assert
187 char * * argv = string_to_args(" \"g++\" -c -I\"Foobar\"" );
188 char const expected[] = "-c -I\"Foobar\"";
190 assert( !strcmp( argv[ 0 ], "g++" ) );
191 assert( !strcmp( argv[ 1 ], expected ) );
199 * exec_cmd() - launch an async command execution.
205 void (* func)( void * closure, int status, timing_info *, char * invoked_command, char * command_output ),
214 char * argv_static[ MAXARGC + 1 ]; /* +1 for NULL */
215 char * * argv = argv_static;
217 char * command_orig = command;
219 /* Check to see if we need to hack around the line-length limitation. Look
220 * for a JAMSHELL setting of "%", indicating that the command should be
223 if ( shell && !strcmp( shell->string, "%" ) && !list_next( shell ) )
229 /* Find a slot in the running commands table for this one. */
230 for ( slot = 0; slot < MAXJOBS; ++slot )
231 if ( !cmdtab[ slot ].pi.hProcess )
233 if ( slot == MAXJOBS )
235 printf( "no slots for child!\n" );
239 /* Compute the name of a temp batch file, for possible use. */
240 if ( !cmdtab[ slot ].tempfile_bat )
242 char const * tempdir = path_tmpdir();
243 DWORD procID = GetCurrentProcessId();
245 /* SVA - allocate 64 bytes extra just to be safe. */
246 cmdtab[ slot ].tempfile_bat = BJAM_MALLOC_ATOMIC( strlen( tempdir ) + 64 );
248 sprintf( cmdtab[ slot ].tempfile_bat, "%s\\jam%d-%02d.bat",
249 tempdir, procID, slot );
252 /* Trim leading, -ending- white space */
253 while ( *( command + 1 ) && isspace( *command ) )
256 /* Write to .BAT file unless the line would be too long and it meets the
257 * other spawnability criteria.
259 if ( raw_cmd && ( can_spawn( command ) >= MAXLINE ) )
262 printf("Executing raw command directly\n");
270 /* Write command to bat file. For some reason this open can fail
271 * intermitently. But doing some retries works. Most likely this is due
272 * to a previously existing file of the same name that happens to be
273 * opened by an active virus scanner. Pointed out and fixed by Bronek
276 for ( ; !f && ( tries < 4 ); ++tries )
278 f = fopen( cmdtab[ slot ].tempfile_bat, "w" );
279 if ( !f && ( tries < 4 ) ) Sleep( 250 );
283 printf( "failed to write command file!\n" );
289 command = cmdtab[ slot ].tempfile_bat;
294 printf( "using user-specified shell: %s", shell->string );
296 printf( "Executing through .bat file\n" );
300 /* Formulate argv; If shell was defined, be prepared for % and ! subs.
301 * Otherwise, use stock cmd.exe.
309 sprintf( jobno, "%d", slot + 1 );
311 for ( i = 0; shell && ( i < MAXARGC ); ++i, shell = list_next( shell ) )
313 switch ( shell->string[ 0 ] )
315 case '%': argv[ i ] = command; ++gotpercent; break;
316 case '!': argv[ i ] = jobno; break;
317 default : argv[ i ] = shell->string;
320 printf( "argv[%d] = '%s'\n", i, argv[ i ] );
324 argv[ i++ ] = command;
330 argv = string_to_args( command );
334 argv[ 0 ] = "cmd.exe";
335 argv[ 1 ] = "/Q/C"; /* anything more is non-portable */
340 /* Catch interrupts whenever commands are running. */
341 if ( !cmdsrunning++ )
342 istat = signal( SIGINT, onintr );
344 /* Start the command. */
346 SECURITY_ATTRIBUTES sa
347 = { sizeof( SECURITY_ATTRIBUTES ), 0, 0 };
348 SECURITY_DESCRIPTOR sd;
350 = { sizeof( STARTUPINFO ), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
353 /* Init the security data. */
354 InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION );
355 SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE );
356 sa.lpSecurityDescriptor = &sd;
357 sa.bInheritHandle = TRUE;
359 /* Create the stdout, which is also the merged out + err, pipe. */
360 if ( !CreatePipe( &cmdtab[ slot ].pipe_out[ 0 ],
361 &cmdtab[ slot ].pipe_out[ 1 ], &sa, 0 ) )
363 perror( "CreatePipe" );
367 /* Create the stdout, which is also the merged out+err, pipe. */
368 if ( globs.pipe_action == 2 )
370 if ( !CreatePipe( &cmdtab[ slot ].pipe_err[ 0 ],
371 &cmdtab[ slot ].pipe_err[ 1 ], &sa, 0 ) )
373 perror( "CreatePipe" );
378 /* Set handle inheritance off for the pipe ends the parent reads from. */
379 SetHandleInformation( cmdtab[ slot ].pipe_out[ 0 ], HANDLE_FLAG_INHERIT, 0 );
380 if ( globs.pipe_action == 2 )
381 SetHandleInformation( cmdtab[ slot ].pipe_err[ 0 ], HANDLE_FLAG_INHERIT, 0 );
383 /* Hide the child window, if any. */
384 si.dwFlags |= STARTF_USESHOWWINDOW;
385 si.wShowWindow = SW_HIDE;
387 /* Set the child outputs to the pipes. */
388 si.dwFlags |= STARTF_USESTDHANDLES;
389 si.hStdOutput = cmdtab[ slot ].pipe_out[ 1 ];
390 if ( globs.pipe_action == 2 )
392 /* Pipe stderr to the action error output. */
393 si.hStdError = cmdtab[ slot ].pipe_err[ 1 ];
395 else if ( globs.pipe_action == 1 )
397 /* Pipe stderr to the console error output. */
398 si.hStdError = GetStdHandle( STD_ERROR_HANDLE );
402 /* Pipe stderr to the action merged output. */
403 si.hStdError = cmdtab[ slot ].pipe_out[ 1 ];
406 /* Let the child inherit stdin, as some commands assume it's available. */
407 si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
409 /* Save the operation for exec_wait() to find. */
410 cmdtab[ slot ].func = func;
411 cmdtab[ slot ].closure = closure;
412 if ( action && target )
414 string_copy( &cmdtab[ slot ].action, action );
415 string_copy( &cmdtab[ slot ].target, target );
419 string_free( &cmdtab[ slot ].action );
420 string_new ( &cmdtab[ slot ].action );
421 string_free( &cmdtab[ slot ].target );
422 string_new ( &cmdtab[ slot ].target );
424 string_copy( &cmdtab[ slot ].command, command_orig );
426 /* Put together the command we run. */
428 char * * argp = argv;
430 string_copy( &cmd, *(argp++) );
433 string_push_back( &cmd, ' ' );
434 string_append( &cmd, *(argp++) );
438 /* Create output buffers. */
439 string_new( &cmdtab[ slot ].buffer_out );
440 string_new( &cmdtab[ slot ].buffer_err );
442 /* Run the command by creating a sub-process for it. */
445 NULL , /* application name */
446 cmd.value , /* command line */
447 NULL , /* process attributes */
448 NULL , /* thread attributes */
449 TRUE , /* inherit handles */
450 CREATE_NEW_PROCESS_GROUP, /* create flags */
451 NULL , /* env vars, null inherits env */
452 NULL , /* current dir, null is our */
454 &si , /* startup info */
455 &cmdtab[ slot ].pi /* child process info, if created */
459 perror( "CreateProcess" );
463 /* Clean up temporary stuff. */
467 /* Wait until we are under the limit of concurrent commands. Do not trust
470 while ( ( cmdsrunning >= MAXJOBS ) || ( cmdsrunning >= globs.jobs ) )
474 if ( argv != argv_static )
481 * * wait and drive at most one execution completion.
482 * * waits for one command to complete, while processing the i/o for all
485 * Returns 0 if called when there were no more commands being executed or 1
493 /* Handle naive make1() which does not know if cmds are running. */
497 /* Wait for a command to complete, while snarfing up any output. */
500 /* Check for a complete command, briefly. */
502 /* Read in the output of all running commands. */
504 /* Close out pending debug style dialogs. */
506 /* Check if a command ran out of time. */
507 if ( i < 0 ) i = try_kill_one();
511 /* We have a command... process it. */
517 /* The time data for the command. */
518 record_times( cmdtab[ i ].pi.hProcess, &time );
520 /* Clear the temp file. */
521 if ( cmdtab[ i ].tempfile_bat )
523 unlink( cmdtab[ i ].tempfile_bat );
524 BJAM_FREE( cmdtab[ i ].tempfile_bat );
525 cmdtab[ i ].tempfile_bat = NULL;
528 /* Find out the process exit code. */
529 GetExitCodeProcess( cmdtab[ i ].pi.hProcess, &cmdtab[ i ].exit_code );
531 /* The dispossition of the command. */
533 rstat = EXEC_CMD_INTR;
534 else if ( cmdtab[ i ].exit_code != 0 )
535 rstat = EXEC_CMD_FAIL;
539 /* Output the action block. */
541 cmdtab[ i ].action.size > 0 ? cmdtab[ i ].action.value : 0,
542 cmdtab[ i ].target.size > 0 ? cmdtab[ i ].target.value : 0,
543 cmdtab[ i ].command.size > 0 ? cmdtab[ i ].command.value : 0,
544 cmdtab[ i ].buffer_out.size > 0 ? cmdtab[ i ].buffer_out.value : 0,
545 cmdtab[ i ].buffer_err.size > 0 ? cmdtab[ i ].buffer_err.value : 0,
546 cmdtab[ i ].exit_reason );
548 /* Call the callback, may call back to jam rule land. Assume -p0 in
549 * effect so only pass buffer containing merged output.
555 cmdtab[ i ].command.value,
556 cmdtab[ i ].buffer_out.value );
558 /* Clean up the command data, process, etc. */
559 string_free( &cmdtab[ i ].action ); string_new( &cmdtab[ i ].action );
560 string_free( &cmdtab[ i ].target ); string_new( &cmdtab[ i ].target );
561 string_free( &cmdtab[ i ].command ); string_new( &cmdtab[ i ].command );
562 if ( cmdtab[ i ].pi.hProcess ) { CloseHandle( cmdtab[ i ].pi.hProcess ); cmdtab[ i ].pi.hProcess = 0; }
563 if ( cmdtab[ i ].pi.hThread ) { CloseHandle( cmdtab[ i ].pi.hThread ); cmdtab[ i ].pi.hThread = 0; }
564 if ( cmdtab[ i ].pipe_out[ 0 ] ) { CloseHandle( cmdtab[ i ].pipe_out[ 0 ] ); cmdtab[ i ].pipe_out[ 0 ] = 0; }
565 if ( cmdtab[ i ].pipe_out[ 1 ] ) { CloseHandle( cmdtab[ i ].pipe_out[ 1 ] ); cmdtab[ i ].pipe_out[ 1 ] = 0; }
566 if ( cmdtab[ i ].pipe_err[ 0 ] ) { CloseHandle( cmdtab[ i ].pipe_err[ 0 ] ); cmdtab[ i ].pipe_err[ 0 ] = 0; }
567 if ( cmdtab[ i ].pipe_err[ 1 ] ) { CloseHandle( cmdtab[ i ].pipe_err[ 1 ] ); cmdtab[ i ].pipe_err[ 1 ] = 0; }
568 string_free( &cmdtab[ i ].buffer_out ); string_new( &cmdtab[ i ].buffer_out );
569 string_free( &cmdtab[ i ].buffer_err ); string_new( &cmdtab[ i ].buffer_err );
570 cmdtab[ i ].exit_code = 0;
571 cmdtab[ i ].exit_reason = EXIT_OK;
578 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
580 static void free_argv( char * * args )
582 BJAM_FREE( args[ 0 ] );
588 * For more details on Windows cmd.exe shell command-line length limitations see
589 * the following MSDN article:
590 * http://support.microsoft.com/default.aspx?scid=kb;en-us;830473
595 OSVERSIONINFO os_info;
596 os_info.dwOSVersionInfoSize = sizeof( os_info );
597 GetVersionEx( &os_info );
599 if ( os_info.dwMajorVersion >= 5 ) return 8191; /* XP > */
600 if ( os_info.dwMajorVersion == 4 ) return 2047; /* NT 4.x */
601 return 996; /* NT 3.5.1 */
606 * Convert a command string into arguments for spawnvp(). The original code,
607 * inherited from ftjam, tried to break up every argument on the command-line,
608 * dealing with quotes, but that is really a waste of time on Win32, at least.
609 * It turns out that all you need to do is get the raw path to the executable in
610 * the first argument to spawnvp(), and you can pass all the rest of the
611 * command-line arguments to spawnvp() in one, un-processed string.
613 * New strategy: break the string in at most one place.
616 static char * * string_to_args( char const * string )
625 /* Drop leading and trailing whitespace if any. */
626 while ( isspace( *string ) )
629 src_len = strlen( string );
630 while ( ( src_len > 0 ) && isspace( string[ src_len - 1 ] ) )
633 /* Copy the input string into a buffer we can modify. */
634 line = (char *)BJAM_MALLOC_ATOMIC( src_len + 1 );
638 /* Allocate the argv array.
639 * element 0: stores the path to the executable
640 * element 1: stores the command-line arguments to the executable
641 * element 2: NULL terminator
643 argv = (char * *)BJAM_MALLOC( 3 * sizeof( char * ) );
650 /* Strip quotes from the first command-line argument and find where it ends.
651 * Quotes are illegal in Win32 pathnames, so we do not need to worry about
652 * preserving escaped quotes here. Spaces can not be escaped in Win32, only
653 * enclosed in quotes, so removing backslash escapes is also a non-issue.
656 for ( src = string, dst = line ; *src; ++src )
659 in_quote = !in_quote;
660 else if ( !in_quote && isspace( *src ) )
668 /* Skip whitespace in src. */
669 while ( isspace( *src ) )
674 /* Copy the rest of the arguments verbatim. */
675 src_len -= src - string;
677 /* Use strncat() because it appends a trailing nul. */
679 strncat( dst, src, src_len );
687 static void onintr( int disp )
690 printf( "...interrupted\n" );
695 * can_spawn() - If the command is suitable for execution via spawnvp(), return
696 * a number >= the number of characters it would occupy on the command-line.
697 * Otherwise, return zero.
700 long can_spawn( char * command )
705 /* Move to the first non-whitespace. */
706 command += strspn( command, " \t" );
710 /* Look for newlines and unquoted i/o redirection. */
713 p += strcspn( p, "'\n\"<>|" );
718 /* Skip over any following spaces. */
719 while ( isspace( *p ) )
721 /* Must use a .bat file if there is anything significant following
730 if ( ( p > command ) && ( p[ -1 ] != '\\' ) )
734 else if ( inquote == 0 )
751 /* Return the number of characters the command will occupy. */
756 /* 64-bit arithmetic helpers. */
758 /* Compute the carry bit from the addition of two 32-bit unsigned numbers. */
759 #define add_carry_bit( a, b ) ( (((a) | (b)) >> 31) & (~((a) + (b)) >> 31) & 0x1 )
761 /* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1 and h2l2. */
762 #define add_64_hi( h1, l1, h2, l2 ) ((h1) + (h2) + add_carry_bit(l1, l2))
766 * Add two 64-bit unsigned numbers, h1l1 and h2l2.
769 static FILETIME add_64
771 unsigned long h1, unsigned long l1,
772 unsigned long h2, unsigned long l2
776 result.dwLowDateTime = l1 + l2;
777 result.dwHighDateTime = add_64_hi( h1, l1, h2, l2 );
782 static FILETIME add_FILETIME( FILETIME t1, FILETIME t2 )
784 return add_64( t1.dwHighDateTime, t1.dwLowDateTime, t2.dwHighDateTime,
789 static FILETIME negate_FILETIME( FILETIME t )
791 /* 2s complement negation */
792 return add_64( ~t.dwHighDateTime, ~t.dwLowDateTime, 0, 1 );
797 * Convert a FILETIME to a number of seconds.
800 static double filetime_seconds( FILETIME t )
802 return t.dwHighDateTime * ( (double)( 1UL << 31 ) * 2.0 * 1.0e-7 ) + t.dwLowDateTime * 1.0e-7;
807 * What should be a simple conversion, turns out to be horribly complicated by
808 * the defficiencies of MSVC and the Win32 API.
811 static time_t filetime_dt( FILETIME t_utc )
813 static int calc_time_diff = 1;
814 static double time_diff;
815 if ( calc_time_diff )
821 GetSystemTime( &s0_ );
822 t0_.tm_year = s0_.wYear-1900;
823 t0_.tm_mon = s0_.wMonth-1;
824 t0_.tm_wday = s0_.wDayOfWeek;
825 t0_.tm_mday = s0_.wDay;
826 t0_.tm_hour = s0_.wHour;
827 t0_.tm_min = s0_.wMinute;
828 t0_.tm_sec = s0_.wSecond;
830 SystemTimeToFileTime( &s0_, &f0_local );
831 LocalFileTimeToFileTime( &f0_local, &f0_ );
832 time_diff = filetime_seconds( f0_ ) - (double)mktime( &t0_ );
835 return ceil( filetime_seconds( t_utc ) - time_diff );
839 static void record_times( HANDLE process, timing_info * time )
845 if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) )
847 time->system = filetime_seconds( kernel );
848 time->user = filetime_seconds( user );
849 time->start = filetime_dt ( creation );
850 time->end = filetime_dt ( exit );
855 #define IO_BUFFER_SIZE ( 16 * 1024 )
857 static char ioBuffer[ IO_BUFFER_SIZE + 1 ];
860 static void read_pipe
862 HANDLE in, /* the pipe to read from */
866 DWORD bytesInBuffer = 0;
867 DWORD bytesAvailable = 0;
871 /* check if we have any data to read */
872 if ( !PeekNamedPipe( in, ioBuffer, IO_BUFFER_SIZE, &bytesInBuffer, &bytesAvailable, NULL ) )
875 /* read in the available data */
876 if ( bytesAvailable > 0 )
878 /* we only read in the available bytes, to avoid blocking */
879 if ( ReadFile( in, ioBuffer,
880 bytesAvailable <= IO_BUFFER_SIZE ? bytesAvailable : IO_BUFFER_SIZE,
881 &bytesInBuffer, NULL ) )
883 if ( bytesInBuffer > 0 )
885 /* Clean up some illegal chars. */
887 for ( i = 0; i < bytesInBuffer; ++i )
889 if ( ( (unsigned char)ioBuffer[ i ] < 1 ) )
892 /* Null, terminate. */
893 ioBuffer[ bytesInBuffer ] = '\0';
894 /* Append to the output. */
895 string_append( out, ioBuffer );
896 /* Subtract what we read in. */
897 bytesAvailable -= bytesInBuffer;
901 /* Likely read a error, bail out. */
907 /* Definitely read a error, bail out. */
912 while ( bytesAvailable > 0 );
916 static void read_output()
919 for ( i = 0; i < globs.jobs && i < MAXJOBS; ++i )
921 /* Read stdout data. */
922 if ( cmdtab[ i ].pipe_out[ 0 ] )
923 read_pipe( cmdtab[ i ].pipe_out[ 0 ], & cmdtab[ i ].buffer_out );
924 /* Read stderr data. */
925 if ( cmdtab[ i ].pipe_err[ 0 ] )
926 read_pipe( cmdtab[ i ].pipe_err[ 0 ], & cmdtab[ i ].buffer_err );
932 * Waits for a single child process command to complete, or the timeout,
933 * whichever comes first. Returns the index of the completed command in the
934 * cmdtab array, or -1.
937 static int try_wait( int timeoutMillis )
942 HANDLE active_handles[ MAXJOBS ];
943 int active_procs[ MAXJOBS ];
945 /* Prepare a list of all active processes to wait for. */
946 for ( num_active = 0, i = 0; i < globs.jobs; ++i )
948 if ( cmdtab[ i ].pi.hProcess )
950 active_handles[ num_active ] = cmdtab[ i ].pi.hProcess;
951 active_procs[ num_active ] = i;
956 /* Wait for a child to complete, or for our timeout window to expire. */
957 wait_api_result = WaitForMultipleObjects( num_active, active_handles,
958 FALSE, timeoutMillis );
959 if ( ( WAIT_OBJECT_0 <= wait_api_result ) &&
960 ( wait_api_result < WAIT_OBJECT_0 + num_active ) )
962 /* Rerminated process detected - return its index. */
963 return active_procs[ wait_api_result - WAIT_OBJECT_0 ];
971 static int try_kill_one()
973 /* Only need to check if a timeout was specified with the -l option. */
974 if ( globs.timeout > 0 )
977 for ( i = 0; i < globs.jobs; ++i )
979 double t = running_time( cmdtab[ i ].pi.hProcess );
980 if ( t > (double)globs.timeout )
982 /* The job may have left an alert dialog around, try and get rid
983 * of it before killing
985 close_alert( cmdtab[ i ].pi.hProcess );
986 /* We have a "runaway" job, kill it. */
987 kill_process_tree( 0, cmdtab[ i ].pi.hProcess );
988 /* And return it marked as a timeout. */
989 cmdtab[ i ].exit_reason = EXIT_TIMEOUT;
998 static void close_alerts()
1000 /* We only attempt this every 5 seconds, or so, because it is not a cheap
1001 * operation, and we will catch the alerts eventually. This check uses
1002 * floats as some compilers define CLOCKS_PER_SEC as a float or double.
1004 if ( ( (float)clock() / (float)( CLOCKS_PER_SEC * 5 ) ) < ( 1.0 / 5.0 ) )
1007 for ( i = 0; i < globs.jobs; ++i )
1008 close_alert( cmdtab[ i ].pi.hProcess );
1014 * Calc the current running time of an *active* process.
1017 static double running_time( HANDLE process )
1024 if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) )
1026 /* Compute the elapsed time. */
1027 GetSystemTimeAsFileTime( ¤t );
1028 return filetime_seconds( add_FILETIME( current,
1029 negate_FILETIME( creation ) ) );
1035 /* It is just stupidly silly that one has to do this. */
1036 typedef struct PROCESS_BASIC_INFORMATION__
1039 PVOID PebBaseAddress;
1042 ULONG UniqueProcessId;
1043 ULONG InheritedFromUniqueProcessId;
1044 } PROCESS_BASIC_INFORMATION_;
1045 typedef LONG (__stdcall * NtQueryInformationProcess__)(
1046 HANDLE ProcessHandle,
1047 LONG ProcessInformationClass,
1048 PVOID ProcessInformation,
1049 ULONG ProcessInformationLength,
1050 PULONG ReturnLength);
1051 static NtQueryInformationProcess__ NtQueryInformationProcess_ = NULL;
1052 static HMODULE NTDLL_ = NULL;
1053 DWORD get_process_id( HANDLE process )
1055 PROCESS_BASIC_INFORMATION_ pinfo;
1056 if ( !NtQueryInformationProcess_ )
1059 NTDLL_ = GetModuleHandleA( "ntdll" );
1061 NtQueryInformationProcess_
1062 = (NtQueryInformationProcess__)GetProcAddress( NTDLL_, "NtQueryInformationProcess" );
1064 if ( NtQueryInformationProcess_ )
1066 LONG r = (*NtQueryInformationProcess_)( process,
1067 /* ProcessBasicInformation == */ 0, &pinfo,
1068 sizeof( PROCESS_BASIC_INFORMATION_ ), NULL );
1069 return pinfo.UniqueProcessId;
1076 * Not really optimal, or efficient, but it is easier this way, and it is not
1077 * like we are going to be killing thousands, or even tens of processes.
1080 static void kill_process_tree( DWORD pid, HANDLE process )
1082 HANDLE process_snapshot_h = INVALID_HANDLE_VALUE;
1084 pid = get_process_id( process );
1085 process_snapshot_h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
1087 if ( INVALID_HANDLE_VALUE != process_snapshot_h )
1090 PROCESSENTRY32 pinfo;
1091 pinfo.dwSize = sizeof( PROCESSENTRY32 );
1093 ok = Process32First( process_snapshot_h, &pinfo );
1095 ok = Process32Next( process_snapshot_h, &pinfo ) )
1097 if ( pinfo.th32ParentProcessID == pid )
1099 /* Found a child, recurse to kill it and anything else below it.
1101 HANDLE ph = OpenProcess( PROCESS_ALL_ACCESS, FALSE,
1102 pinfo.th32ProcessID );
1105 kill_process_tree( pinfo.th32ProcessID, ph );
1110 CloseHandle( process_snapshot_h );
1112 /* Now that the children are all dead, kill the root. */
1113 TerminateProcess( process, -2 );
1117 static double creation_time( HANDLE process )
1124 return GetProcessTimes( process, &creation, &exit, &kernel, &user )
1125 ? filetime_seconds( creation )
1131 * Recursive check if first process is parent (directly or indirectly) of the
1132 * second one. Both processes are passed as process ids, not handles. Special
1133 * return value 2 means that the second process is smss.exe and its parent
1134 * process is System (first argument is ignored).
1137 static int is_parent_child( DWORD parent, DWORD child )
1139 HANDLE process_snapshot_h = INVALID_HANDLE_VALUE;
1143 if ( parent == child )
1146 process_snapshot_h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
1147 if ( INVALID_HANDLE_VALUE != process_snapshot_h )
1150 PROCESSENTRY32 pinfo;
1151 pinfo.dwSize = sizeof( PROCESSENTRY32 );
1153 ok = Process32First( process_snapshot_h, &pinfo );
1155 ok = Process32Next( process_snapshot_h, &pinfo ) )
1157 if ( pinfo.th32ProcessID == child )
1159 /* Unfortunately, process ids are not really unique. There might
1160 * be spurious "parent and child" relationship match between two
1161 * non-related processes if real parent process of a given
1162 * process has exited (while child process kept running as an
1163 * "orphan") and the process id of such parent process has been
1164 * reused by internals of the operating system when creating
1167 * Thus additional check is needed - process creation time. This
1168 * check may fail (i.e. return 0) for system processes due to
1169 * insufficient privileges, and that is OK.
1171 double tchild = 0.0;
1172 double tparent = 0.0;
1173 HANDLE hchild = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pinfo.th32ProcessID );
1174 CloseHandle( process_snapshot_h );
1176 /* csrss.exe may display message box like following:
1177 * xyz.exe - Unable To Locate Component
1178 * This application has failed to start because
1179 * boost_foo-bar.dll was not found. Re-installing the
1180 * application may fix the problem
1181 * This actually happens when starting test process that depends
1182 * on a dynamic library which failed to build. We want to
1183 * automatically close these message boxes even though csrss.exe
1184 * is not our child process. We may depend on the fact that (in
1185 * all current versions of Windows) csrss.exe is directly child
1186 * of the smss.exe process, which in turn is directly child of
1187 * the System process, which always has process id == 4. This
1188 * check must be performed before comparison of process creation
1191 if ( !stricmp( pinfo.szExeFile, "csrss.exe" ) &&
1192 ( is_parent_child( parent, pinfo.th32ParentProcessID ) == 2 ) )
1194 if ( !stricmp( pinfo.szExeFile, "smss.exe" ) &&
1195 ( pinfo.th32ParentProcessID == 4 ) )
1200 HANDLE hparent = OpenProcess( PROCESS_QUERY_INFORMATION,
1201 FALSE, pinfo.th32ParentProcessID );
1204 tchild = creation_time( hchild );
1205 tparent = creation_time( hparent );
1206 CloseHandle( hparent );
1208 CloseHandle( hchild );
1211 /* Return 0 if one of the following is true:
1212 * 1. we failed to read process creation time
1213 * 2. child was created before alleged parent
1215 if ( ( tchild == 0.0 ) || ( tparent == 0.0 ) ||
1216 ( tchild < tparent ) )
1219 return is_parent_child( parent, pinfo.th32ParentProcessID ) & 1;
1223 CloseHandle( process_snapshot_h );
1229 typedef struct PROCESS_HANDLE_ID { HANDLE h; DWORD pid; } PROCESS_HANDLE_ID;
1233 * This function is called by the operating system for each topmost window.
1236 BOOL CALLBACK close_alert_window_enum( HWND hwnd, LPARAM lParam )
1238 char buf[ 7 ] = { 0 };
1239 PROCESS_HANDLE_ID p = *( (PROCESS_HANDLE_ID *)lParam );
1243 /* We want to find and close any window that:
1245 * 2. is a dialog and
1246 * 3. is displayed by any of our child processes
1248 if ( !IsWindowVisible( hwnd ) )
1251 if ( !GetClassNameA( hwnd, buf, sizeof( buf ) ) )
1252 return TRUE; /* Failed to read class name; presume it is not a dialog. */
1254 if ( strcmp( buf, "#32770" ) )
1255 return TRUE; /* Not a dialog */
1257 /* GetWindowThreadProcessId() returns 0 on error, otherwise thread id of
1258 * window message pump thread.
1260 tid = GetWindowThreadProcessId( hwnd, &pid );
1262 if ( tid && is_parent_child( p.pid, pid ) )
1264 /* Ask really nice. */
1265 PostMessageA( hwnd, WM_CLOSE, 0, 0 );
1266 /* Now wait and see if it worked. If not, insist. */
1267 if ( WaitForSingleObject( p.h, 200 ) == WAIT_TIMEOUT )
1269 PostThreadMessageA( tid, WM_QUIT, 0, 0 );
1270 WaitForSingleObject( p.h, 300 );
1273 /* Done, we do not want to check any other window now. */
1281 static void close_alert( HANDLE process )
1283 DWORD pid = get_process_id( process );
1284 /* If process already exited or we just can not get its process id, do not
1289 PROCESS_HANDLE_ID p;
1292 EnumWindows( &close_alert_window_enum, (LPARAM)&p );
1296 #endif /* USE_EXECNT */