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
);
104 /* Adds the job index to the list of currently active jobs. */
105 static void register_wait( int job_id
);
107 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
109 /* CreateProcessA() Windows API places a limit of 32768 characters (bytes) on
110 * the allowed command-line length, including a trailing Unicode (2-byte)
111 * nul-terminator character.
113 #define MAX_RAW_COMMAND_LENGTH 32766
115 /* We hold handles for pipes used to communicate with child processes in two
116 * element arrays indexed as follows.
118 #define EXECCMD_PIPE_READ 0
119 #define EXECCMD_PIPE_WRITE 1
121 static int intr_installed
;
124 /* The list of commands we run. */
125 static struct _cmdtab_t
127 /* Temporary command file used to execute the action when needed. */
128 string command_file
[ 1 ];
130 /* Pipes for communicating with the child process. Parent reads from (0),
131 * child writes to (1).
133 HANDLE pipe_out
[ 2 ];
134 HANDLE pipe_err
[ 2 ];
136 string buffer_out
[ 1 ]; /* buffer to hold stdout, if any */
137 string buffer_err
[ 1 ]; /* buffer to hold stderr, if any */
139 PROCESS_INFORMATION pi
; /* running process information */
143 /* Function called when the command completes. */
144 ExecCmdCallback func
;
146 /* Opaque data passed back to the 'func' callback. */
149 static int cmdtab_size
= 0;
151 /* A thread-safe single element queue. Used by the worker threads
152 * to signal the main thread that a process is completed.
162 * Execution unit tests.
165 void execnt_unit_test()
167 #if !defined( NDEBUG )
168 /* vc6 preprocessor is broken, so assert with these strings gets confused.
169 * Use a table instead.
172 typedef struct test
{ char * command
; int result
; } test
;
182 { " \n\t\t\v\r\r\n \t x \v \t\t\r\n\n\n \n\n\v\t", 8 },
185 { "echo x > foo.bar", -1 },
186 { "echo x < foo.bar", -1 },
187 { "echo x | foo.bar", -1 },
188 { "echo x \">\" foo.bar", 18 },
189 { "echo x '<' foo.bar", 18 },
190 { "echo x \"|\" foo.bar", 18 },
191 { "echo x \\\">\\\" foo.bar", -1 },
192 { "echo x \\\"<\\\" foo.bar", -1 },
193 { "echo x \\\"|\\\" foo.bar", -1 },
194 { "\"echo x > foo.bar\"", 18 },
195 { "echo x \"'\"<' foo.bar", -1 },
196 { "echo x \\\\\"<\\\\\" foo.bar", 22 },
197 { "echo x \\x\\\"<\\\\\" foo.bar", -1 },
200 for ( t
= tests
; t
->command
; ++t
)
201 assert( raw_command_length( t
->command
) == t
->result
);
205 int const length
= maxline() + 9;
206 char * const cmd
= (char *)BJAM_MALLOC_ATOMIC( length
+ 1 );
207 memset( cmd
, 'x', length
);
209 assert( raw_command_length( cmd
) == length
);
216 * exec_init() - global initialization
218 void exec_init( void )
220 if ( globs
.jobs
> cmdtab_size
)
222 cmdtab
= BJAM_REALLOC( cmdtab
, globs
.jobs
* sizeof( *cmdtab
) );
223 memset( cmdtab
+ cmdtab_size
, 0, ( globs
.jobs
- cmdtab_size
) * sizeof( *cmdtab
) );
224 cmdtab_size
= globs
.jobs
;
226 if ( globs
.jobs
> MAXIMUM_WAIT_OBJECTS
&& !process_queue
.read_okay
)
228 process_queue
.read_okay
= CreateEvent( NULL
, FALSE
, FALSE
, NULL
);
229 process_queue
.write_okay
= CreateEvent( NULL
, FALSE
, TRUE
, NULL
);
234 * exec_done - free resources.
236 void exec_done( void )
238 if ( process_queue
.read_okay
)
240 CloseHandle( process_queue
.read_okay
);
242 if ( process_queue
.write_okay
)
244 CloseHandle( process_queue
.write_okay
);
250 * exec_check() - preprocess and validate the command
255 string
const * command
,
258 int * error_max_length
261 /* Default shell does nothing when triggered with an empty or a
262 * whitespace-only command so we simply skip running it in that case. We
263 * still pass them on to non-default shells as we do not really know what
264 * they are going to do with such commands.
266 if ( list_empty( *pShell
) )
268 char const * s
= command
->value
;
269 while ( isspace( *s
) ) ++s
;
271 return EXEC_CHECK_NOOP
;
274 /* Check prerequisites for executing raw commands. */
275 if ( is_raw_command_request( *pShell
) )
277 int const raw_cmd_length
= raw_command_length( command
->value
);
278 if ( raw_cmd_length
< 0 )
280 /* Invalid characters detected - fallback to default shell. */
281 list_free( *pShell
);
284 else if ( raw_cmd_length
> MAX_RAW_COMMAND_LENGTH
)
286 *error_length
= raw_cmd_length
;
287 *error_max_length
= MAX_RAW_COMMAND_LENGTH
;
288 return EXEC_CHECK_TOO_LONG
;
291 return raw_cmd_length
? EXEC_CHECK_OK
: EXEC_CHECK_NOOP
;
294 /* Now we know we are using an external shell. Note that there is no need to
295 * check for too long command strings when using an external shell since we
296 * use a command file and assume no one is going to set up a JAMSHELL format
297 * string longer than a few hundred bytes at most which should be well under
298 * the total command string limit. Should someone actually construct such a
299 * JAMSHELL value it will get reported as an 'invalid parameter'
300 * CreateProcessA() Windows API failure which seems like a good enough
301 * result for such intentional mischief.
304 /* Check for too long command lines. */
305 return check_cmd_for_too_long_lines( command
->value
, maxline(),
306 error_length
, error_max_length
);
311 * exec_cmd() - launch an async command execution
313 * We assume exec_check() already verified that the given command can have its
314 * command string constructed as requested.
319 string
const * cmd_orig
,
320 ExecCmdCallback func
,
325 int const slot
= get_free_cmdtab_slot();
326 int const is_raw_cmd
= is_raw_command_request( shell
);
327 string cmd_local
[ 1 ];
329 /* Initialize default shell - anything more than /Q/C is non-portable. */
330 static LIST
* default_shell
;
331 if ( !default_shell
)
332 default_shell
= list_new( object_new( "cmd.exe /Q/C" ) );
334 /* Specifying no shell means requesting the default shell. */
335 if ( list_empty( shell
) )
336 shell
= default_shell
;
340 out_printf( "Executing raw command directly\n" );
343 out_printf( "Executing using a command file and the shell: " );
348 /* If we are running a raw command directly - trim its leading whitespaces
349 * as well as any trailing all-whitespace lines but keep any trailing
350 * whitespace in the final/only line containing something other than
355 char const * start
= cmd_orig
->value
;
356 char const * p
= cmd_orig
->value
+ cmd_orig
->size
;
357 char const * end
= p
;
358 while ( isspace( *start
) ) ++start
;
359 while ( p
> start
&& isspace( p
[ -1 ] ) )
362 string_new( cmd_local
);
363 string_append_range( cmd_local
, start
, end
);
364 assert( cmd_local
->size
== raw_command_length( cmd_orig
->value
) );
366 /* If we are not running a raw command directly, prepare a command file to
367 * be executed using an external shell and the actual command string using
372 char const * const cmd_file
= prepare_command_file( cmd_orig
, slot
);
373 char const * argv
[ MAXARGC
+ 1 ]; /* +1 for NULL */
374 argv_from_shell( argv
, shell
, cmd_file
, slot
);
375 string_new_from_argv( cmd_local
, argv
);
378 /* Catch interrupts whenever commands are running. */
379 if ( !intr_installed
)
382 signal( SIGINT
, onintr
);
385 /* Save input data into the selected running commands table slot. */
386 cmdtab
[ slot
].func
= func
;
387 cmdtab
[ slot
].closure
= closure
;
389 /* Invoke the actual external process using the constructed command line. */
390 invoke_cmd( cmd_local
->value
, slot
);
392 /* Free our local command string copy. */
393 string_free( cmd_local
);
398 * exec_wait() - wait for any of the async command processes to terminate
400 * Wait and drive at most one execution completion, while processing the I/O for
401 * all ongoing commands.
407 int exit_reason
; /* reason why a command completed */
409 /* Wait for a command to complete, while snarfing up any output. */
412 /* Check for a complete command, briefly. */
414 /* Read in the output of all running commands. */
416 /* Close out pending debug style dialogs. */
418 /* Process the completed command we found. */
419 if ( i
>= 0 ) { exit_reason
= EXIT_OK
; break; }
420 /* Check if a command ran out of time. */
422 if ( i
>= 0 ) { exit_reason
= EXIT_TIMEOUT
; break; }
425 /* We have a command... process it. */
431 /* The time data for the command. */
432 record_times( cmdtab
[ i
].pi
.hProcess
, &time
);
434 /* Removed the used temporary command file. */
435 if ( cmdtab
[ i
].command_file
->size
)
436 unlink( cmdtab
[ i
].command_file
->value
);
438 /* Find out the process exit code. */
439 GetExitCodeProcess( cmdtab
[ i
].pi
.hProcess
, &exit_code
);
441 /* The dispossition of the command. */
443 rstat
= EXEC_CMD_INTR
;
444 else if ( exit_code
)
445 rstat
= EXEC_CMD_FAIL
;
449 /* Call the callback, may call back to jam rule land. */
450 (*cmdtab
[ i
].func
)( cmdtab
[ i
].closure
, rstat
, &time
,
451 cmdtab
[ i
].buffer_out
->value
, cmdtab
[ i
].buffer_err
->value
,
454 /* Clean up our child process tracking data. No need to clear the
455 * temporary command file name as it gets reused.
457 closeWinHandle( &cmdtab
[ i
].pi
.hProcess
);
458 closeWinHandle( &cmdtab
[ i
].pi
.hThread
);
459 closeWinHandle( &cmdtab
[ i
].pipe_out
[ EXECCMD_PIPE_READ
] );
460 closeWinHandle( &cmdtab
[ i
].pipe_out
[ EXECCMD_PIPE_WRITE
] );
461 closeWinHandle( &cmdtab
[ i
].pipe_err
[ EXECCMD_PIPE_READ
] );
462 closeWinHandle( &cmdtab
[ i
].pipe_err
[ EXECCMD_PIPE_WRITE
] );
463 string_renew( cmdtab
[ i
].buffer_out
);
464 string_renew( cmdtab
[ i
].buffer_err
);
469 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
472 * Invoke the actual external process using the given command line. Track the
473 * process in our running commands table.
476 static void invoke_cmd( char const * const command
, int const slot
)
478 SECURITY_ATTRIBUTES sa
= { sizeof( SECURITY_ATTRIBUTES
), 0, 0 };
479 SECURITY_DESCRIPTOR sd
;
480 STARTUPINFO si
= { sizeof( STARTUPINFO
), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
483 /* Init the security data. */
484 InitializeSecurityDescriptor( &sd
, SECURITY_DESCRIPTOR_REVISION
);
485 SetSecurityDescriptorDacl( &sd
, TRUE
, NULL
, FALSE
);
486 sa
.lpSecurityDescriptor
= &sd
;
487 sa
.bInheritHandle
= TRUE
;
489 /* Create output buffers. */
490 string_new( cmdtab
[ slot
].buffer_out
);
491 string_new( cmdtab
[ slot
].buffer_err
);
493 /* Create pipes for communicating with the child process. */
494 if ( !CreatePipe( &cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_READ
],
495 &cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_WRITE
], &sa
, 0 ) )
497 reportWindowsError( "CreatePipe", slot
);
500 if ( globs
.pipe_action
&& !CreatePipe( &cmdtab
[ slot
].pipe_err
[
501 EXECCMD_PIPE_READ
], &cmdtab
[ slot
].pipe_err
[ EXECCMD_PIPE_WRITE
],
504 reportWindowsError( "CreatePipe", slot
);
508 /* Set handle inheritance off for the pipe ends the parent reads from. */
509 SetHandleInformation( cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_READ
],
510 HANDLE_FLAG_INHERIT
, 0 );
511 if ( globs
.pipe_action
)
512 SetHandleInformation( cmdtab
[ slot
].pipe_err
[ EXECCMD_PIPE_READ
],
513 HANDLE_FLAG_INHERIT
, 0 );
515 /* Hide the child window, if any. */
516 si
.dwFlags
|= STARTF_USESHOWWINDOW
;
517 si
.wShowWindow
= SW_HIDE
;
519 /* Redirect the child's output streams to our pipes. */
520 si
.dwFlags
|= STARTF_USESTDHANDLES
;
521 si
.hStdOutput
= cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_WRITE
];
522 si
.hStdError
= globs
.pipe_action
523 ? cmdtab
[ slot
].pipe_err
[ EXECCMD_PIPE_WRITE
]
524 : cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_WRITE
];
526 /* Let the child inherit stdin, as some commands assume it is available. */
527 si
.hStdInput
= GetStdHandle( STD_INPUT_HANDLE
);
530 out_printf( "Command string for CreateProcessA(): '%s'\n", command
);
532 /* Run the command by creating a sub-process for it. */
533 if ( !CreateProcessA(
534 NULL
, /* application name */
535 (char *)command
, /* command line */
536 NULL
, /* process attributes */
537 NULL
, /* thread attributes */
538 TRUE
, /* inherit handles */
539 CREATE_NEW_PROCESS_GROUP
, /* create flags */
540 NULL
, /* env vars, null inherits env */
541 NULL
, /* current dir, null is our current dir */
542 &si
, /* startup info */
543 &cmdtab
[ slot
].pi
) ) /* child process info, if created */
545 reportWindowsError( "CreateProcessA", slot
);
549 register_wait( slot
);
554 * For more details on Windows cmd.exe shell command-line length limitations see
555 * the following MSDN article:
556 * http://support.microsoft.com/default.aspx?scid=kb;en-us;830473
559 static int raw_maxline()
561 OSVERSIONINFO os_info
;
562 os_info
.dwOSVersionInfoSize
= sizeof( os_info
);
563 GetVersionEx( &os_info
);
565 if ( os_info
.dwMajorVersion
>= 5 ) return 8191; /* XP */
566 if ( os_info
.dwMajorVersion
== 4 ) return 2047; /* NT 4.x */
567 return 996; /* NT 3.5.1 */
573 if ( !result
) result
= raw_maxline();
579 * Closes a Windows HANDLE and resets its variable to 0.
582 static void closeWinHandle( HANDLE
* const handle
)
586 CloseHandle( *handle
);
593 * Frees and renews the given string.
596 static void string_renew( string
* const s
)
604 * raw_command_length() - valid raw command string length
606 * Checks whether the given command may be executed as a raw command. If yes,
607 * returns the corresponding command string length. If not, returns -1.
609 * Rules for constructing raw command strings:
610 * - Command may not contain unquoted shell I/O redirection characters.
611 * - May have at most one command line with non-whitespace content.
612 * - Leading whitespace trimmed.
613 * - Trailing all-whitespace lines trimmed.
614 * - Trailing whitespace on the sole command line kept (may theoretically
615 * affect the executed command).
618 static long raw_command_length( char const * command
)
621 char const * escape
= 0;
623 char const * newline
= 0;
625 /* Skip leading whitespace. */
626 while ( isspace( *command
) )
631 /* Look for newlines and unquoted I/O redirection. */
634 p
+= strcspn( p
, "\n\"'<>|\\" );
638 /* If our command contains non-whitespace content split over
639 * multiple lines we can not execute it directly.
642 while ( isspace( *++p
) );
647 escape
= escape
&& escape
== p
- 1 ? 0 : p
;
653 if ( escape
&& escape
== p
- 1 )
655 else if ( inquote
== *p
)
673 /* Return the number of characters the command will occupy. */
674 return ( newline
? newline
: p
) - command
;
678 /* 64-bit arithmetic helpers. */
680 /* Compute the carry bit from the addition of two 32-bit unsigned numbers. */
681 #define add_carry_bit( a, b ) ((((a) | (b)) >> 31) & (~((a) + (b)) >> 31) & 0x1)
683 /* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1
686 #define add_64_hi( h1, l1, h2, l2 ) ((h1) + (h2) + add_carry_bit(l1, l2))
690 * Add two 64-bit unsigned numbers, h1l1 and h2l2.
693 static FILETIME add_64
695 unsigned long h1
, unsigned long l1
,
696 unsigned long h2
, unsigned long l2
700 result
.dwLowDateTime
= l1
+ l2
;
701 result
.dwHighDateTime
= add_64_hi( h1
, l1
, h2
, l2
);
706 static FILETIME
add_FILETIME( FILETIME t1
, FILETIME t2
)
708 return add_64( t1
.dwHighDateTime
, t1
.dwLowDateTime
, t2
.dwHighDateTime
,
713 static FILETIME
negate_FILETIME( FILETIME t
)
715 /* 2s complement negation */
716 return add_64( ~t
.dwHighDateTime
, ~t
.dwLowDateTime
, 0, 1 );
721 * filetime_to_seconds() - Windows FILETIME --> number of seconds conversion
724 static double filetime_to_seconds( FILETIME
const ft
)
726 return ft
.dwHighDateTime
* ( (double)( 1UL << 31 ) * 2.0 * 1.0e-7 ) +
727 ft
.dwLowDateTime
* 1.0e-7;
731 static void record_times( HANDLE
const process
, timing_info
* const time
)
737 if ( GetProcessTimes( process
, &creation
, &exit
, &kernel
, &user
) )
739 time
->system
= filetime_to_seconds( kernel
);
740 time
->user
= filetime_to_seconds( user
);
741 timestamp_from_filetime( &time
->start
, &creation
);
742 timestamp_from_filetime( &time
->end
, &exit
);
747 #define IO_BUFFER_SIZE ( 16 * 1024 )
749 static char ioBuffer
[ IO_BUFFER_SIZE
+ 1 ];
752 static void read_pipe
754 HANDLE in
, /* the pipe to read from */
758 DWORD bytesInBuffer
= 0;
759 DWORD bytesAvailable
= 0;
763 /* check if we have any data to read */
764 if ( !PeekNamedPipe( in
, ioBuffer
, IO_BUFFER_SIZE
, &bytesInBuffer
,
765 &bytesAvailable
, NULL
) )
768 /* read in the available data */
769 if ( bytesAvailable
> 0 )
771 /* we only read in the available bytes, to avoid blocking */
772 if ( ReadFile( in
, ioBuffer
, bytesAvailable
<= IO_BUFFER_SIZE
?
773 bytesAvailable
: IO_BUFFER_SIZE
, &bytesInBuffer
, NULL
) )
775 if ( bytesInBuffer
> 0 )
777 /* Clean up some illegal chars. */
779 for ( i
= 0; i
< bytesInBuffer
; ++i
)
781 if ( ( (unsigned char)ioBuffer
[ i
] < 1 ) )
784 /* Null, terminate. */
785 ioBuffer
[ bytesInBuffer
] = '\0';
786 /* Append to the output. */
787 string_append( out
, ioBuffer
);
788 /* Subtract what we read in. */
789 bytesAvailable
-= bytesInBuffer
;
793 /* Likely read a error, bail out. */
799 /* Definitely read a error, bail out. */
804 while ( bytesAvailable
> 0 );
808 static void read_output()
811 for ( i
= 0; i
< globs
.jobs
; ++i
)
812 if ( cmdtab
[ i
].pi
.hProcess
)
814 /* Read stdout data. */
815 if ( cmdtab
[ i
].pipe_out
[ EXECCMD_PIPE_READ
] )
816 read_pipe( cmdtab
[ i
].pipe_out
[ EXECCMD_PIPE_READ
],
817 cmdtab
[ i
].buffer_out
);
818 /* Read stderr data. */
819 if ( cmdtab
[ i
].pipe_err
[ EXECCMD_PIPE_READ
] )
820 read_pipe( cmdtab
[ i
].pipe_err
[ EXECCMD_PIPE_READ
],
821 cmdtab
[ i
].buffer_err
);
825 static void CALLBACK
try_wait_callback( void * data
, BOOLEAN is_timeout
)
827 struct _cmdtab_t
* slot
= ( struct _cmdtab_t
* )data
;
828 WaitForSingleObject( process_queue
.write_okay
, INFINITE
);
829 process_queue
.job_index
= slot
- cmdtab
;
830 assert( !is_timeout
);
831 SetEvent( process_queue
.read_okay
);
832 /* Okay. Non-blocking. */
833 UnregisterWait( slot
->wait_handle
);
836 static int try_wait_impl( DWORD timeout
)
840 int res
= WaitForSingleObject( process_queue
.read_okay
, timeout
);
841 if ( res
!= WAIT_OBJECT_0
)
843 job_index
= process_queue
.job_index
;
844 SetEvent( process_queue
.write_okay
);
848 static void register_wait( int job_id
)
850 if ( globs
.jobs
> MAXIMUM_WAIT_OBJECTS
)
853 RegisterWaitForSingleObject( &cmdtab
[ job_id
].wait_handle
,
854 cmdtab
[ job_id
].pi
.hProcess
,
855 &try_wait_callback
, &cmdtab
[ job_id
], INFINITE
,
856 WT_EXECUTEDEFAULT
| WT_EXECUTEONLYONCE
);
861 * Waits for a single child process command to complete, or the timeout,
862 * whichever comes first. Returns the index of the completed command in the
863 * cmdtab array, or -1.
866 static int try_wait( int const timeoutMillis
)
868 if ( globs
.jobs
<= MAXIMUM_WAIT_OBJECTS
)
871 HANDLE active_handles
[ MAXIMUM_WAIT_OBJECTS
];
872 int job_ids
[ MAXIMUM_WAIT_OBJECTS
];
873 DWORD num_handles
= 0;
874 DWORD wait_api_result
;
875 for ( i
= 0; i
< globs
.jobs
; ++i
)
877 if( cmdtab
[ i
].pi
.hProcess
)
879 job_ids
[ num_handles
] = i
;
880 active_handles
[ num_handles
] = cmdtab
[ i
].pi
.hProcess
;
884 wait_api_result
= WaitForMultipleObjects( num_handles
, active_handles
, FALSE
, timeoutMillis
);
885 if ( WAIT_OBJECT_0
<= wait_api_result
&& wait_api_result
< WAIT_OBJECT_0
+ globs
.jobs
)
887 return job_ids
[ wait_api_result
- WAIT_OBJECT_0
];
896 return try_wait_impl( timeoutMillis
);
902 static int try_kill_one()
904 /* Only need to check if a timeout was specified with the -l option. */
905 if ( globs
.timeout
> 0 )
908 for ( i
= 0; i
< globs
.jobs
; ++i
)
909 if ( cmdtab
[ i
].pi
.hProcess
)
911 double const t
= running_time( cmdtab
[ i
].pi
.hProcess
);
912 if ( t
> (double)globs
.timeout
)
914 /* The job may have left an alert dialog around, try and get
915 * rid of it before killing the job itself.
917 close_alert( &cmdtab
[ i
].pi
);
918 /* We have a "runaway" job, kill it. */
919 kill_process_tree( cmdtab
[ i
].pi
.dwProcessId
,
920 cmdtab
[ i
].pi
.hProcess
);
921 /* And return its running commands table slot. */
930 static void close_alerts()
932 /* We only attempt this every 5 seconds or so, because it is not a cheap
933 * operation, and we will catch the alerts eventually. This check uses
934 * floats as some compilers define CLOCKS_PER_SEC as a float or double.
936 if ( ( (float)clock() / (float)( CLOCKS_PER_SEC
* 5 ) ) < ( 1.0 / 5.0 ) )
939 for ( i
= 0; i
< globs
.jobs
; ++i
)
940 if ( cmdtab
[ i
].pi
.hProcess
)
941 close_alert( &cmdtab
[ i
].pi
);
947 * Calc the current running time of an *active* process.
950 static double running_time( HANDLE
const process
)
956 if ( GetProcessTimes( process
, &creation
, &exit
, &kernel
, &user
) )
958 /* Compute the elapsed time. */
960 GetSystemTimeAsFileTime( ¤t
);
961 return filetime_to_seconds( add_FILETIME( current
,
962 negate_FILETIME( creation
) ) );
969 * Not really optimal, or efficient, but it is easier this way, and it is not
970 * like we are going to be killing thousands, or even tens of processes.
973 static void kill_process_tree( DWORD
const pid
, HANDLE
const process
)
975 HANDLE
const process_snapshot_h
= CreateToolhelp32Snapshot(
976 TH32CS_SNAPPROCESS
, 0 );
977 if ( INVALID_HANDLE_VALUE
!= process_snapshot_h
)
980 PROCESSENTRY32 pinfo
;
981 pinfo
.dwSize
= sizeof( PROCESSENTRY32
);
983 ok
= Process32First( process_snapshot_h
, &pinfo
);
985 ok
= Process32Next( process_snapshot_h
, &pinfo
) )
987 if ( pinfo
.th32ParentProcessID
== pid
)
989 /* Found a child, recurse to kill it and anything else below it.
991 HANDLE
const ph
= OpenProcess( PROCESS_ALL_ACCESS
, FALSE
,
992 pinfo
.th32ProcessID
);
995 kill_process_tree( pinfo
.th32ProcessID
, ph
);
1000 CloseHandle( process_snapshot_h
);
1002 /* Now that the children are all dead, kill the root. */
1003 TerminateProcess( process
, -2 );
1007 static double creation_time( HANDLE
const process
)
1013 return GetProcessTimes( process
, &creation
, &exit
, &kernel
, &user
)
1014 ? filetime_to_seconds( creation
)
1020 * Recursive check if first process is parent (directly or indirectly) of the
1021 * second one. Both processes are passed as process ids, not handles. Special
1022 * return value 2 means that the second process is smss.exe and its parent
1023 * process is System (first argument is ignored).
1026 static int is_parent_child( DWORD
const parent
, DWORD
const child
)
1028 HANDLE process_snapshot_h
= INVALID_HANDLE_VALUE
;
1032 if ( parent
== child
)
1035 process_snapshot_h
= CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS
, 0 );
1036 if ( INVALID_HANDLE_VALUE
!= process_snapshot_h
)
1039 PROCESSENTRY32 pinfo
;
1040 pinfo
.dwSize
= sizeof( PROCESSENTRY32
);
1042 ok
= Process32First( process_snapshot_h
, &pinfo
);
1044 ok
= Process32Next( process_snapshot_h
, &pinfo
) )
1046 if ( pinfo
.th32ProcessID
== child
)
1048 /* Unfortunately, process ids are not really unique. There might
1049 * be spurious "parent and child" relationship match between two
1050 * non-related processes if real parent process of a given
1051 * process has exited (while child process kept running as an
1052 * "orphan") and the process id of such parent process has been
1053 * reused by internals of the operating system when creating
1056 * Thus an additional check is needed - process creation time.
1057 * This check may fail (i.e. return 0) for system processes due
1058 * to insufficient privileges, and that is OK.
1060 double tchild
= 0.0;
1061 double tparent
= 0.0;
1062 HANDLE
const hchild
= OpenProcess( PROCESS_QUERY_INFORMATION
,
1063 FALSE
, pinfo
.th32ProcessID
);
1064 CloseHandle( process_snapshot_h
);
1066 /* csrss.exe may display message box like following:
1067 * xyz.exe - Unable To Locate Component
1068 * This application has failed to start because
1069 * boost_foo-bar.dll was not found. Re-installing the
1070 * application may fix the problem
1071 * This actually happens when starting a test process that
1072 * depends on a dynamic library which failed to build. We want
1073 * to automatically close these message boxes even though
1074 * csrss.exe is not our child process. We may depend on the fact
1075 * that (in all current versions of Windows) csrss.exe is a
1076 * direct child of the smss.exe process, which in turn is a
1077 * direct child of the System process, which always has process
1078 * id == 4. This check must be performed before comparing
1079 * process creation times.
1081 if ( !stricmp( pinfo
.szExeFile
, "csrss.exe" ) &&
1082 is_parent_child( parent
, pinfo
.th32ParentProcessID
) == 2 )
1084 if ( !stricmp( pinfo
.szExeFile
, "smss.exe" ) &&
1085 ( pinfo
.th32ParentProcessID
== 4 ) )
1090 HANDLE hparent
= OpenProcess( PROCESS_QUERY_INFORMATION
,
1091 FALSE
, pinfo
.th32ParentProcessID
);
1094 tchild
= creation_time( hchild
);
1095 tparent
= creation_time( hparent
);
1096 CloseHandle( hparent
);
1098 CloseHandle( hchild
);
1101 /* Return 0 if one of the following is true:
1102 * 1. we failed to read process creation time
1103 * 2. child was created before alleged parent
1105 if ( ( tchild
== 0.0 ) || ( tparent
== 0.0 ) ||
1106 ( tchild
< tparent
) )
1109 return is_parent_child( parent
, pinfo
.th32ParentProcessID
) & 1;
1113 CloseHandle( process_snapshot_h
);
1121 * Called by the OS for each topmost window.
1124 BOOL CALLBACK
close_alert_window_enum( HWND hwnd
, LPARAM lParam
)
1126 char buf
[ 7 ] = { 0 };
1127 PROCESS_INFORMATION
const * const pi
= (PROCESS_INFORMATION
*)lParam
;
1131 /* We want to find and close any window that:
1133 * 2. is a dialog and
1134 * 3. is displayed by any of our child processes
1137 /* We assume hidden windows do not require user interaction. */
1138 !IsWindowVisible( hwnd
)
1139 /* Failed to read class name; presume it is not a dialog. */
1140 || !GetClassNameA( hwnd
, buf
, sizeof( buf
) )
1141 /* All Windows system dialogs use the same Window class name. */
1142 || strcmp( buf
, "#32770" ) )
1145 /* GetWindowThreadProcessId() returns 0 on error, otherwise thread id of
1146 * the window's message pump thread.
1148 tid
= GetWindowThreadProcessId( hwnd
, &pid
);
1149 if ( !tid
|| !is_parent_child( pi
->dwProcessId
, pid
) )
1152 /* Ask real nice. */
1153 PostMessageA( hwnd
, WM_CLOSE
, 0, 0 );
1155 /* Wait and see if it worked. If not, insist. */
1156 if ( WaitForSingleObject( pi
->hProcess
, 200 ) == WAIT_TIMEOUT
)
1158 PostThreadMessageA( tid
, WM_QUIT
, 0, 0 );
1159 WaitForSingleObject( pi
->hProcess
, 300 );
1162 /* Done, we do not want to check any other windows now. */
1167 static void close_alert( PROCESS_INFORMATION
const * const pi
)
1169 EnumWindows( &close_alert_window_enum
, (LPARAM
)pi
);
1174 * Open a command file to store the command into for executing using an external
1175 * shell. Returns a pointer to a FILE open for writing or 0 in case such a file
1176 * could not be opened. The file name used is stored back in the corresponding
1177 * running commands table slot.
1179 * Expects the running commands table slot's command_file attribute to contain
1180 * either a zeroed out string object or one prepared previously by this same
1184 static FILE * open_command_file( int const slot
)
1186 string
* const command_file
= cmdtab
[ slot
].command_file
;
1188 /* If the temporary command file name has not already been prepared for this
1189 * slot number, prepare a new one containing a '##' place holder that will
1190 * be changed later and needs to be located at a fixed distance from the
1193 if ( !command_file
->value
)
1195 DWORD
const procID
= GetCurrentProcessId();
1196 string
const * const tmpdir
= path_tmpdir();
1197 string_new( command_file
);
1198 string_reserve( command_file
, tmpdir
->size
+ 64 );
1199 command_file
->size
= sprintf( command_file
->value
,
1200 "%s\\jam%d-%02d-##.bat", tmpdir
->value
, procID
, slot
);
1203 /* For some reason opening a command file can fail intermittently. But doing
1204 * some retries works. Most likely this is due to a previously existing file
1205 * of the same name that happens to still be opened by an active virus
1206 * scanner. Originally pointed out and fixed by Bronek Kozicki.
1208 * We first try to open several differently named files to avoid having to
1209 * wait idly if not absolutely necessary. Our temporary command file names
1210 * contain a fixed position place holder we use for generating different
1214 char * const index1
= command_file
->value
+ command_file
->size
- 6;
1215 char * const index2
= index1
+ 1;
1216 int waits_remaining
;
1217 assert( command_file
->value
< index1
);
1218 assert( index2
+ 1 < command_file
->value
+ command_file
->size
);
1219 assert( index2
[ 1 ] == '.' );
1220 for ( waits_remaining
= 3; ; --waits_remaining
)
1223 for ( index
= 0; index
!= 20; ++index
)
1226 *index1
= '0' + index
/ 10;
1227 *index2
= '0' + index
% 10;
1228 f
= fopen( command_file
->value
, "w" );
1231 if ( !waits_remaining
) break;
1241 * Prepare a command file to be executed using an external shell.
1244 static char const * prepare_command_file( string
const * command
, int slot
)
1246 FILE * const f
= open_command_file( slot
);
1249 err_printf( "failed to write command file!\n" );
1252 fputs( command
->value
, f
);
1254 return cmdtab
[ slot
].command_file
->value
;
1259 * Find a free slot in the running commands table.
1262 static int get_free_cmdtab_slot()
1265 for ( slot
= 0; slot
< globs
.jobs
; ++slot
)
1266 if ( !cmdtab
[ slot
].pi
.hProcess
)
1268 err_printf( "no slots for child!\n" );
1274 * Put together the final command string we are to run.
1277 static void string_new_from_argv( string
* result
, char const * const * argv
)
1280 assert( argv
[ 0 ] );
1281 string_copy( result
, *(argv
++) );
1284 string_push_back( result
, ' ' );
1285 string_append( result
, *(argv
++) );
1291 * Reports the last failed Windows API related error message.
1294 static void reportWindowsError( char const * const apiName
, int slot
)
1296 char * errorMessage
;
1300 DWORD
const errorCode
= GetLastError();
1301 DWORD apiResult
= FormatMessageA(
1302 FORMAT_MESSAGE_ALLOCATE_BUFFER
| /* __in DWORD dwFlags */
1303 FORMAT_MESSAGE_FROM_SYSTEM
|
1304 FORMAT_MESSAGE_IGNORE_INSERTS
,
1305 NULL
, /* __in_opt LPCVOID lpSource */
1306 errorCode
, /* __in DWORD dwMessageId */
1307 0, /* __in DWORD dwLanguageId */
1308 (LPSTR
)&errorMessage
, /* __out LPTSTR lpBuffer */
1309 0, /* __in DWORD nSize */
1310 0 ); /* __in_opt va_list * Arguments */
1312 /* Build a message as if the process had written to stderr. */
1313 if ( globs
.pipe_action
)
1314 err_buf
= cmdtab
[ slot
].buffer_err
;
1316 err_buf
= cmdtab
[ slot
].buffer_out
;
1317 string_append( err_buf
, apiName
);
1318 string_append( err_buf
, "() Windows API failed: " );
1319 sprintf( buf
, "%d", errorCode
);
1320 string_append( err_buf
, buf
);
1323 string_append( err_buf
, ".\n" );
1326 string_append( err_buf
, " - " );
1327 string_append( err_buf
, errorMessage
);
1328 /* Make sure that the buffer is terminated with a newline */
1329 if( err_buf
->value
[ err_buf
->size
- 1 ] != '\n' )
1330 string_push_back( err_buf
, '\n' );
1331 LocalFree( errorMessage
);
1334 /* Since the process didn't actually start, use a blank timing_info. */
1337 timestamp_current( &time
.start
);
1338 timestamp_current( &time
.end
);
1340 /* Invoke the callback with a failure status. */
1341 (*cmdtab
[ slot
].func
)( cmdtab
[ slot
].closure
, EXEC_CMD_FAIL
, &time
,
1342 cmdtab
[ slot
].buffer_out
->value
, cmdtab
[ slot
].buffer_err
->value
,
1345 /* Clean up any handles that were opened. */
1346 closeWinHandle( &cmdtab
[ slot
].pi
.hProcess
);
1347 closeWinHandle( &cmdtab
[ slot
].pi
.hThread
);
1348 closeWinHandle( &cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_READ
] );
1349 closeWinHandle( &cmdtab
[ slot
].pipe_out
[ EXECCMD_PIPE_WRITE
] );
1350 closeWinHandle( &cmdtab
[ slot
].pipe_err
[ EXECCMD_PIPE_READ
] );
1351 closeWinHandle( &cmdtab
[ slot
].pipe_err
[ EXECCMD_PIPE_WRITE
] );
1352 string_renew( cmdtab
[ slot
].buffer_out
);
1353 string_renew( cmdtab
[ slot
].buffer_err
);
1357 #endif /* USE_EXECNT */