]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/build/src/engine/execnt.c
add subtree-ish sources for 12.0.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
105 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
106
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.
110 */
111 #define MAX_RAW_COMMAND_LENGTH 32766
112
113 /* We hold handles for pipes used to communicate with child processes in two
114 * element arrays indexed as follows.
115 */
116 #define EXECCMD_PIPE_READ 0
117 #define EXECCMD_PIPE_WRITE 1
118
119 static int intr_installed;
120
121
122 /* The list of commands we run. */
123 static struct
124 {
125 /* Temporary command file used to execute the action when needed. */
126 string command_file[ 1 ];
127
128 /* Pipes for communicating with the child process. Parent reads from (0),
129 * child writes to (1).
130 */
131 HANDLE pipe_out[ 2 ];
132 HANDLE pipe_err[ 2 ];
133
134 string buffer_out[ 1 ]; /* buffer to hold stdout, if any */
135 string buffer_err[ 1 ]; /* buffer to hold stderr, if any */
136
137 PROCESS_INFORMATION pi; /* running process information */
138
139 /* Function called when the command completes. */
140 ExecCmdCallback func;
141
142 /* Opaque data passed back to the 'func' callback. */
143 void * closure;
144 }
145 cmdtab[ MAXJOBS ] = { { 0 } };
146
147
148 /*
149 * Execution unit tests.
150 */
151
152 void execnt_unit_test()
153 {
154 #if !defined( NDEBUG )
155 /* vc6 preprocessor is broken, so assert with these strings gets confused.
156 * Use a table instead.
157 */
158 {
159 typedef struct test { char * command; int result; } test;
160 test tests[] = {
161 { "", 0 },
162 { " ", 0 },
163 { "x", 1 },
164 { "\nx", 1 },
165 { "x\n", 1 },
166 { "\nx\n", 1 },
167 { "\nx \n", 2 },
168 { "\nx \n ", 2 },
169 { " \n\t\t\v\r\r\n \t x \v \t\t\r\n\n\n \n\n\v\t", 8 },
170 { "x\ny", -1 },
171 { "x\n\n y", -1 },
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 },
185 { 0 } };
186 test const * t;
187 for ( t = tests; t->command; ++t )
188 assert( raw_command_length( t->command ) == t->result );
189 }
190
191 {
192 int const length = maxline() + 9;
193 char * const cmd = (char *)BJAM_MALLOC_ATOMIC( length + 1 );
194 memset( cmd, 'x', length );
195 cmd[ length ] = 0;
196 assert( raw_command_length( cmd ) == length );
197 BJAM_FREE( cmd );
198 }
199 #endif
200 }
201
202
203 /*
204 * exec_check() - preprocess and validate the command
205 */
206
207 int exec_check
208 (
209 string const * command,
210 LIST * * pShell,
211 int * error_length,
212 int * error_max_length
213 )
214 {
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.
219 */
220 if ( list_empty( *pShell ) )
221 {
222 char const * s = command->value;
223 while ( isspace( *s ) ) ++s;
224 if ( !*s )
225 return EXEC_CHECK_NOOP;
226 }
227
228 /* Check prerequisites for executing raw commands. */
229 if ( is_raw_command_request( *pShell ) )
230 {
231 int const raw_cmd_length = raw_command_length( command->value );
232 if ( raw_cmd_length < 0 )
233 {
234 /* Invalid characters detected - fallback to default shell. */
235 list_free( *pShell );
236 *pShell = L0;
237 }
238 else if ( raw_cmd_length > MAX_RAW_COMMAND_LENGTH )
239 {
240 *error_length = raw_cmd_length;
241 *error_max_length = MAX_RAW_COMMAND_LENGTH;
242 return EXEC_CHECK_TOO_LONG;
243 }
244 else
245 return raw_cmd_length ? EXEC_CHECK_OK : EXEC_CHECK_NOOP;
246 }
247
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.
256 */
257
258 /* Check for too long command lines. */
259 return check_cmd_for_too_long_lines( command->value, maxline(),
260 error_length, error_max_length );
261 }
262
263
264 /*
265 * exec_cmd() - launch an async command execution
266 *
267 * We assume exec_check() already verified that the given command can have its
268 * command string constructed as requested.
269 */
270
271 void exec_cmd
272 (
273 string const * cmd_orig,
274 ExecCmdCallback func,
275 void * closure,
276 LIST * shell
277 )
278 {
279 int const slot = get_free_cmdtab_slot();
280 int const is_raw_cmd = is_raw_command_request( shell );
281 string cmd_local[ 1 ];
282
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" ) );
287
288 /* Specifying no shell means requesting the default shell. */
289 if ( list_empty( shell ) )
290 shell = default_shell;
291
292 if ( DEBUG_EXECCMD )
293 if ( is_raw_cmd )
294 out_printf( "Executing raw command directly\n" );
295 else
296 {
297 out_printf( "Executing using a command file and the shell: " );
298 list_print( shell );
299 out_printf( "\n" );
300 }
301
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
305 * whitespace).
306 */
307 if ( is_raw_cmd )
308 {
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 ] ) )
314 if ( *--p == '\n' )
315 end = p;
316 string_new( cmd_local );
317 string_append_range( cmd_local, start, end );
318 assert( cmd_local->size == raw_command_length( cmd_orig->value ) );
319 }
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
322 * that command file.
323 */
324 else
325 {
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 );
330 }
331
332 /* Catch interrupts whenever commands are running. */
333 if ( !intr_installed )
334 {
335 intr_installed = 1;
336 signal( SIGINT, onintr );
337 }
338
339 /* Save input data into the selected running commands table slot. */
340 cmdtab[ slot ].func = func;
341 cmdtab[ slot ].closure = closure;
342
343 /* Invoke the actual external process using the constructed command line. */
344 invoke_cmd( cmd_local->value, slot );
345
346 /* Free our local command string copy. */
347 string_free( cmd_local );
348 }
349
350
351 /*
352 * exec_wait() - wait for any of the async command processes to terminate
353 *
354 * Wait and drive at most one execution completion, while processing the I/O for
355 * all ongoing commands.
356 */
357
358 void exec_wait()
359 {
360 int i = -1;
361 int exit_reason; /* reason why a command completed */
362
363 /* Wait for a command to complete, while snarfing up any output. */
364 while ( 1 )
365 {
366 /* Check for a complete command, briefly. */
367 i = try_wait( 500 );
368 /* Read in the output of all running commands. */
369 read_output();
370 /* Close out pending debug style dialogs. */
371 close_alerts();
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. */
375 i = try_kill_one();
376 if ( i >= 0 ) { exit_reason = EXIT_TIMEOUT; break; }
377 }
378
379 /* We have a command... process it. */
380 {
381 DWORD exit_code;
382 timing_info time;
383 int rstat;
384
385 /* The time data for the command. */
386 record_times( cmdtab[ i ].pi.hProcess, &time );
387
388 /* Removed the used temporary command file. */
389 if ( cmdtab[ i ].command_file->size )
390 unlink( cmdtab[ i ].command_file->value );
391
392 /* Find out the process exit code. */
393 GetExitCodeProcess( cmdtab[ i ].pi.hProcess, &exit_code );
394
395 /* The dispossition of the command. */
396 if ( interrupted() )
397 rstat = EXEC_CMD_INTR;
398 else if ( exit_code )
399 rstat = EXEC_CMD_FAIL;
400 else
401 rstat = EXEC_CMD_OK;
402
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,
406 exit_reason );
407
408 /* Clean up our child process tracking data. No need to clear the
409 * temporary command file name as it gets reused.
410 */
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 );
419 }
420 }
421
422
423 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
424
425 /*
426 * Invoke the actual external process using the given command line. Track the
427 * process in our running commands table.
428 */
429
430 static void invoke_cmd( char const * const command, int const slot )
431 {
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,
435 0, 0, 0, 0, 0, 0 };
436
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;
442
443 /* Create output buffers. */
444 string_new( cmdtab[ slot ].buffer_out );
445 string_new( cmdtab[ slot ].buffer_err );
446
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 ) )
450 {
451 reportWindowsError( "CreatePipe", slot );
452 return;
453 }
454 if ( globs.pipe_action && !CreatePipe( &cmdtab[ slot ].pipe_err[
455 EXECCMD_PIPE_READ ], &cmdtab[ slot ].pipe_err[ EXECCMD_PIPE_WRITE ],
456 &sa, 0 ) )
457 {
458 reportWindowsError( "CreatePipe", slot );
459 return;
460 }
461
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 );
468
469 /* Hide the child window, if any. */
470 si.dwFlags |= STARTF_USESHOWWINDOW;
471 si.wShowWindow = SW_HIDE;
472
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 ];
479
480 /* Let the child inherit stdin, as some commands assume it is available. */
481 si.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
482
483 if ( DEBUG_EXECCMD )
484 out_printf( "Command string for CreateProcessA(): '%s'\n", command );
485
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 */
498 {
499 reportWindowsError( "CreateProcessA", slot );
500 return;
501 }
502 }
503
504
505 /*
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
509 */
510
511 static int raw_maxline()
512 {
513 OSVERSIONINFO os_info;
514 os_info.dwOSVersionInfoSize = sizeof( os_info );
515 GetVersionEx( &os_info );
516
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 */
520 }
521
522 static int maxline()
523 {
524 static result;
525 if ( !result ) result = raw_maxline();
526 return result;
527 }
528
529
530 /*
531 * Closes a Windows HANDLE and resets its variable to 0.
532 */
533
534 static void closeWinHandle( HANDLE * const handle )
535 {
536 if ( *handle )
537 {
538 CloseHandle( *handle );
539 *handle = 0;
540 }
541 }
542
543
544 /*
545 * Frees and renews the given string.
546 */
547
548 static void string_renew( string * const s )
549 {
550 string_free( s );
551 string_new( s );
552 }
553
554
555 /*
556 * raw_command_length() - valid raw command string length
557 *
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.
560 *
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).
568 */
569
570 static long raw_command_length( char const * command )
571 {
572 char const * p;
573 char const * escape = 0;
574 char inquote = 0;
575 char const * newline = 0;
576
577 /* Skip leading whitespace. */
578 while ( isspace( *command ) )
579 ++command;
580
581 p = command;
582
583 /* Look for newlines and unquoted I/O redirection. */
584 do
585 {
586 p += strcspn( p, "\n\"'<>|\\" );
587 switch ( *p )
588 {
589 case '\n':
590 /* If our command contains non-whitespace content split over
591 * multiple lines we can not execute it directly.
592 */
593 newline = p;
594 while ( isspace( *++p ) );
595 if ( *p ) return -1;
596 break;
597
598 case '\\':
599 escape = escape && escape == p - 1 ? 0 : p;
600 ++p;
601 break;
602
603 case '"':
604 case '\'':
605 if ( escape && escape == p - 1 )
606 escape = 0;
607 else if ( inquote == *p )
608 inquote = 0;
609 else if ( !inquote )
610 inquote = *p;
611 ++p;
612 break;
613
614 case '<':
615 case '>':
616 case '|':
617 if ( !inquote )
618 return -1;
619 ++p;
620 break;
621 }
622 }
623 while ( *p );
624
625 /* Return the number of characters the command will occupy. */
626 return ( newline ? newline : p ) - command;
627 }
628
629
630 /* 64-bit arithmetic helpers. */
631
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)
634
635 /* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1
636 * and h2l2.
637 */
638 #define add_64_hi( h1, l1, h2, l2 ) ((h1) + (h2) + add_carry_bit(l1, l2))
639
640
641 /*
642 * Add two 64-bit unsigned numbers, h1l1 and h2l2.
643 */
644
645 static FILETIME add_64
646 (
647 unsigned long h1, unsigned long l1,
648 unsigned long h2, unsigned long l2
649 )
650 {
651 FILETIME result;
652 result.dwLowDateTime = l1 + l2;
653 result.dwHighDateTime = add_64_hi( h1, l1, h2, l2 );
654 return result;
655 }
656
657
658 static FILETIME add_FILETIME( FILETIME t1, FILETIME t2 )
659 {
660 return add_64( t1.dwHighDateTime, t1.dwLowDateTime, t2.dwHighDateTime,
661 t2.dwLowDateTime );
662 }
663
664
665 static FILETIME negate_FILETIME( FILETIME t )
666 {
667 /* 2s complement negation */
668 return add_64( ~t.dwHighDateTime, ~t.dwLowDateTime, 0, 1 );
669 }
670
671
672 /*
673 * filetime_to_seconds() - Windows FILETIME --> number of seconds conversion
674 */
675
676 static double filetime_to_seconds( FILETIME const ft )
677 {
678 return ft.dwHighDateTime * ( (double)( 1UL << 31 ) * 2.0 * 1.0e-7 ) +
679 ft.dwLowDateTime * 1.0e-7;
680 }
681
682
683 static void record_times( HANDLE const process, timing_info * const time )
684 {
685 FILETIME creation;
686 FILETIME exit;
687 FILETIME kernel;
688 FILETIME user;
689 if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) )
690 {
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 );
695 }
696 }
697
698
699 #define IO_BUFFER_SIZE ( 16 * 1024 )
700
701 static char ioBuffer[ IO_BUFFER_SIZE + 1 ];
702
703
704 static void read_pipe
705 (
706 HANDLE in, /* the pipe to read from */
707 string * out
708 )
709 {
710 DWORD bytesInBuffer = 0;
711 DWORD bytesAvailable = 0;
712
713 do
714 {
715 /* check if we have any data to read */
716 if ( !PeekNamedPipe( in, ioBuffer, IO_BUFFER_SIZE, &bytesInBuffer,
717 &bytesAvailable, NULL ) )
718 bytesAvailable = 0;
719
720 /* read in the available data */
721 if ( bytesAvailable > 0 )
722 {
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 ) )
726 {
727 if ( bytesInBuffer > 0 )
728 {
729 /* Clean up some illegal chars. */
730 int i;
731 for ( i = 0; i < bytesInBuffer; ++i )
732 {
733 if ( ( (unsigned char)ioBuffer[ i ] < 1 ) )
734 ioBuffer[ i ] = '?';
735 }
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;
742 }
743 else
744 {
745 /* Likely read a error, bail out. */
746 bytesAvailable = 0;
747 }
748 }
749 else
750 {
751 /* Definitely read a error, bail out. */
752 bytesAvailable = 0;
753 }
754 }
755 }
756 while ( bytesAvailable > 0 );
757 }
758
759
760 static void read_output()
761 {
762 int i;
763 for ( i = 0; i < globs.jobs; ++i )
764 if ( cmdtab[ i ].pi.hProcess )
765 {
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 );
774 }
775 }
776
777
778 /*
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.
782 */
783
784 typedef struct _twh_params
785 {
786 int * active_procs;
787 HANDLE * active_handles;
788 DWORD num_active;
789 DWORD timeoutMillis;
790 } twh_params;
791
792 static int try_wait_helper( twh_params * );
793
794 static int try_wait( int const timeoutMillis )
795 {
796 #define MAX_THREADS MAXJOBS/(MAXIMUM_WAIT_OBJECTS - 1) + 1
797 int i;
798 int num_active;
799 int wait_api_result;
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];
809 int result = -1;
810 BOOL success;
811
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 )
815 {
816 if ( num_active == MAXIMUM_WAIT_OBJECTS )
817 {
818 /*
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.
823 */
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;
830 ++num_active;
831 }
832 else if ( ( completed_event != INVALID_HANDLE_VALUE ) &&
833 !((num_active + 1) % MAXIMUM_WAIT_OBJECTS) )
834 {
835 active_handles[ num_active ] = completed_event;
836 active_procs[ num_active ] = -1;
837 ++num_active;
838 }
839 active_handles[ num_active ] = cmdtab[ i ].pi.hProcess;
840 active_procs[ num_active ] = i;
841 ++num_active;
842 }
843
844 assert( (num_active <= MAXIMUM_WAIT_OBJECTS) ==
845 (completed_event == INVALID_HANDLE_VALUE) );
846 if ( num_active <= MAXIMUM_WAIT_OBJECTS )
847 {
848 twh_params twh;
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 );
854 }
855
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 )
860 {
861 /* Can we fit the last chunk in the outer WFMO call? */
862 if ( last_chunk_size <= MAXIMUM_WAIT_OBJECTS - num_threads )
863 {
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;
869 }
870 else
871 {
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;
876 ++last_chunk_size;
877 ++num_active;
878 ++num_threads;
879 num_handles = num_threads;
880 }
881 }
882
883 assert( num_threads <= MAX_THREADS );
884
885 for ( i = 0; i < num_threads; ++i )
886 {
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],
898 0, NULL);
899 }
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 ) )
904 {
905 HANDLE thread_handle = thread_handles[wait_api_result - WAIT_OBJECT_0];
906 success = GetExitCodeThread(thread_handle, (DWORD *)&result);
907 assert( success );
908 }
909 else if ( ( WAIT_OBJECT_0 + num_threads <= wait_api_result ) &&
910 ( wait_api_result < WAIT_OBJECT_0 + num_handles ) )
911 {
912 unsigned int offset = wait_api_result - num_threads - WAIT_OBJECT_0;
913 result = active_procs[ last_chunk_offset + offset ];
914 }
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]);
921 return result;
922 #undef MAX_THREADS
923 }
924
925 static int try_wait_helper( twh_params * params )
926 {
927 int wait_api_result;
928
929 assert( params->num_active <= MAXIMUM_WAIT_OBJECTS );
930
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 ) )
936 {
937 /* Terminated process detected - return its index. */
938 return params->active_procs[ wait_api_result - WAIT_OBJECT_0 ];
939 }
940
941 /* Timeout. */
942 return -1;
943 }
944
945
946 static int try_kill_one()
947 {
948 /* Only need to check if a timeout was specified with the -l option. */
949 if ( globs.timeout > 0 )
950 {
951 int i;
952 for ( i = 0; i < globs.jobs; ++i )
953 if ( cmdtab[ i ].pi.hProcess )
954 {
955 double const t = running_time( cmdtab[ i ].pi.hProcess );
956 if ( t > (double)globs.timeout )
957 {
958 /* The job may have left an alert dialog around, try and get
959 * rid of it before killing the job itself.
960 */
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. */
966 return i;
967 }
968 }
969 }
970 return -1;
971 }
972
973
974 static void close_alerts()
975 {
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.
979 */
980 if ( ( (float)clock() / (float)( CLOCKS_PER_SEC * 5 ) ) < ( 1.0 / 5.0 ) )
981 {
982 int i;
983 for ( i = 0; i < globs.jobs; ++i )
984 if ( cmdtab[ i ].pi.hProcess )
985 close_alert( &cmdtab[ i ].pi );
986 }
987 }
988
989
990 /*
991 * Calc the current running time of an *active* process.
992 */
993
994 static double running_time( HANDLE const process )
995 {
996 FILETIME creation;
997 FILETIME exit;
998 FILETIME kernel;
999 FILETIME user;
1000 if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) )
1001 {
1002 /* Compute the elapsed time. */
1003 FILETIME current;
1004 GetSystemTimeAsFileTime( &current );
1005 return filetime_to_seconds( add_FILETIME( current,
1006 negate_FILETIME( creation ) ) );
1007 }
1008 return 0.0;
1009 }
1010
1011
1012 /*
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.
1015 */
1016
1017 static void kill_process_tree( DWORD const pid, HANDLE const process )
1018 {
1019 HANDLE const process_snapshot_h = CreateToolhelp32Snapshot(
1020 TH32CS_SNAPPROCESS, 0 );
1021 if ( INVALID_HANDLE_VALUE != process_snapshot_h )
1022 {
1023 BOOL ok = TRUE;
1024 PROCESSENTRY32 pinfo;
1025 pinfo.dwSize = sizeof( PROCESSENTRY32 );
1026 for (
1027 ok = Process32First( process_snapshot_h, &pinfo );
1028 ok == TRUE;
1029 ok = Process32Next( process_snapshot_h, &pinfo ) )
1030 {
1031 if ( pinfo.th32ParentProcessID == pid )
1032 {
1033 /* Found a child, recurse to kill it and anything else below it.
1034 */
1035 HANDLE const ph = OpenProcess( PROCESS_ALL_ACCESS, FALSE,
1036 pinfo.th32ProcessID );
1037 if ( ph )
1038 {
1039 kill_process_tree( pinfo.th32ProcessID, ph );
1040 CloseHandle( ph );
1041 }
1042 }
1043 }
1044 CloseHandle( process_snapshot_h );
1045 }
1046 /* Now that the children are all dead, kill the root. */
1047 TerminateProcess( process, -2 );
1048 }
1049
1050
1051 static double creation_time( HANDLE const process )
1052 {
1053 FILETIME creation;
1054 FILETIME exit;
1055 FILETIME kernel;
1056 FILETIME user;
1057 return GetProcessTimes( process, &creation, &exit, &kernel, &user )
1058 ? filetime_to_seconds( creation )
1059 : 0.0;
1060 }
1061
1062
1063 /*
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).
1068 */
1069
1070 static int is_parent_child( DWORD const parent, DWORD const child )
1071 {
1072 HANDLE process_snapshot_h = INVALID_HANDLE_VALUE;
1073
1074 if ( !child )
1075 return 0;
1076 if ( parent == child )
1077 return 1;
1078
1079 process_snapshot_h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
1080 if ( INVALID_HANDLE_VALUE != process_snapshot_h )
1081 {
1082 BOOL ok = TRUE;
1083 PROCESSENTRY32 pinfo;
1084 pinfo.dwSize = sizeof( PROCESSENTRY32 );
1085 for (
1086 ok = Process32First( process_snapshot_h, &pinfo );
1087 ok == TRUE;
1088 ok = Process32Next( process_snapshot_h, &pinfo ) )
1089 {
1090 if ( pinfo.th32ProcessID == child )
1091 {
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
1098 * another process.
1099 *
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.
1103 */
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 );
1109
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.
1124 */
1125 if ( !stricmp( pinfo.szExeFile, "csrss.exe" ) &&
1126 is_parent_child( parent, pinfo.th32ParentProcessID ) == 2 )
1127 return 1;
1128 if ( !stricmp( pinfo.szExeFile, "smss.exe" ) &&
1129 ( pinfo.th32ParentProcessID == 4 ) )
1130 return 2;
1131
1132 if ( hchild )
1133 {
1134 HANDLE hparent = OpenProcess( PROCESS_QUERY_INFORMATION,
1135 FALSE, pinfo.th32ParentProcessID );
1136 if ( hparent )
1137 {
1138 tchild = creation_time( hchild );
1139 tparent = creation_time( hparent );
1140 CloseHandle( hparent );
1141 }
1142 CloseHandle( hchild );
1143 }
1144
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
1148 */
1149 if ( ( tchild == 0.0 ) || ( tparent == 0.0 ) ||
1150 ( tchild < tparent ) )
1151 return 0;
1152
1153 return is_parent_child( parent, pinfo.th32ParentProcessID ) & 1;
1154 }
1155 }
1156
1157 CloseHandle( process_snapshot_h );
1158 }
1159
1160 return 0;
1161 }
1162
1163
1164 /*
1165 * Called by the OS for each topmost window.
1166 */
1167
1168 BOOL CALLBACK close_alert_window_enum( HWND hwnd, LPARAM lParam )
1169 {
1170 char buf[ 7 ] = { 0 };
1171 PROCESS_INFORMATION const * const pi = (PROCESS_INFORMATION *)lParam;
1172 DWORD pid;
1173 DWORD tid;
1174
1175 /* We want to find and close any window that:
1176 * 1. is visible and
1177 * 2. is a dialog and
1178 * 3. is displayed by any of our child processes
1179 */
1180 if (
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" ) )
1187 return TRUE;
1188
1189 /* GetWindowThreadProcessId() returns 0 on error, otherwise thread id of
1190 * the window's message pump thread.
1191 */
1192 tid = GetWindowThreadProcessId( hwnd, &pid );
1193 if ( !tid || !is_parent_child( pi->dwProcessId, pid ) )
1194 return TRUE;
1195
1196 /* Ask real nice. */
1197 PostMessageA( hwnd, WM_CLOSE, 0, 0 );
1198
1199 /* Wait and see if it worked. If not, insist. */
1200 if ( WaitForSingleObject( pi->hProcess, 200 ) == WAIT_TIMEOUT )
1201 {
1202 PostThreadMessageA( tid, WM_QUIT, 0, 0 );
1203 WaitForSingleObject( pi->hProcess, 300 );
1204 }
1205
1206 /* Done, we do not want to check any other windows now. */
1207 return FALSE;
1208 }
1209
1210
1211 static void close_alert( PROCESS_INFORMATION const * const pi )
1212 {
1213 EnumWindows( &close_alert_window_enum, (LPARAM)pi );
1214 }
1215
1216
1217 /*
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.
1222 *
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
1225 * function.
1226 */
1227
1228 static FILE * open_command_file( int const slot )
1229 {
1230 string * const command_file = cmdtab[ slot ].command_file;
1231
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
1235 * end.
1236 */
1237 if ( !command_file->value )
1238 {
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 );
1245 }
1246
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.
1251 *
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
1255 * file names.
1256 */
1257 {
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 )
1265 {
1266 int index;
1267 for ( index = 0; index != 20; ++index )
1268 {
1269 FILE * f;
1270 *index1 = '0' + index / 10;
1271 *index2 = '0' + index % 10;
1272 f = fopen( command_file->value, "w" );
1273 if ( f ) return f;
1274 }
1275 if ( !waits_remaining ) break;
1276 Sleep( 250 );
1277 }
1278 }
1279
1280 return 0;
1281 }
1282
1283
1284 /*
1285 * Prepare a command file to be executed using an external shell.
1286 */
1287
1288 static char const * prepare_command_file( string const * command, int slot )
1289 {
1290 FILE * const f = open_command_file( slot );
1291 if ( !f )
1292 {
1293 err_printf( "failed to write command file!\n" );
1294 exit( EXITBAD );
1295 }
1296 fputs( command->value, f );
1297 fclose( f );
1298 return cmdtab[ slot ].command_file->value;
1299 }
1300
1301
1302 /*
1303 * Find a free slot in the running commands table.
1304 */
1305
1306 static int get_free_cmdtab_slot()
1307 {
1308 int slot;
1309 for ( slot = 0; slot < MAXJOBS; ++slot )
1310 if ( !cmdtab[ slot ].pi.hProcess )
1311 return slot;
1312 err_printf( "no slots for child!\n" );
1313 exit( EXITBAD );
1314 }
1315
1316
1317 /*
1318 * Put together the final command string we are to run.
1319 */
1320
1321 static void string_new_from_argv( string * result, char const * const * argv )
1322 {
1323 assert( argv );
1324 assert( argv[ 0 ] );
1325 string_copy( result, *(argv++) );
1326 while ( *argv )
1327 {
1328 string_push_back( result, ' ' );
1329 string_append( result, *(argv++) );
1330 }
1331 }
1332
1333
1334 /*
1335 * Reports the last failed Windows API related error message.
1336 */
1337
1338 static void reportWindowsError( char const * const apiName, int slot )
1339 {
1340 char * errorMessage;
1341 char buf[24];
1342 string * err_buf;
1343 timing_info time;
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 */
1355
1356 /* Build a message as if the process had written to stderr. */
1357 if ( globs.pipe_action )
1358 err_buf = cmdtab[ slot ].buffer_err;
1359 else
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 );
1365
1366 if ( !apiResult )
1367 string_append( err_buf, ".\n" );
1368 else
1369 {
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 );
1376 }
1377
1378 /* Since the process didn't actually start, use a blank timing_info. */
1379 time.system = 0;
1380 time.user = 0;
1381 timestamp_current( &time.start );
1382 timestamp_current( &time.end );
1383
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,
1387 EXIT_OK );
1388
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 );
1398 }
1399
1400
1401 #endif /* USE_EXECNT */