]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/build/src/engine/execnt.c
update sources to v12.2.3
[ceph.git] / ceph / src / boost / tools / build / src / engine / execnt.c
1 /*
2 * Copyright 1993, 1995 Christopher Seiwald.
3 *
4 * This file is part of Jam - see jam.c for Copyright information.
5 */
6
7 /* This file is ALSO:
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)
13 */
14
15 /*
16 * execnt.c - execute a shell command on Windows NT
17 *
18 * If $(JAMSHELL) is defined, uses that to formulate the actual command. The
19 * default is: cmd.exe /Q/C
20 *
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.
24 *
25 * Each $(JAMSHELL) placeholder must be specified as a separate individual
26 * element in a jam variable value.
27 *
28 * Do not just set JAMSHELL to cmd.exe - it will not work!
29 *
30 * External routines:
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
34 *
35 * Internal routines:
36 * filetime_to_seconds() - Windows FILETIME --> number of seconds conversion
37 */
38
39 #include "jam.h"
40 #include "output.h"
41 #ifdef USE_EXECNT
42 #include "execcmd.h"
43
44 #include "lists.h"
45 #include "output.h"
46 #include "pathsys.h"
47 #include "string.h"
48
49 #include <assert.h>
50 #include <ctype.h>
51 #include <errno.h>
52 #include <time.h>
53
54 #define WIN32_LEAN_AND_MEAN
55 #include <windows.h>
56 #include <process.h>
57 #include <tlhelp32.h>
58
59
60 /* get the maximum shell command line length according to the OS */
61 static int maxline();
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 );
68 /* */
69 static FILETIME add_FILETIME( FILETIME t1, FILETIME t2 );
70 /* */
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 );
86 /* */
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 );
106
107 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
108
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.
112 */
113 #define MAX_RAW_COMMAND_LENGTH 32766
114
115 /* We hold handles for pipes used to communicate with child processes in two
116 * element arrays indexed as follows.
117 */
118 #define EXECCMD_PIPE_READ 0
119 #define EXECCMD_PIPE_WRITE 1
120
121 static int intr_installed;
122
123
124 /* The list of commands we run. */
125 static struct _cmdtab_t
126 {
127 /* Temporary command file used to execute the action when needed. */
128 string command_file[ 1 ];
129
130 /* Pipes for communicating with the child process. Parent reads from (0),
131 * child writes to (1).
132 */
133 HANDLE pipe_out[ 2 ];
134 HANDLE pipe_err[ 2 ];
135
136 string buffer_out[ 1 ]; /* buffer to hold stdout, if any */
137 string buffer_err[ 1 ]; /* buffer to hold stderr, if any */
138
139 PROCESS_INFORMATION pi; /* running process information */
140
141 HANDLE wait_handle;
142
143 /* Function called when the command completes. */
144 ExecCmdCallback func;
145
146 /* Opaque data passed back to the 'func' callback. */
147 void * closure;
148 } * cmdtab = NULL;
149 static int cmdtab_size = 0;
150
151 /* A thread-safe single element queue. Used by the worker threads
152 * to signal the main thread that a process is completed.
153 */
154 struct
155 {
156 int job_index;
157 HANDLE read_okay;
158 HANDLE write_okay;
159 } process_queue;
160
161 /*
162 * Execution unit tests.
163 */
164
165 void execnt_unit_test()
166 {
167 #if !defined( NDEBUG )
168 /* vc6 preprocessor is broken, so assert with these strings gets confused.
169 * Use a table instead.
170 */
171 {
172 typedef struct test { char * command; int result; } test;
173 test tests[] = {
174 { "", 0 },
175 { " ", 0 },
176 { "x", 1 },
177 { "\nx", 1 },
178 { "x\n", 1 },
179 { "\nx\n", 1 },
180 { "\nx \n", 2 },
181 { "\nx \n ", 2 },
182 { " \n\t\t\v\r\r\n \t x \v \t\t\r\n\n\n \n\n\v\t", 8 },
183 { "x\ny", -1 },
184 { "x\n\n y", -1 },
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 },
198 { 0 } };
199 test const * t;
200 for ( t = tests; t->command; ++t )
201 assert( raw_command_length( t->command ) == t->result );
202 }
203
204 {
205 int const length = maxline() + 9;
206 char * const cmd = (char *)BJAM_MALLOC_ATOMIC( length + 1 );
207 memset( cmd, 'x', length );
208 cmd[ length ] = 0;
209 assert( raw_command_length( cmd ) == length );
210 BJAM_FREE( cmd );
211 }
212 #endif
213 }
214
215 /*
216 * exec_init() - global initialization
217 */
218 void exec_init( void )
219 {
220 if ( globs.jobs > cmdtab_size )
221 {
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;
225 }
226 if ( globs.jobs > MAXIMUM_WAIT_OBJECTS && !process_queue.read_okay )
227 {
228 process_queue.read_okay = CreateEvent( NULL, FALSE, FALSE, NULL );
229 process_queue.write_okay = CreateEvent( NULL, FALSE, TRUE, NULL );
230 }
231 }
232
233 /*
234 * exec_done - free resources.
235 */
236 void exec_done( void )
237 {
238 if ( process_queue.read_okay )
239 {
240 CloseHandle( process_queue.read_okay );
241 }
242 if ( process_queue.write_okay )
243 {
244 CloseHandle( process_queue.write_okay );
245 }
246 BJAM_FREE( cmdtab );
247 }
248
249 /*
250 * exec_check() - preprocess and validate the command
251 */
252
253 int exec_check
254 (
255 string const * command,
256 LIST * * pShell,
257 int * error_length,
258 int * error_max_length
259 )
260 {
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.
265 */
266 if ( list_empty( *pShell ) )
267 {
268 char const * s = command->value;
269 while ( isspace( *s ) ) ++s;
270 if ( !*s )
271 return EXEC_CHECK_NOOP;
272 }
273
274 /* Check prerequisites for executing raw commands. */
275 if ( is_raw_command_request( *pShell ) )
276 {
277 int const raw_cmd_length = raw_command_length( command->value );
278 if ( raw_cmd_length < 0 )
279 {
280 /* Invalid characters detected - fallback to default shell. */
281 list_free( *pShell );
282 *pShell = L0;
283 }
284 else if ( raw_cmd_length > MAX_RAW_COMMAND_LENGTH )
285 {
286 *error_length = raw_cmd_length;
287 *error_max_length = MAX_RAW_COMMAND_LENGTH;
288 return EXEC_CHECK_TOO_LONG;
289 }
290 else
291 return raw_cmd_length ? EXEC_CHECK_OK : EXEC_CHECK_NOOP;
292 }
293
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.
302 */
303
304 /* Check for too long command lines. */
305 return check_cmd_for_too_long_lines( command->value, maxline(),
306 error_length, error_max_length );
307 }
308
309
310 /*
311 * exec_cmd() - launch an async command execution
312 *
313 * We assume exec_check() already verified that the given command can have its
314 * command string constructed as requested.
315 */
316
317 void exec_cmd
318 (
319 string const * cmd_orig,
320 ExecCmdCallback func,
321 void * closure,
322 LIST * shell
323 )
324 {
325 int const slot = get_free_cmdtab_slot();
326 int const is_raw_cmd = is_raw_command_request( shell );
327 string cmd_local[ 1 ];
328
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" ) );
333
334 /* Specifying no shell means requesting the default shell. */
335 if ( list_empty( shell ) )
336 shell = default_shell;
337
338 if ( DEBUG_EXECCMD )
339 if ( is_raw_cmd )
340 out_printf( "Executing raw command directly\n" );
341 else
342 {
343 out_printf( "Executing using a command file and the shell: " );
344 list_print( shell );
345 out_printf( "\n" );
346 }
347
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
351 * whitespace).
352 */
353 if ( is_raw_cmd )
354 {
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 ] ) )
360 if ( *--p == '\n' )
361 end = p;
362 string_new( cmd_local );
363 string_append_range( cmd_local, start, end );
364 assert( cmd_local->size == raw_command_length( cmd_orig->value ) );
365 }
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
368 * that command file.
369 */
370 else
371 {
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 );
376 }
377
378 /* Catch interrupts whenever commands are running. */
379 if ( !intr_installed )
380 {
381 intr_installed = 1;
382 signal( SIGINT, onintr );
383 }
384
385 /* Save input data into the selected running commands table slot. */
386 cmdtab[ slot ].func = func;
387 cmdtab[ slot ].closure = closure;
388
389 /* Invoke the actual external process using the constructed command line. */
390 invoke_cmd( cmd_local->value, slot );
391
392 /* Free our local command string copy. */
393 string_free( cmd_local );
394 }
395
396
397 /*
398 * exec_wait() - wait for any of the async command processes to terminate
399 *
400 * Wait and drive at most one execution completion, while processing the I/O for
401 * all ongoing commands.
402 */
403
404 void exec_wait()
405 {
406 int i = -1;
407 int exit_reason; /* reason why a command completed */
408
409 /* Wait for a command to complete, while snarfing up any output. */
410 while ( 1 )
411 {
412 /* Check for a complete command, briefly. */
413 i = try_wait( 500 );
414 /* Read in the output of all running commands. */
415 read_output();
416 /* Close out pending debug style dialogs. */
417 close_alerts();
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. */
421 i = try_kill_one();
422 if ( i >= 0 ) { exit_reason = EXIT_TIMEOUT; break; }
423 }
424
425 /* We have a command... process it. */
426 {
427 DWORD exit_code;
428 timing_info time;
429 int rstat;
430
431 /* The time data for the command. */
432 record_times( cmdtab[ i ].pi.hProcess, &time );
433
434 /* Removed the used temporary command file. */
435 if ( cmdtab[ i ].command_file->size )
436 unlink( cmdtab[ i ].command_file->value );
437
438 /* Find out the process exit code. */
439 GetExitCodeProcess( cmdtab[ i ].pi.hProcess, &exit_code );
440
441 /* The dispossition of the command. */
442 if ( interrupted() )
443 rstat = EXEC_CMD_INTR;
444 else if ( exit_code )
445 rstat = EXEC_CMD_FAIL;
446 else
447 rstat = EXEC_CMD_OK;
448
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,
452 exit_reason );
453
454 /* Clean up our child process tracking data. No need to clear the
455 * temporary command file name as it gets reused.
456 */
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 );
465 }
466 }
467
468
469 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
470
471 /*
472 * Invoke the actual external process using the given command line. Track the
473 * process in our running commands table.
474 */
475
476 static void invoke_cmd( char const * const command, int const slot )
477 {
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,
481 0, 0, 0, 0, 0, 0 };
482
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;
488
489 /* Create output buffers. */
490 string_new( cmdtab[ slot ].buffer_out );
491 string_new( cmdtab[ slot ].buffer_err );
492
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 ) )
496 {
497 reportWindowsError( "CreatePipe", slot );
498 return;
499 }
500 if ( globs.pipe_action && !CreatePipe( &cmdtab[ slot ].pipe_err[
501 EXECCMD_PIPE_READ ], &cmdtab[ slot ].pipe_err[ EXECCMD_PIPE_WRITE ],
502 &sa, 0 ) )
503 {
504 reportWindowsError( "CreatePipe", slot );
505 return;
506 }
507
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 );
514
515 /* Hide the child window, if any. */
516 si.dwFlags |= STARTF_USESHOWWINDOW;
517 si.wShowWindow = SW_HIDE;
518
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 ];
525
526 /* Let the child inherit stdin, as some commands assume it is available. */
527 si.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
528
529 if ( DEBUG_EXECCMD )
530 out_printf( "Command string for CreateProcessA(): '%s'\n", command );
531
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 */
544 {
545 reportWindowsError( "CreateProcessA", slot );
546 return;
547 }
548
549 register_wait( slot );
550 }
551
552
553 /*
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
557 */
558
559 static int raw_maxline()
560 {
561 OSVERSIONINFO os_info;
562 os_info.dwOSVersionInfoSize = sizeof( os_info );
563 GetVersionEx( &os_info );
564
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 */
568 }
569
570 static int maxline()
571 {
572 static int result;
573 if ( !result ) result = raw_maxline();
574 return result;
575 }
576
577
578 /*
579 * Closes a Windows HANDLE and resets its variable to 0.
580 */
581
582 static void closeWinHandle( HANDLE * const handle )
583 {
584 if ( *handle )
585 {
586 CloseHandle( *handle );
587 *handle = 0;
588 }
589 }
590
591
592 /*
593 * Frees and renews the given string.
594 */
595
596 static void string_renew( string * const s )
597 {
598 string_free( s );
599 string_new( s );
600 }
601
602
603 /*
604 * raw_command_length() - valid raw command string length
605 *
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.
608 *
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).
616 */
617
618 static long raw_command_length( char const * command )
619 {
620 char const * p;
621 char const * escape = 0;
622 char inquote = 0;
623 char const * newline = 0;
624
625 /* Skip leading whitespace. */
626 while ( isspace( *command ) )
627 ++command;
628
629 p = command;
630
631 /* Look for newlines and unquoted I/O redirection. */
632 do
633 {
634 p += strcspn( p, "\n\"'<>|\\" );
635 switch ( *p )
636 {
637 case '\n':
638 /* If our command contains non-whitespace content split over
639 * multiple lines we can not execute it directly.
640 */
641 newline = p;
642 while ( isspace( *++p ) );
643 if ( *p ) return -1;
644 break;
645
646 case '\\':
647 escape = escape && escape == p - 1 ? 0 : p;
648 ++p;
649 break;
650
651 case '"':
652 case '\'':
653 if ( escape && escape == p - 1 )
654 escape = 0;
655 else if ( inquote == *p )
656 inquote = 0;
657 else if ( !inquote )
658 inquote = *p;
659 ++p;
660 break;
661
662 case '<':
663 case '>':
664 case '|':
665 if ( !inquote )
666 return -1;
667 ++p;
668 break;
669 }
670 }
671 while ( *p );
672
673 /* Return the number of characters the command will occupy. */
674 return ( newline ? newline : p ) - command;
675 }
676
677
678 /* 64-bit arithmetic helpers. */
679
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)
682
683 /* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1
684 * and h2l2.
685 */
686 #define add_64_hi( h1, l1, h2, l2 ) ((h1) + (h2) + add_carry_bit(l1, l2))
687
688
689 /*
690 * Add two 64-bit unsigned numbers, h1l1 and h2l2.
691 */
692
693 static FILETIME add_64
694 (
695 unsigned long h1, unsigned long l1,
696 unsigned long h2, unsigned long l2
697 )
698 {
699 FILETIME result;
700 result.dwLowDateTime = l1 + l2;
701 result.dwHighDateTime = add_64_hi( h1, l1, h2, l2 );
702 return result;
703 }
704
705
706 static FILETIME add_FILETIME( FILETIME t1, FILETIME t2 )
707 {
708 return add_64( t1.dwHighDateTime, t1.dwLowDateTime, t2.dwHighDateTime,
709 t2.dwLowDateTime );
710 }
711
712
713 static FILETIME negate_FILETIME( FILETIME t )
714 {
715 /* 2s complement negation */
716 return add_64( ~t.dwHighDateTime, ~t.dwLowDateTime, 0, 1 );
717 }
718
719
720 /*
721 * filetime_to_seconds() - Windows FILETIME --> number of seconds conversion
722 */
723
724 static double filetime_to_seconds( FILETIME const ft )
725 {
726 return ft.dwHighDateTime * ( (double)( 1UL << 31 ) * 2.0 * 1.0e-7 ) +
727 ft.dwLowDateTime * 1.0e-7;
728 }
729
730
731 static void record_times( HANDLE const process, timing_info * const time )
732 {
733 FILETIME creation;
734 FILETIME exit;
735 FILETIME kernel;
736 FILETIME user;
737 if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) )
738 {
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 );
743 }
744 }
745
746
747 #define IO_BUFFER_SIZE ( 16 * 1024 )
748
749 static char ioBuffer[ IO_BUFFER_SIZE + 1 ];
750
751
752 static void read_pipe
753 (
754 HANDLE in, /* the pipe to read from */
755 string * out
756 )
757 {
758 DWORD bytesInBuffer = 0;
759 DWORD bytesAvailable = 0;
760
761 do
762 {
763 /* check if we have any data to read */
764 if ( !PeekNamedPipe( in, ioBuffer, IO_BUFFER_SIZE, &bytesInBuffer,
765 &bytesAvailable, NULL ) )
766 bytesAvailable = 0;
767
768 /* read in the available data */
769 if ( bytesAvailable > 0 )
770 {
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 ) )
774 {
775 if ( bytesInBuffer > 0 )
776 {
777 /* Clean up some illegal chars. */
778 int i;
779 for ( i = 0; i < bytesInBuffer; ++i )
780 {
781 if ( ( (unsigned char)ioBuffer[ i ] < 1 ) )
782 ioBuffer[ i ] = '?';
783 }
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;
790 }
791 else
792 {
793 /* Likely read a error, bail out. */
794 bytesAvailable = 0;
795 }
796 }
797 else
798 {
799 /* Definitely read a error, bail out. */
800 bytesAvailable = 0;
801 }
802 }
803 }
804 while ( bytesAvailable > 0 );
805 }
806
807
808 static void read_output()
809 {
810 int i;
811 for ( i = 0; i < globs.jobs; ++i )
812 if ( cmdtab[ i ].pi.hProcess )
813 {
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 );
822 }
823 }
824
825 static void CALLBACK try_wait_callback( void * data, BOOLEAN is_timeout )
826 {
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 );
834 }
835
836 static int try_wait_impl( DWORD timeout )
837 {
838 int job_index;
839 int timed_out;
840 int res = WaitForSingleObject( process_queue.read_okay, timeout );
841 if ( res != WAIT_OBJECT_0 )
842 return -1;
843 job_index = process_queue.job_index;
844 SetEvent( process_queue.write_okay );
845 return job_index;
846 }
847
848 static void register_wait( int job_id )
849 {
850 if ( globs.jobs > MAXIMUM_WAIT_OBJECTS )
851 {
852 HANDLE ignore;
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 );
857 }
858 }
859
860 /*
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.
864 */
865
866 static int try_wait( int const timeoutMillis )
867 {
868 if ( globs.jobs <= MAXIMUM_WAIT_OBJECTS )
869 {
870 int i;
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 )
876 {
877 if( cmdtab[ i ].pi.hProcess )
878 {
879 job_ids[ num_handles ] = i;
880 active_handles[ num_handles ] = cmdtab[ i ].pi.hProcess;
881 ++num_handles;
882 }
883 }
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 )
886 {
887 return job_ids[ wait_api_result - WAIT_OBJECT_0 ];
888 }
889 else
890 {
891 return -1;
892 }
893 }
894 else
895 {
896 return try_wait_impl( timeoutMillis );
897 }
898
899 }
900
901
902 static int try_kill_one()
903 {
904 /* Only need to check if a timeout was specified with the -l option. */
905 if ( globs.timeout > 0 )
906 {
907 int i;
908 for ( i = 0; i < globs.jobs; ++i )
909 if ( cmdtab[ i ].pi.hProcess )
910 {
911 double const t = running_time( cmdtab[ i ].pi.hProcess );
912 if ( t > (double)globs.timeout )
913 {
914 /* The job may have left an alert dialog around, try and get
915 * rid of it before killing the job itself.
916 */
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. */
922 return i;
923 }
924 }
925 }
926 return -1;
927 }
928
929
930 static void close_alerts()
931 {
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.
935 */
936 if ( ( (float)clock() / (float)( CLOCKS_PER_SEC * 5 ) ) < ( 1.0 / 5.0 ) )
937 {
938 int i;
939 for ( i = 0; i < globs.jobs; ++i )
940 if ( cmdtab[ i ].pi.hProcess )
941 close_alert( &cmdtab[ i ].pi );
942 }
943 }
944
945
946 /*
947 * Calc the current running time of an *active* process.
948 */
949
950 static double running_time( HANDLE const process )
951 {
952 FILETIME creation;
953 FILETIME exit;
954 FILETIME kernel;
955 FILETIME user;
956 if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) )
957 {
958 /* Compute the elapsed time. */
959 FILETIME current;
960 GetSystemTimeAsFileTime( &current );
961 return filetime_to_seconds( add_FILETIME( current,
962 negate_FILETIME( creation ) ) );
963 }
964 return 0.0;
965 }
966
967
968 /*
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.
971 */
972
973 static void kill_process_tree( DWORD const pid, HANDLE const process )
974 {
975 HANDLE const process_snapshot_h = CreateToolhelp32Snapshot(
976 TH32CS_SNAPPROCESS, 0 );
977 if ( INVALID_HANDLE_VALUE != process_snapshot_h )
978 {
979 BOOL ok = TRUE;
980 PROCESSENTRY32 pinfo;
981 pinfo.dwSize = sizeof( PROCESSENTRY32 );
982 for (
983 ok = Process32First( process_snapshot_h, &pinfo );
984 ok == TRUE;
985 ok = Process32Next( process_snapshot_h, &pinfo ) )
986 {
987 if ( pinfo.th32ParentProcessID == pid )
988 {
989 /* Found a child, recurse to kill it and anything else below it.
990 */
991 HANDLE const ph = OpenProcess( PROCESS_ALL_ACCESS, FALSE,
992 pinfo.th32ProcessID );
993 if ( ph )
994 {
995 kill_process_tree( pinfo.th32ProcessID, ph );
996 CloseHandle( ph );
997 }
998 }
999 }
1000 CloseHandle( process_snapshot_h );
1001 }
1002 /* Now that the children are all dead, kill the root. */
1003 TerminateProcess( process, -2 );
1004 }
1005
1006
1007 static double creation_time( HANDLE const process )
1008 {
1009 FILETIME creation;
1010 FILETIME exit;
1011 FILETIME kernel;
1012 FILETIME user;
1013 return GetProcessTimes( process, &creation, &exit, &kernel, &user )
1014 ? filetime_to_seconds( creation )
1015 : 0.0;
1016 }
1017
1018
1019 /*
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).
1024 */
1025
1026 static int is_parent_child( DWORD const parent, DWORD const child )
1027 {
1028 HANDLE process_snapshot_h = INVALID_HANDLE_VALUE;
1029
1030 if ( !child )
1031 return 0;
1032 if ( parent == child )
1033 return 1;
1034
1035 process_snapshot_h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
1036 if ( INVALID_HANDLE_VALUE != process_snapshot_h )
1037 {
1038 BOOL ok = TRUE;
1039 PROCESSENTRY32 pinfo;
1040 pinfo.dwSize = sizeof( PROCESSENTRY32 );
1041 for (
1042 ok = Process32First( process_snapshot_h, &pinfo );
1043 ok == TRUE;
1044 ok = Process32Next( process_snapshot_h, &pinfo ) )
1045 {
1046 if ( pinfo.th32ProcessID == child )
1047 {
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
1054 * another process.
1055 *
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.
1059 */
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 );
1065
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.
1080 */
1081 if ( !stricmp( pinfo.szExeFile, "csrss.exe" ) &&
1082 is_parent_child( parent, pinfo.th32ParentProcessID ) == 2 )
1083 return 1;
1084 if ( !stricmp( pinfo.szExeFile, "smss.exe" ) &&
1085 ( pinfo.th32ParentProcessID == 4 ) )
1086 return 2;
1087
1088 if ( hchild )
1089 {
1090 HANDLE hparent = OpenProcess( PROCESS_QUERY_INFORMATION,
1091 FALSE, pinfo.th32ParentProcessID );
1092 if ( hparent )
1093 {
1094 tchild = creation_time( hchild );
1095 tparent = creation_time( hparent );
1096 CloseHandle( hparent );
1097 }
1098 CloseHandle( hchild );
1099 }
1100
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
1104 */
1105 if ( ( tchild == 0.0 ) || ( tparent == 0.0 ) ||
1106 ( tchild < tparent ) )
1107 return 0;
1108
1109 return is_parent_child( parent, pinfo.th32ParentProcessID ) & 1;
1110 }
1111 }
1112
1113 CloseHandle( process_snapshot_h );
1114 }
1115
1116 return 0;
1117 }
1118
1119
1120 /*
1121 * Called by the OS for each topmost window.
1122 */
1123
1124 BOOL CALLBACK close_alert_window_enum( HWND hwnd, LPARAM lParam )
1125 {
1126 char buf[ 7 ] = { 0 };
1127 PROCESS_INFORMATION const * const pi = (PROCESS_INFORMATION *)lParam;
1128 DWORD pid;
1129 DWORD tid;
1130
1131 /* We want to find and close any window that:
1132 * 1. is visible and
1133 * 2. is a dialog and
1134 * 3. is displayed by any of our child processes
1135 */
1136 if (
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" ) )
1143 return TRUE;
1144
1145 /* GetWindowThreadProcessId() returns 0 on error, otherwise thread id of
1146 * the window's message pump thread.
1147 */
1148 tid = GetWindowThreadProcessId( hwnd, &pid );
1149 if ( !tid || !is_parent_child( pi->dwProcessId, pid ) )
1150 return TRUE;
1151
1152 /* Ask real nice. */
1153 PostMessageA( hwnd, WM_CLOSE, 0, 0 );
1154
1155 /* Wait and see if it worked. If not, insist. */
1156 if ( WaitForSingleObject( pi->hProcess, 200 ) == WAIT_TIMEOUT )
1157 {
1158 PostThreadMessageA( tid, WM_QUIT, 0, 0 );
1159 WaitForSingleObject( pi->hProcess, 300 );
1160 }
1161
1162 /* Done, we do not want to check any other windows now. */
1163 return FALSE;
1164 }
1165
1166
1167 static void close_alert( PROCESS_INFORMATION const * const pi )
1168 {
1169 EnumWindows( &close_alert_window_enum, (LPARAM)pi );
1170 }
1171
1172
1173 /*
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.
1178 *
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
1181 * function.
1182 */
1183
1184 static FILE * open_command_file( int const slot )
1185 {
1186 string * const command_file = cmdtab[ slot ].command_file;
1187
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
1191 * end.
1192 */
1193 if ( !command_file->value )
1194 {
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 );
1201 }
1202
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.
1207 *
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
1211 * file names.
1212 */
1213 {
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 )
1221 {
1222 int index;
1223 for ( index = 0; index != 20; ++index )
1224 {
1225 FILE * f;
1226 *index1 = '0' + index / 10;
1227 *index2 = '0' + index % 10;
1228 f = fopen( command_file->value, "w" );
1229 if ( f ) return f;
1230 }
1231 if ( !waits_remaining ) break;
1232 Sleep( 250 );
1233 }
1234 }
1235
1236 return 0;
1237 }
1238
1239
1240 /*
1241 * Prepare a command file to be executed using an external shell.
1242 */
1243
1244 static char const * prepare_command_file( string const * command, int slot )
1245 {
1246 FILE * const f = open_command_file( slot );
1247 if ( !f )
1248 {
1249 err_printf( "failed to write command file!\n" );
1250 exit( EXITBAD );
1251 }
1252 fputs( command->value, f );
1253 fclose( f );
1254 return cmdtab[ slot ].command_file->value;
1255 }
1256
1257
1258 /*
1259 * Find a free slot in the running commands table.
1260 */
1261
1262 static int get_free_cmdtab_slot()
1263 {
1264 int slot;
1265 for ( slot = 0; slot < globs.jobs; ++slot )
1266 if ( !cmdtab[ slot ].pi.hProcess )
1267 return slot;
1268 err_printf( "no slots for child!\n" );
1269 exit( EXITBAD );
1270 }
1271
1272
1273 /*
1274 * Put together the final command string we are to run.
1275 */
1276
1277 static void string_new_from_argv( string * result, char const * const * argv )
1278 {
1279 assert( argv );
1280 assert( argv[ 0 ] );
1281 string_copy( result, *(argv++) );
1282 while ( *argv )
1283 {
1284 string_push_back( result, ' ' );
1285 string_append( result, *(argv++) );
1286 }
1287 }
1288
1289
1290 /*
1291 * Reports the last failed Windows API related error message.
1292 */
1293
1294 static void reportWindowsError( char const * const apiName, int slot )
1295 {
1296 char * errorMessage;
1297 char buf[24];
1298 string * err_buf;
1299 timing_info time;
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 */
1311
1312 /* Build a message as if the process had written to stderr. */
1313 if ( globs.pipe_action )
1314 err_buf = cmdtab[ slot ].buffer_err;
1315 else
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 );
1321
1322 if ( !apiResult )
1323 string_append( err_buf, ".\n" );
1324 else
1325 {
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 );
1332 }
1333
1334 /* Since the process didn't actually start, use a blank timing_info. */
1335 time.system = 0;
1336 time.user = 0;
1337 timestamp_current( &time.start );
1338 timestamp_current( &time.end );
1339
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,
1343 EXIT_OK );
1344
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 );
1354 }
1355
1356
1357 #endif /* USE_EXECNT */