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 copy at
12 * http://www.boost.org/LICENSE_1_0.txt)
16 * execnt.c - execute a shell command on Windows NT
18 * If $(JAMSHELL) is defined, uses that to formulate the actual command. The
19 * default is: cmd.exe /Q/C
21 * In $(JAMSHELL), % expands to the command string and ! expands to the slot
22 * number (starting at 1) for multiprocess (-j) invocations. If $(JAMSHELL) does
23 * not include a %, it is tacked on as the last argument.
25 * Each $(JAMSHELL) placeholder must be specified as a separate individual
26 * element in a jam variable value.
28 * Do not just set JAMSHELL to cmd.exe - it will not work!
31 * exec_check() - preprocess and validate the command
32 * exec_cmd() - launch an async command execution
33 * exec_wait() - wait for any of the async command processes to terminate
36 * filetime_to_seconds() - Windows FILETIME --> number of seconds conversion
54 #define WIN32_LEAN_AND_MEAN
60 /* get the maximum shell command line length according to the OS */
62 /* valid raw command string length */
63 static long raw_command_length( char const * command
);
64 /* add two 64-bit unsigned numbers, h1l1 and h2l2 */
65 static FILETIME
add_64(
66 unsigned long h1
, unsigned long l1
,
67 unsigned long h2
, unsigned long l2
);
69 static FILETIME
add_FILETIME( FILETIME t1
, FILETIME t2
);
71 static FILETIME
negate_FILETIME( FILETIME t
);
72 /* record the timing info for the process */
73 static void record_times( HANDLE
const, timing_info
* const );
74 /* calc the current running time of an *active* process */
75 static double running_time( HANDLE
const );
76 /* terminate the given process, after terminating all its children first */
77 static void kill_process_tree( DWORD
const procesdId
, HANDLE
const );
78 /* waits for a command to complete or time out */
79 static int try_wait( int const timeoutMillis
);
80 /* reads any pending output for running commands */
81 static void read_output();
82 /* checks if a command ran out of time, and kills it */
83 static int try_kill_one();
84 /* is the first process a parent (direct or indirect) to the second one */
85 static int is_parent_child( DWORD
const parent
, DWORD
const child
);
87 static void close_alert( PROCESS_INFORMATION
const * const );
88 /* close any alerts hanging around */
89 static void close_alerts();
90 /* prepare a command file to be executed using an external shell */
91 static char const * prepare_command_file( string
const * command
, int slot
);
92 /* invoke the actual external process using the given command line */
93 static void invoke_cmd( char const * const command
, int const slot
);
94 /* find a free slot in the running commands table */
95 static int get_free_cmdtab_slot();
96 /* put together the final command string we are to run */
97 static void string_new_from_argv( string
* result
, char const * const * argv
);
98 /* frees and renews the given string */
99 static void string_renew( string
* const );
100 /* reports the last failed Windows API related error message */
101 static void reportWindowsError( char const * const apiName
, int slot
);
102 /* closes a Windows HANDLE and resets its variable to 0. */
103 static void closeWinHandle( HANDLE
* const handle
);
105 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
107 /* CreateProcessA() Windows API places a limit of 32768 characters (bytes) on
108 * the allowed command-line length, including a trailing Unicode (2-byte)
109 * nul-terminator character.
111 #define MAX_RAW_COMMAND_LENGTH 32766
113 /* We hold handles for pipes used to communicate with child processes in two
114 * element arrays indexed as follows.
116 #define EXECCMD_PIPE_READ 0
117 #define EXECCMD_PIPE_WRITE 1
119 static int intr_installed
;
122 /* The list of commands we run. */
125 /* Temporary command file used to execute the action when needed. */
126 string command_file
[ 1 ];
128 /* Pipes for communicating with the child process. Parent reads from (0),
129 * child writes to (1).
131 HANDLE pipe_out
[ 2 ];
132 HANDLE pipe_err
[ 2 ];
134 string buffer_out
[ 1 ]; /* buffer to hold stdout, if any */
135 string buffer_err
[ 1 ]; /* buffer to hold stderr, if any */
137 PROCESS_INFORMATION pi
; /* running process information */
139 /* Function called when the command completes. */
140 ExecCmdCallback func
;
142 /* Opaque data passed back to the 'func' callback. */
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.
159 typedef struct test
{ char * command
; int result
; } test
;
169 { " \n\t\t\v\r\r\n \t x \v \t\t\r\n\n\n \n\n\v\t", 8 },
172 { "echo x > foo.bar", -1 },
173 { "echo x < foo.bar", -1 },
174 { "echo x | foo.bar", -1 },
175 { "echo x \">\" foo.bar", 18 },
176 { "echo x '<' foo.bar", 18 },
177 { "echo x \"|\" foo.bar", 18 },
178 { "echo x \\\">\\\" foo.bar", -1 },
179 { "echo x \\\"<\\\" foo.bar", -1 },
180 { "echo x \\\"|\\\" foo.bar", -1 },
181 { "\"echo x > foo.bar\"", 18 },
182 { "echo x \"'\"<' foo.bar", -1 },
183 { "echo x \\\\\"<\\\\\" foo.bar", 22 },
184 { "echo x \\x\\\"<\\\\\" foo.bar", -1 },
187 for ( t
= tests
; t
->command
; ++t
)
188 assert( raw_command_length( t
->command
) == t
->result
);
192 int const length
= maxline() + 9;
193 char * const cmd
= (char *)BJAM_MALLOC_ATOMIC( length
+ 1 );
194 memset( cmd
, 'x', length
);
196 assert( raw_command_length( cmd
) == length
);
204 * exec_check() - preprocess and validate the command
209 string
const * command
,
212 int * error_max_length
215 /* Default shell does nothing when triggered with an empty or a
216 * whitespace-only command so we simply skip running it in that case. We
217 * still pass them on to non-default shells as we do not really know what
218 * they are going to do with such commands.
220 if ( list_empty( *pShell
) )
222 char const * s
= command
->value
;
223 while ( isspace( *s
) ) ++s
;
225 return EXEC_CHECK_NOOP
;
228 /* Check prerequisites for executing raw commands. */
229 if ( is_raw_command_request( *pShell
) )
231 int const raw_cmd_length
= raw_command_length( command
->value
);
232 if ( raw_cmd_length
< 0 )
234 /* Invalid characters detected - fallback to default shell. */
235 list_free( *pShell
);
238 else if ( raw_cmd_length
> MAX_RAW_COMMAND_LENGTH
)
240 *error_length
= raw_cmd_length
;
241 *error_max_length
= MAX_RAW_COMMAND_LENGTH
;
242 return EXEC_CHECK_TOO_LONG
;
245 return raw_cmd_length
? EXEC_CHECK_OK
: EXEC_CHECK_NOOP
;
248 /* Now we know we are using an external shell. Note that there is no need to
249 * check for too long command strings when using an external shell since we
250 * use a command file and assume no one is going to set up a JAMSHELL format
251 * string longer than a few hundred bytes at most which should be well under
252 * the total command string limit. Should someone actually construct such a
253 * JAMSHELL value it will get reported as an 'invalid parameter'
254 * CreateProcessA() Windows API failure which seems like a good enough
255 * result for such intentional mischief.
258 /* Check for too long command lines. */
259 return check_cmd_for_too_long_lines( command
->value
, maxline(),
260 error_length
, error_max_length
);
265 * exec_cmd() - launch an async command execution
267 * We assume exec_check() already verified that the given command can have its
268 * command string constructed as requested.
273 string
const * cmd_orig
,
274 ExecCmdCallback func
,
279 int const slot
= get_free_cmdtab_slot();
280 int const is_raw_cmd
= is_raw_command_request( shell
);
281 string cmd_local
[ 1 ];
283 /* Initialize default shell - anything more than /Q/C is non-portable. */
284 static LIST
* default_shell
;
285 if ( !default_shell
)
286 default_shell
= list_new( object_new( "cmd.exe /Q/C" ) );
288 /* Specifying no shell means requesting the default shell. */
289 if ( list_empty( shell
) )
290 shell
= default_shell
;
294 out_printf( "Executing raw command directly\n" );
297 out_printf( "Executing using a command file and the shell: " );
302 /* If we are running a raw command directly - trim its leading whitespaces
303 * as well as any trailing all-whitespace lines but keep any trailing
304 * whitespace in the final/only line containing something other than
309 char const * start
= cmd_orig
->value
;
310 char const * p
= cmd_orig
->value
+ cmd_orig
->size
;
311 char const * end
= p
;
312 while ( isspace( *start
) ) ++start
;
313 while ( p
> start
&& isspace( p
[ -1 ] ) )
316 string_new( cmd_local
);
317 string_append_range( cmd_local
, start
, end
);
318 assert( cmd_local
->size
== raw_command_length( cmd_orig
->value
) );
320 /* If we are not running a raw command directly, prepare a command file to
321 * be executed using an external shell and the actual command string using
326 char const * const cmd_file
= prepare_command_file( cmd_orig
, slot
);
327 char const * argv
[ MAXARGC
+ 1 ]; /* +1 for NULL */
328 argv_from_shell( argv
, shell
, cmd_file
, slot
);
329 string_new_from_argv( cmd_local
, argv
);
332 /* Catch interrupts whenever commands are running. */
333 if ( !intr_installed
)
336 signal( SIGINT
, onintr
);
339 /* Save input data into the selected running commands table slot. */
340 cmdtab
[ slot
].func
= func
;
341 cmdtab
[ slot
].closure
= closure
;
343 /* Invoke the actual external process using the constructed command line. */
344 invoke_cmd( cmd_local
->value
, slot
);
346 /* Free our local command string copy. */
347 string_free( cmd_local
);
352 * exec_wait() - wait for any of the async command processes to terminate
354 * Wait and drive at most one execution completion, while processing the I/O for
355 * all ongoing commands.
361 int exit_reason
; /* reason why a command completed */
363 /* Wait for a command to complete, while snarfing up any output. */
366 /* Check for a complete command, briefly. */
368 /* Read in the output of all running commands. */
370 /* Close out pending debug style dialogs. */
372 /* Process the completed command we found. */
373 if ( i
>= 0 ) { exit_reason
= EXIT_OK
; break; }
374 /* Check if a command ran out of time. */
376 if ( i
>= 0 ) { exit_reason
= EXIT_TIMEOUT
; break; }
379 /* We have a command... process it. */
385 /* The time data for the command. */
386 record_times( cmdtab
[ i
].pi
.hProcess
, &time
);
388 /* Removed the used temporary command file. */
389 if ( cmdtab
[ i
].command_file
->size
)
390 unlink( cmdtab
[ i
].command_file
->value
);
392 /* Find out the process exit code. */
393 GetExitCodeProcess( cmdtab
[ i
].pi
.hProcess
, &exit_code
);
395 /* The dispossition of the command. */
397 rstat
= EXEC_CMD_INTR
;
398 else if ( exit_code
)
399 rstat
= EXEC_CMD_FAIL
;
403 /* Call the callback, may call back to jam rule land. */
404 (*cmdtab
[ i
].func
)( cmdtab
[ i
].closure
, rstat
, &time
,
405 cmdtab
[ i
].buffer_out
->value
, cmdtab
[ i
].buffer_err
->value
,
408 /* Clean up our child process tracking data. No need to clear the
409 * temporary command file name as it gets reused.
411 closeWinHandle( &cmdtab
[ i
].pi
.hProcess
);
412 closeWinHandle( &cmdtab
[ i
].pi
.hThread
);
413 closeWinHandle( &cmdtab
[ i
].pipe_out
[ EXECCMD_PIPE_READ
] );
414 closeWinHandle( &cmdtab
[ i
].pipe_out
[ EXECCMD_PIPE_WRITE
] );
415 closeWinHandle( &cmdtab
[ i
].pipe_err
[ EXECCMD_PIPE_READ
] );
416 closeWinHandle( &cmdtab
[ i
].pipe_err
[ EXECCMD_PIPE_WRITE
] );
417 string_renew( cmdtab
[ i
].buffer_out
);
418 string_renew( cmdtab
[ i
].buffer_err
);
423 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
426 * Invoke the actual external process using the given command line. Track the
427 * process in our running commands table.
430 static void invoke_cmd( char const * const command
, int const slot
)
432 SECURITY_ATTRIBUTES sa
= { sizeof( SECURITY_ATTRIBUTES
), 0, 0 };
433 SECURITY_DESCRIPTOR sd
;
434 STARTUPINFO si
= { sizeof( STARTUPINFO
), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
437 /* Init the security data. */
438 InitializeSecurityDescriptor( &sd
, SECURITY_DESCRIPTOR_REVISION
);
439 SetSecurityDescriptorDacl( &sd
, TRUE
, NULL
, FALSE
);
440 sa
.lpSecurityDescriptor
= &sd
;
441 sa
.bInheritHandle
= TRUE
;
443 /* Create output buffers. */
444 string_new( cmdtab
[ slot
].buffer_out
);
445 string_new( cmdtab
[ slot
].buffer_err
);
447 /* Create pipes for communicating with the child process. */
448 if ( !CreatePipe( &cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_READ
],
449 &cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_WRITE
], &sa
, 0 ) )
451 reportWindowsError( "CreatePipe", slot
);
454 if ( globs
.pipe_action
&& !CreatePipe( &cmdtab
[ slot
].pipe_err
[
455 EXECCMD_PIPE_READ
], &cmdtab
[ slot
].pipe_err
[ EXECCMD_PIPE_WRITE
],
458 reportWindowsError( "CreatePipe", slot
);
462 /* Set handle inheritance off for the pipe ends the parent reads from. */
463 SetHandleInformation( cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_READ
],
464 HANDLE_FLAG_INHERIT
, 0 );
465 if ( globs
.pipe_action
)
466 SetHandleInformation( cmdtab
[ slot
].pipe_err
[ EXECCMD_PIPE_READ
],
467 HANDLE_FLAG_INHERIT
, 0 );
469 /* Hide the child window, if any. */
470 si
.dwFlags
|= STARTF_USESHOWWINDOW
;
471 si
.wShowWindow
= SW_HIDE
;
473 /* Redirect the child's output streams to our pipes. */
474 si
.dwFlags
|= STARTF_USESTDHANDLES
;
475 si
.hStdOutput
= cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_WRITE
];
476 si
.hStdError
= globs
.pipe_action
477 ? cmdtab
[ slot
].pipe_err
[ EXECCMD_PIPE_WRITE
]
478 : cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_WRITE
];
480 /* Let the child inherit stdin, as some commands assume it is available. */
481 si
.hStdInput
= GetStdHandle( STD_INPUT_HANDLE
);
484 out_printf( "Command string for CreateProcessA(): '%s'\n", command
);
486 /* Run the command by creating a sub-process for it. */
487 if ( !CreateProcessA(
488 NULL
, /* application name */
489 (char *)command
, /* command line */
490 NULL
, /* process attributes */
491 NULL
, /* thread attributes */
492 TRUE
, /* inherit handles */
493 CREATE_NEW_PROCESS_GROUP
, /* create flags */
494 NULL
, /* env vars, null inherits env */
495 NULL
, /* current dir, null is our current dir */
496 &si
, /* startup info */
497 &cmdtab
[ slot
].pi
) ) /* child process info, if created */
499 reportWindowsError( "CreateProcessA", slot
);
506 * For more details on Windows cmd.exe shell command-line length limitations see
507 * the following MSDN article:
508 * http://support.microsoft.com/default.aspx?scid=kb;en-us;830473
511 static int raw_maxline()
513 OSVERSIONINFO os_info
;
514 os_info
.dwOSVersionInfoSize
= sizeof( os_info
);
515 GetVersionEx( &os_info
);
517 if ( os_info
.dwMajorVersion
>= 5 ) return 8191; /* XP */
518 if ( os_info
.dwMajorVersion
== 4 ) return 2047; /* NT 4.x */
519 return 996; /* NT 3.5.1 */
525 if ( !result
) result
= raw_maxline();
531 * Closes a Windows HANDLE and resets its variable to 0.
534 static void closeWinHandle( HANDLE
* const handle
)
538 CloseHandle( *handle
);
545 * Frees and renews the given string.
548 static void string_renew( string
* const s
)
556 * raw_command_length() - valid raw command string length
558 * Checks whether the given command may be executed as a raw command. If yes,
559 * returns the corresponding command string length. If not, returns -1.
561 * Rules for constructing raw command strings:
562 * - Command may not contain unquoted shell I/O redirection characters.
563 * - May have at most one command line with non-whitespace content.
564 * - Leading whitespace trimmed.
565 * - Trailing all-whitespace lines trimmed.
566 * - Trailing whitespace on the sole command line kept (may theoretically
567 * affect the executed command).
570 static long raw_command_length( char const * command
)
573 char const * escape
= 0;
575 char const * newline
= 0;
577 /* Skip leading whitespace. */
578 while ( isspace( *command
) )
583 /* Look for newlines and unquoted I/O redirection. */
586 p
+= strcspn( p
, "\n\"'<>|\\" );
590 /* If our command contains non-whitespace content split over
591 * multiple lines we can not execute it directly.
594 while ( isspace( *++p
) );
599 escape
= escape
&& escape
== p
- 1 ? 0 : p
;
605 if ( escape
&& escape
== p
- 1 )
607 else if ( inquote
== *p
)
625 /* Return the number of characters the command will occupy. */
626 return ( newline
? newline
: p
) - command
;
630 /* 64-bit arithmetic helpers. */
632 /* Compute the carry bit from the addition of two 32-bit unsigned numbers. */
633 #define add_carry_bit( a, b ) ((((a) | (b)) >> 31) & (~((a) + (b)) >> 31) & 0x1)
635 /* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1
638 #define add_64_hi( h1, l1, h2, l2 ) ((h1) + (h2) + add_carry_bit(l1, l2))
642 * Add two 64-bit unsigned numbers, h1l1 and h2l2.
645 static FILETIME add_64
647 unsigned long h1
, unsigned long l1
,
648 unsigned long h2
, unsigned long l2
652 result
.dwLowDateTime
= l1
+ l2
;
653 result
.dwHighDateTime
= add_64_hi( h1
, l1
, h2
, l2
);
658 static FILETIME
add_FILETIME( FILETIME t1
, FILETIME t2
)
660 return add_64( t1
.dwHighDateTime
, t1
.dwLowDateTime
, t2
.dwHighDateTime
,
665 static FILETIME
negate_FILETIME( FILETIME t
)
667 /* 2s complement negation */
668 return add_64( ~t
.dwHighDateTime
, ~t
.dwLowDateTime
, 0, 1 );
673 * filetime_to_seconds() - Windows FILETIME --> number of seconds conversion
676 static double filetime_to_seconds( FILETIME
const ft
)
678 return ft
.dwHighDateTime
* ( (double)( 1UL << 31 ) * 2.0 * 1.0e-7 ) +
679 ft
.dwLowDateTime
* 1.0e-7;
683 static void record_times( HANDLE
const process
, timing_info
* const time
)
689 if ( GetProcessTimes( process
, &creation
, &exit
, &kernel
, &user
) )
691 time
->system
= filetime_to_seconds( kernel
);
692 time
->user
= filetime_to_seconds( user
);
693 timestamp_from_filetime( &time
->start
, &creation
);
694 timestamp_from_filetime( &time
->end
, &exit
);
699 #define IO_BUFFER_SIZE ( 16 * 1024 )
701 static char ioBuffer
[ IO_BUFFER_SIZE
+ 1 ];
704 static void read_pipe
706 HANDLE in
, /* the pipe to read from */
710 DWORD bytesInBuffer
= 0;
711 DWORD bytesAvailable
= 0;
715 /* check if we have any data to read */
716 if ( !PeekNamedPipe( in
, ioBuffer
, IO_BUFFER_SIZE
, &bytesInBuffer
,
717 &bytesAvailable
, NULL
) )
720 /* read in the available data */
721 if ( bytesAvailable
> 0 )
723 /* we only read in the available bytes, to avoid blocking */
724 if ( ReadFile( in
, ioBuffer
, bytesAvailable
<= IO_BUFFER_SIZE
?
725 bytesAvailable
: IO_BUFFER_SIZE
, &bytesInBuffer
, NULL
) )
727 if ( bytesInBuffer
> 0 )
729 /* Clean up some illegal chars. */
731 for ( i
= 0; i
< bytesInBuffer
; ++i
)
733 if ( ( (unsigned char)ioBuffer
[ i
] < 1 ) )
736 /* Null, terminate. */
737 ioBuffer
[ bytesInBuffer
] = '\0';
738 /* Append to the output. */
739 string_append( out
, ioBuffer
);
740 /* Subtract what we read in. */
741 bytesAvailable
-= bytesInBuffer
;
745 /* Likely read a error, bail out. */
751 /* Definitely read a error, bail out. */
756 while ( bytesAvailable
> 0 );
760 static void read_output()
763 for ( i
= 0; i
< globs
.jobs
; ++i
)
764 if ( cmdtab
[ i
].pi
.hProcess
)
766 /* Read stdout data. */
767 if ( cmdtab
[ i
].pipe_out
[ EXECCMD_PIPE_READ
] )
768 read_pipe( cmdtab
[ i
].pipe_out
[ EXECCMD_PIPE_READ
],
769 cmdtab
[ i
].buffer_out
);
770 /* Read stderr data. */
771 if ( cmdtab
[ i
].pipe_err
[ EXECCMD_PIPE_READ
] )
772 read_pipe( cmdtab
[ i
].pipe_err
[ EXECCMD_PIPE_READ
],
773 cmdtab
[ i
].buffer_err
);
779 * Waits for a single child process command to complete, or the timeout,
780 * whichever comes first. Returns the index of the completed command in the
781 * cmdtab array, or -1.
784 typedef struct _twh_params
787 HANDLE
* active_handles
;
792 static int try_wait_helper( twh_params
* );
794 static int try_wait( int const timeoutMillis
)
796 #define MAX_THREADS MAXJOBS/(MAXIMUM_WAIT_OBJECTS - 1) + 1
800 HANDLE active_handles
[ MAXJOBS
+ MAX_THREADS
];
801 int active_procs
[ MAXJOBS
+ MAX_THREADS
];
802 unsigned int num_threads
;
803 unsigned int num_handles
;
804 unsigned int last_chunk_size
;
805 unsigned int last_chunk_offset
;
806 HANDLE completed_event
= INVALID_HANDLE_VALUE
;
807 HANDLE thread_handles
[MAXIMUM_WAIT_OBJECTS
];
808 twh_params thread_params
[MAX_THREADS
];
812 /* Prepare a list of all active processes to wait for. */
813 for ( num_active
= 0, i
= 0; i
< globs
.jobs
; ++i
)
814 if ( cmdtab
[ i
].pi
.hProcess
)
816 if ( num_active
== MAXIMUM_WAIT_OBJECTS
)
819 * We surpassed MAXIMUM_WAIT_OBJECTS, so we need to use threads
820 * to wait for this set. Create an event object which will
821 * notify threads to stop waiting. Every handle set chunk should
822 * have this event as its last element.
824 assert( completed_event
== INVALID_HANDLE_VALUE
);
825 completed_event
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
826 active_handles
[ num_active
] = active_handles
[ num_active
- 1 ];
827 active_procs
[ num_active
] = active_procs
[ num_active
- 1 ];
828 active_handles
[ num_active
- 1 ] = completed_event
;
829 active_procs
[ num_active
- 1 ] = -1;
832 else if ( ( completed_event
!= INVALID_HANDLE_VALUE
) &&
833 !((num_active
+ 1) % MAXIMUM_WAIT_OBJECTS
) )
835 active_handles
[ num_active
] = completed_event
;
836 active_procs
[ num_active
] = -1;
839 active_handles
[ num_active
] = cmdtab
[ i
].pi
.hProcess
;
840 active_procs
[ num_active
] = i
;
844 assert( (num_active
<= MAXIMUM_WAIT_OBJECTS
) ==
845 (completed_event
== INVALID_HANDLE_VALUE
) );
846 if ( num_active
<= MAXIMUM_WAIT_OBJECTS
)
849 twh
.active_procs
= active_procs
;
850 twh
.active_handles
= active_handles
;
851 twh
.num_active
= num_active
;
852 twh
.timeoutMillis
= timeoutMillis
;
853 return try_wait_helper( &twh
);
856 num_threads
= num_active
/ MAXIMUM_WAIT_OBJECTS
;
857 last_chunk_size
= num_active
% MAXIMUM_WAIT_OBJECTS
;
858 num_handles
= num_threads
;
859 if ( last_chunk_size
)
861 /* Can we fit the last chunk in the outer WFMO call? */
862 if ( last_chunk_size
<= MAXIMUM_WAIT_OBJECTS
- num_threads
)
864 last_chunk_offset
= num_threads
* MAXIMUM_WAIT_OBJECTS
;
865 for ( i
= 0; i
< last_chunk_size
; ++i
)
866 thread_handles
[ i
+ num_threads
] =
867 active_handles
[ i
+ last_chunk_offset
];
868 num_handles
= num_threads
+ last_chunk_size
;
872 /* We need another thread for the remainder. */
873 /* Add completed_event handle to the last chunk. */
874 active_handles
[ num_active
] = completed_event
;
875 active_procs
[ num_active
] = -1;
879 num_handles
= num_threads
;
883 assert( num_threads
<= MAX_THREADS
);
885 for ( i
= 0; i
< num_threads
; ++i
)
887 thread_params
[i
].active_procs
= active_procs
+
888 i
* MAXIMUM_WAIT_OBJECTS
;
889 thread_params
[i
].active_handles
= active_handles
+
890 i
* MAXIMUM_WAIT_OBJECTS
;
891 thread_params
[i
].timeoutMillis
= INFINITE
;
892 thread_params
[i
].num_active
= MAXIMUM_WAIT_OBJECTS
;
893 if ( ( i
== num_threads
- 1 ) && last_chunk_size
&&
894 ( num_handles
== num_threads
) )
895 thread_params
[i
].num_active
= last_chunk_size
;
896 thread_handles
[i
] = CreateThread(NULL
, 4 * 1024,
897 (LPTHREAD_START_ROUTINE
)&try_wait_helper
, &thread_params
[i
],
900 wait_api_result
= WaitForMultipleObjects(num_handles
, thread_handles
,
901 FALSE
, timeoutMillis
);
902 if ( ( WAIT_OBJECT_0
<= wait_api_result
) &&
903 ( wait_api_result
< WAIT_OBJECT_0
+ num_threads
) )
905 HANDLE thread_handle
= thread_handles
[wait_api_result
- WAIT_OBJECT_0
];
906 success
= GetExitCodeThread(thread_handle
, (DWORD
*)&result
);
909 else if ( ( WAIT_OBJECT_0
+ num_threads
<= wait_api_result
) &&
910 ( wait_api_result
< WAIT_OBJECT_0
+ num_handles
) )
912 unsigned int offset
= wait_api_result
- num_threads
- WAIT_OBJECT_0
;
913 result
= active_procs
[ last_chunk_offset
+ offset
];
915 SetEvent(completed_event
);
916 /* Should complete instantly. */
917 WaitForMultipleObjects(num_threads
, thread_handles
, TRUE
, INFINITE
);
918 CloseHandle(completed_event
);
919 for ( i
= 0; i
< num_threads
; ++i
)
920 CloseHandle(thread_handles
[i
]);
925 static int try_wait_helper( twh_params
* params
)
929 assert( params
->num_active
<= MAXIMUM_WAIT_OBJECTS
);
931 /* Wait for a child to complete, or for our timeout window to expire. */
932 wait_api_result
= WaitForMultipleObjects( params
->num_active
,
933 params
->active_handles
, FALSE
, params
->timeoutMillis
);
934 if ( ( WAIT_OBJECT_0
<= wait_api_result
) &&
935 ( wait_api_result
< WAIT_OBJECT_0
+ params
->num_active
) )
937 /* Terminated process detected - return its index. */
938 return params
->active_procs
[ wait_api_result
- WAIT_OBJECT_0
];
946 static int try_kill_one()
948 /* Only need to check if a timeout was specified with the -l option. */
949 if ( globs
.timeout
> 0 )
952 for ( i
= 0; i
< globs
.jobs
; ++i
)
953 if ( cmdtab
[ i
].pi
.hProcess
)
955 double const t
= running_time( cmdtab
[ i
].pi
.hProcess
);
956 if ( t
> (double)globs
.timeout
)
958 /* The job may have left an alert dialog around, try and get
959 * rid of it before killing the job itself.
961 close_alert( &cmdtab
[ i
].pi
);
962 /* We have a "runaway" job, kill it. */
963 kill_process_tree( cmdtab
[ i
].pi
.dwProcessId
,
964 cmdtab
[ i
].pi
.hProcess
);
965 /* And return its running commands table slot. */
974 static void close_alerts()
976 /* We only attempt this every 5 seconds or so, because it is not a cheap
977 * operation, and we will catch the alerts eventually. This check uses
978 * floats as some compilers define CLOCKS_PER_SEC as a float or double.
980 if ( ( (float)clock() / (float)( CLOCKS_PER_SEC
* 5 ) ) < ( 1.0 / 5.0 ) )
983 for ( i
= 0; i
< globs
.jobs
; ++i
)
984 if ( cmdtab
[ i
].pi
.hProcess
)
985 close_alert( &cmdtab
[ i
].pi
);
991 * Calc the current running time of an *active* process.
994 static double running_time( HANDLE
const process
)
1000 if ( GetProcessTimes( process
, &creation
, &exit
, &kernel
, &user
) )
1002 /* Compute the elapsed time. */
1004 GetSystemTimeAsFileTime( ¤t
);
1005 return filetime_to_seconds( add_FILETIME( current
,
1006 negate_FILETIME( creation
) ) );
1013 * Not really optimal, or efficient, but it is easier this way, and it is not
1014 * like we are going to be killing thousands, or even tens of processes.
1017 static void kill_process_tree( DWORD
const pid
, HANDLE
const process
)
1019 HANDLE
const process_snapshot_h
= CreateToolhelp32Snapshot(
1020 TH32CS_SNAPPROCESS
, 0 );
1021 if ( INVALID_HANDLE_VALUE
!= process_snapshot_h
)
1024 PROCESSENTRY32 pinfo
;
1025 pinfo
.dwSize
= sizeof( PROCESSENTRY32
);
1027 ok
= Process32First( process_snapshot_h
, &pinfo
);
1029 ok
= Process32Next( process_snapshot_h
, &pinfo
) )
1031 if ( pinfo
.th32ParentProcessID
== pid
)
1033 /* Found a child, recurse to kill it and anything else below it.
1035 HANDLE
const ph
= OpenProcess( PROCESS_ALL_ACCESS
, FALSE
,
1036 pinfo
.th32ProcessID
);
1039 kill_process_tree( pinfo
.th32ProcessID
, ph
);
1044 CloseHandle( process_snapshot_h
);
1046 /* Now that the children are all dead, kill the root. */
1047 TerminateProcess( process
, -2 );
1051 static double creation_time( HANDLE
const process
)
1057 return GetProcessTimes( process
, &creation
, &exit
, &kernel
, &user
)
1058 ? filetime_to_seconds( creation
)
1064 * Recursive check if first process is parent (directly or indirectly) of the
1065 * second one. Both processes are passed as process ids, not handles. Special
1066 * return value 2 means that the second process is smss.exe and its parent
1067 * process is System (first argument is ignored).
1070 static int is_parent_child( DWORD
const parent
, DWORD
const child
)
1072 HANDLE process_snapshot_h
= INVALID_HANDLE_VALUE
;
1076 if ( parent
== child
)
1079 process_snapshot_h
= CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS
, 0 );
1080 if ( INVALID_HANDLE_VALUE
!= process_snapshot_h
)
1083 PROCESSENTRY32 pinfo
;
1084 pinfo
.dwSize
= sizeof( PROCESSENTRY32
);
1086 ok
= Process32First( process_snapshot_h
, &pinfo
);
1088 ok
= Process32Next( process_snapshot_h
, &pinfo
) )
1090 if ( pinfo
.th32ProcessID
== child
)
1092 /* Unfortunately, process ids are not really unique. There might
1093 * be spurious "parent and child" relationship match between two
1094 * non-related processes if real parent process of a given
1095 * process has exited (while child process kept running as an
1096 * "orphan") and the process id of such parent process has been
1097 * reused by internals of the operating system when creating
1100 * Thus an additional check is needed - process creation time.
1101 * This check may fail (i.e. return 0) for system processes due
1102 * to insufficient privileges, and that is OK.
1104 double tchild
= 0.0;
1105 double tparent
= 0.0;
1106 HANDLE
const hchild
= OpenProcess( PROCESS_QUERY_INFORMATION
,
1107 FALSE
, pinfo
.th32ProcessID
);
1108 CloseHandle( process_snapshot_h
);
1110 /* csrss.exe may display message box like following:
1111 * xyz.exe - Unable To Locate Component
1112 * This application has failed to start because
1113 * boost_foo-bar.dll was not found. Re-installing the
1114 * application may fix the problem
1115 * This actually happens when starting a test process that
1116 * depends on a dynamic library which failed to build. We want
1117 * to automatically close these message boxes even though
1118 * csrss.exe is not our child process. We may depend on the fact
1119 * that (in all current versions of Windows) csrss.exe is a
1120 * direct child of the smss.exe process, which in turn is a
1121 * direct child of the System process, which always has process
1122 * id == 4. This check must be performed before comparing
1123 * process creation times.
1125 if ( !stricmp( pinfo
.szExeFile
, "csrss.exe" ) &&
1126 is_parent_child( parent
, pinfo
.th32ParentProcessID
) == 2 )
1128 if ( !stricmp( pinfo
.szExeFile
, "smss.exe" ) &&
1129 ( pinfo
.th32ParentProcessID
== 4 ) )
1134 HANDLE hparent
= OpenProcess( PROCESS_QUERY_INFORMATION
,
1135 FALSE
, pinfo
.th32ParentProcessID
);
1138 tchild
= creation_time( hchild
);
1139 tparent
= creation_time( hparent
);
1140 CloseHandle( hparent
);
1142 CloseHandle( hchild
);
1145 /* Return 0 if one of the following is true:
1146 * 1. we failed to read process creation time
1147 * 2. child was created before alleged parent
1149 if ( ( tchild
== 0.0 ) || ( tparent
== 0.0 ) ||
1150 ( tchild
< tparent
) )
1153 return is_parent_child( parent
, pinfo
.th32ParentProcessID
) & 1;
1157 CloseHandle( process_snapshot_h
);
1165 * Called by the OS for each topmost window.
1168 BOOL CALLBACK
close_alert_window_enum( HWND hwnd
, LPARAM lParam
)
1170 char buf
[ 7 ] = { 0 };
1171 PROCESS_INFORMATION
const * const pi
= (PROCESS_INFORMATION
*)lParam
;
1175 /* We want to find and close any window that:
1177 * 2. is a dialog and
1178 * 3. is displayed by any of our child processes
1181 /* We assume hidden windows do not require user interaction. */
1182 !IsWindowVisible( hwnd
)
1183 /* Failed to read class name; presume it is not a dialog. */
1184 || !GetClassNameA( hwnd
, buf
, sizeof( buf
) )
1185 /* All Windows system dialogs use the same Window class name. */
1186 || strcmp( buf
, "#32770" ) )
1189 /* GetWindowThreadProcessId() returns 0 on error, otherwise thread id of
1190 * the window's message pump thread.
1192 tid
= GetWindowThreadProcessId( hwnd
, &pid
);
1193 if ( !tid
|| !is_parent_child( pi
->dwProcessId
, pid
) )
1196 /* Ask real nice. */
1197 PostMessageA( hwnd
, WM_CLOSE
, 0, 0 );
1199 /* Wait and see if it worked. If not, insist. */
1200 if ( WaitForSingleObject( pi
->hProcess
, 200 ) == WAIT_TIMEOUT
)
1202 PostThreadMessageA( tid
, WM_QUIT
, 0, 0 );
1203 WaitForSingleObject( pi
->hProcess
, 300 );
1206 /* Done, we do not want to check any other windows now. */
1211 static void close_alert( PROCESS_INFORMATION
const * const pi
)
1213 EnumWindows( &close_alert_window_enum
, (LPARAM
)pi
);
1218 * Open a command file to store the command into for executing using an external
1219 * shell. Returns a pointer to a FILE open for writing or 0 in case such a file
1220 * could not be opened. The file name used is stored back in the corresponding
1221 * running commands table slot.
1223 * Expects the running commands table slot's command_file attribute to contain
1224 * either a zeroed out string object or one prepared previously by this same
1228 static FILE * open_command_file( int const slot
)
1230 string
* const command_file
= cmdtab
[ slot
].command_file
;
1232 /* If the temporary command file name has not already been prepared for this
1233 * slot number, prepare a new one containing a '##' place holder that will
1234 * be changed later and needs to be located at a fixed distance from the
1237 if ( !command_file
->value
)
1239 DWORD
const procID
= GetCurrentProcessId();
1240 string
const * const tmpdir
= path_tmpdir();
1241 string_new( command_file
);
1242 string_reserve( command_file
, tmpdir
->size
+ 64 );
1243 command_file
->size
= sprintf( command_file
->value
,
1244 "%s\\jam%d-%02d-##.bat", tmpdir
->value
, procID
, slot
);
1247 /* For some reason opening a command file can fail intermittently. But doing
1248 * some retries works. Most likely this is due to a previously existing file
1249 * of the same name that happens to still be opened by an active virus
1250 * scanner. Originally pointed out and fixed by Bronek Kozicki.
1252 * We first try to open several differently named files to avoid having to
1253 * wait idly if not absolutely necessary. Our temporary command file names
1254 * contain a fixed position place holder we use for generating different
1258 char * const index1
= command_file
->value
+ command_file
->size
- 6;
1259 char * const index2
= index1
+ 1;
1260 int waits_remaining
;
1261 assert( command_file
->value
< index1
);
1262 assert( index2
+ 1 < command_file
->value
+ command_file
->size
);
1263 assert( index2
[ 1 ] == '.' );
1264 for ( waits_remaining
= 3; ; --waits_remaining
)
1267 for ( index
= 0; index
!= 20; ++index
)
1270 *index1
= '0' + index
/ 10;
1271 *index2
= '0' + index
% 10;
1272 f
= fopen( command_file
->value
, "w" );
1275 if ( !waits_remaining
) break;
1285 * Prepare a command file to be executed using an external shell.
1288 static char const * prepare_command_file( string
const * command
, int slot
)
1290 FILE * const f
= open_command_file( slot
);
1293 err_printf( "failed to write command file!\n" );
1296 fputs( command
->value
, f
);
1298 return cmdtab
[ slot
].command_file
->value
;
1303 * Find a free slot in the running commands table.
1306 static int get_free_cmdtab_slot()
1309 for ( slot
= 0; slot
< MAXJOBS
; ++slot
)
1310 if ( !cmdtab
[ slot
].pi
.hProcess
)
1312 err_printf( "no slots for child!\n" );
1318 * Put together the final command string we are to run.
1321 static void string_new_from_argv( string
* result
, char const * const * argv
)
1324 assert( argv
[ 0 ] );
1325 string_copy( result
, *(argv
++) );
1328 string_push_back( result
, ' ' );
1329 string_append( result
, *(argv
++) );
1335 * Reports the last failed Windows API related error message.
1338 static void reportWindowsError( char const * const apiName
, int slot
)
1340 char * errorMessage
;
1344 DWORD
const errorCode
= GetLastError();
1345 DWORD apiResult
= FormatMessageA(
1346 FORMAT_MESSAGE_ALLOCATE_BUFFER
| /* __in DWORD dwFlags */
1347 FORMAT_MESSAGE_FROM_SYSTEM
|
1348 FORMAT_MESSAGE_IGNORE_INSERTS
,
1349 NULL
, /* __in_opt LPCVOID lpSource */
1350 errorCode
, /* __in DWORD dwMessageId */
1351 0, /* __in DWORD dwLanguageId */
1352 (LPSTR
)&errorMessage
, /* __out LPTSTR lpBuffer */
1353 0, /* __in DWORD nSize */
1354 0 ); /* __in_opt va_list * Arguments */
1356 /* Build a message as if the process had written to stderr. */
1357 if ( globs
.pipe_action
)
1358 err_buf
= cmdtab
[ slot
].buffer_err
;
1360 err_buf
= cmdtab
[ slot
].buffer_out
;
1361 string_append( err_buf
, apiName
);
1362 string_append( err_buf
, "() Windows API failed: " );
1363 sprintf( buf
, "%d", errorCode
);
1364 string_append( err_buf
, buf
);
1367 string_append( err_buf
, ".\n" );
1370 string_append( err_buf
, " - " );
1371 string_append( err_buf
, errorMessage
);
1372 /* Make sure that the buffer is terminated with a newline */
1373 if( err_buf
->value
[ err_buf
->size
- 1 ] != '\n' )
1374 string_push_back( err_buf
, '\n' );
1375 LocalFree( errorMessage
);
1378 /* Since the process didn't actually start, use a blank timing_info. */
1381 timestamp_current( &time
.start
);
1382 timestamp_current( &time
.end
);
1384 /* Invoke the callback with a failure status. */
1385 (*cmdtab
[ slot
].func
)( cmdtab
[ slot
].closure
, EXEC_CMD_FAIL
, &time
,
1386 cmdtab
[ slot
].buffer_out
->value
, cmdtab
[ slot
].buffer_err
->value
,
1389 /* Clean up any handles that were opened. */
1390 closeWinHandle( &cmdtab
[ slot
].pi
.hProcess
);
1391 closeWinHandle( &cmdtab
[ slot
].pi
.hThread
);
1392 closeWinHandle( &cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_READ
] );
1393 closeWinHandle( &cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_WRITE
] );
1394 closeWinHandle( &cmdtab
[ slot
].pipe_err
[ EXECCMD_PIPE_READ
] );
1395 closeWinHandle( &cmdtab
[ slot
].pipe_err
[ EXECCMD_PIPE_WRITE
] );
1396 string_renew( cmdtab
[ slot
].buffer_out
);
1397 string_renew( cmdtab
[ slot
].buffer_err
);
1401 #endif /* USE_EXECNT */