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  /* Communication buffers size */
116 #define IO_BUFFER_SIZE ( 64 * 1024 )
117 
118 /* We hold handles for pipes used to communicate with child processes in two
119  * element arrays indexed as follows.
120  */
121 #define EXECCMD_PIPE_READ 0
122 #define EXECCMD_PIPE_WRITE 1
123 
124 static int intr_installed;
125 
126 
127 /* The list of commands we run. */
128 static struct _cmdtab_t
129 {
130     /* Temporary command file used to execute the action when needed. */
131     string command_file[ 1 ];
132 
133     /* Pipes for communicating with the child process. Parent reads from (0),
134      * child writes to (1).
135      */
136     HANDLE pipe_out[ 2 ];
137     HANDLE pipe_err[ 2 ];
138 
139     string buffer_out[ 1 ];  /* buffer to hold stdout, if any */
140     string buffer_err[ 1 ];  /* buffer to hold stderr, if any */
141 
142     PROCESS_INFORMATION pi;  /* running process information */
143 
144     HANDLE wait_handle;
145 
146     int flags;
147 
148     /* Function called when the command completes. */
149     ExecCmdCallback func;
150 
151     /* Opaque data passed back to the 'func' callback. */
152     void * closure;
153 } * cmdtab = NULL;
154 static int cmdtab_size = 0;
155 
156 /* A thread-safe single element queue.  Used by the worker threads
157  * to signal the main thread that a process is completed.
158  */
159 struct
160 {
161     int job_index;
162     HANDLE read_okay;
163     HANDLE write_okay;
164 } process_queue;
165 
166 /*
167  * Execution unit tests.
168  */
169 
execnt_unit_test()170 void execnt_unit_test()
171 {
172 #if !defined( NDEBUG )
173     /* vc6 preprocessor is broken, so assert with these strings gets confused.
174      * Use a table instead.
175      */
176     {
177         typedef struct test { char * command; int result; } test;
178         test tests[] = {
179             { "", 0 },
180             { "  ", 0 },
181             { "x", 1 },
182             { "\nx", 1 },
183             { "x\n", 1 },
184             { "\nx\n", 1 },
185             { "\nx \n", 2 },
186             { "\nx \n ", 2 },
187             { " \n\t\t\v\r\r\n \t  x  \v \t\t\r\n\n\n   \n\n\v\t", 8 },
188             { "x\ny", -1 },
189             { "x\n\n y", -1 },
190             { "echo x > foo.bar", -1 },
191             { "echo x < foo.bar", -1 },
192             { "echo x | foo.bar", -1 },
193             { "echo x \">\" foo.bar", 18 },
194             { "echo x '<' foo.bar", 18 },
195             { "echo x \"|\" foo.bar", 18 },
196             { "echo x \\\">\\\" foo.bar", -1 },
197             { "echo x \\\"<\\\" foo.bar", -1 },
198             { "echo x \\\"|\\\" foo.bar", -1 },
199             { "\"echo x > foo.bar\"", 18 },
200             { "echo x \"'\"<' foo.bar", -1 },
201             { "echo x \\\\\"<\\\\\" foo.bar", 22 },
202             { "echo x \\x\\\"<\\\\\" foo.bar", -1 },
203             { 0 } };
204         test const * t;
205         for ( t = tests; t->command; ++t )
206             assert( raw_command_length( t->command ) == t->result );
207     }
208 
209     {
210         int const length = maxline() + 9;
211         char * const cmd = (char *)BJAM_MALLOC_ATOMIC( length + 1 );
212         memset( cmd, 'x', length );
213         cmd[ length ] = 0;
214         assert( raw_command_length( cmd ) == length );
215         BJAM_FREE( cmd );
216     }
217 #endif
218 }
219 
220 /*
221  * exec_init() - global initialization
222  */
exec_init(void)223 void exec_init( void )
224 {
225     if ( globs.jobs > cmdtab_size )
226     {
227         cmdtab = (_cmdtab_t*)BJAM_REALLOC( cmdtab, globs.jobs * sizeof( *cmdtab ) );
228         memset( cmdtab + cmdtab_size, 0, ( globs.jobs - cmdtab_size ) * sizeof( *cmdtab ) );
229         cmdtab_size = globs.jobs;
230     }
231     if ( globs.jobs > MAXIMUM_WAIT_OBJECTS && !process_queue.read_okay )
232     {
233         process_queue.read_okay = CreateEvent( NULL, FALSE, FALSE, NULL );
234         process_queue.write_okay = CreateEvent( NULL, FALSE, TRUE, NULL );
235     }
236 }
237 
238 /*
239  * exec_done - free resources.
240  */
exec_done(void)241 void exec_done( void )
242 {
243     if ( process_queue.read_okay )
244     {
245         CloseHandle( process_queue.read_okay );
246     }
247     if ( process_queue.write_okay )
248     {
249         CloseHandle( process_queue.write_okay );
250     }
251     BJAM_FREE( cmdtab );
252 }
253 
254 /*
255  * exec_check() - preprocess and validate the command
256  */
257 
exec_check(string const * command,LIST ** pShell,int * error_length,int * error_max_length)258 int exec_check
259 (
260     string const * command,
261     LIST * * pShell,
262     int * error_length,
263     int * error_max_length
264 )
265 {
266     /* Default shell does nothing when triggered with an empty or a
267      * whitespace-only command so we simply skip running it in that case. We
268      * still pass them on to non-default shells as we do not really know what
269      * they are going to do with such commands.
270      */
271     if ( list_empty( *pShell ) )
272     {
273         char const * s = command->value;
274         while ( isspace( *s ) ) ++s;
275         if ( !*s )
276             return EXEC_CHECK_NOOP;
277     }
278 
279     /* Check prerequisites for executing raw commands. */
280     if ( is_raw_command_request( *pShell ) )
281     {
282         int const raw_cmd_length = raw_command_length( command->value );
283         if ( raw_cmd_length < 0 )
284         {
285             /* Invalid characters detected - fallback to default shell. */
286             list_free( *pShell );
287             *pShell = L0;
288         }
289         else if ( raw_cmd_length > MAX_RAW_COMMAND_LENGTH )
290         {
291             *error_length = raw_cmd_length;
292             *error_max_length = MAX_RAW_COMMAND_LENGTH;
293             return EXEC_CHECK_TOO_LONG;
294         }
295         else
296             return raw_cmd_length ? EXEC_CHECK_OK : EXEC_CHECK_NOOP;
297     }
298 
299     /* Now we know we are using an external shell. Note that there is no need to
300      * check for too long command strings when using an external shell since we
301      * use a command file and assume no one is going to set up a JAMSHELL format
302      * string longer than a few hundred bytes at most which should be well under
303      * the total command string limit. Should someone actually construct such a
304      * JAMSHELL value it will get reported as an 'invalid parameter'
305      * CreateProcessA() Windows API failure which seems like a good enough
306      * result for such intentional mischief.
307      */
308 
309     /* Check for too long command lines. */
310     return check_cmd_for_too_long_lines( command->value, maxline(),
311         error_length, error_max_length );
312 }
313 
314 
315 /*
316  * exec_cmd() - launch an async command execution
317  *
318  * We assume exec_check() already verified that the given command can have its
319  * command string constructed as requested.
320  */
321 
exec_cmd(string const * cmd_orig,int flags,ExecCmdCallback func,void * closure,LIST * shell)322 void exec_cmd
323 (
324     string const * cmd_orig,
325     int flags,
326     ExecCmdCallback func,
327     void * closure,
328     LIST * shell
329 )
330 {
331     int const slot = get_free_cmdtab_slot();
332     int const is_raw_cmd = is_raw_command_request( shell );
333     string cmd_local[ 1 ];
334 
335     /* Initialize default shell - anything more than /Q/C is non-portable. */
336     static LIST * default_shell;
337     if ( !default_shell )
338         default_shell = list_new( object_new( "cmd.exe /Q/C" ) );
339 
340     /* Specifying no shell means requesting the default shell. */
341     if ( list_empty( shell ) )
342         shell = default_shell;
343 
344     if ( DEBUG_EXECCMD )
345         if ( is_raw_cmd )
346             out_printf( "Executing raw command directly\n" );
347         else
348         {
349             out_printf( "Executing using a command file and the shell: " );
350             list_print( shell );
351             out_printf( "\n" );
352         }
353 
354     /* If we are running a raw command directly - trim its leading whitespaces
355      * as well as any trailing all-whitespace lines but keep any trailing
356      * whitespace in the final/only line containing something other than
357      * whitespace).
358      */
359     if ( is_raw_cmd )
360     {
361         char const * start = cmd_orig->value;
362         char const * p = cmd_orig->value + cmd_orig->size;
363         char const * end = p;
364         while ( isspace( *start ) ) ++start;
365         while ( p > start && isspace( p[ -1 ] ) )
366             if ( *--p == '\n' )
367                 end = p;
368         string_new( cmd_local );
369         string_append_range( cmd_local, start, end );
370         assert( cmd_local->size == raw_command_length( cmd_orig->value ) );
371     }
372     /* If we are not running a raw command directly, prepare a command file to
373      * be executed using an external shell and the actual command string using
374      * that command file.
375      */
376     else
377     {
378         char const * const cmd_file = prepare_command_file( cmd_orig, slot );
379         char const * argv[ MAXARGC + 1 ];  /* +1 for NULL */
380         argv_from_shell( argv, shell, cmd_file, slot );
381         string_new_from_argv( cmd_local, argv );
382     }
383 
384     /* Catch interrupts whenever commands are running. */
385     if ( !intr_installed )
386     {
387         intr_installed = 1;
388         signal( SIGINT, onintr );
389     }
390 
391     cmdtab[ slot ].flags = flags;
392 
393     /* Save input data into the selected running commands table slot. */
394     cmdtab[ slot ].func = func;
395     cmdtab[ slot ].closure = closure;
396 
397     /* Invoke the actual external process using the constructed command line. */
398     invoke_cmd( cmd_local->value, slot );
399 
400     /* Free our local command string copy. */
401     string_free( cmd_local );
402 }
403 
404 
405 /*
406  * exec_wait() - wait for any of the async command processes to terminate
407  *
408  * Wait and drive at most one execution completion, while processing the I/O for
409  * all ongoing commands.
410  */
411 
exec_wait()412 void exec_wait()
413 {
414     int i = -1;
415     int exit_reason;  /* reason why a command completed */
416 
417     /* Wait for a command to complete, while snarfing up any output. */
418     while ( 1 )
419     {
420         /* Check for a complete command, briefly. */
421         i = try_wait( 500 );
422         /* Read in the output of all running commands. */
423         read_output();
424         /* Close out pending debug style dialogs. */
425         close_alerts();
426         /* Process the completed command we found. */
427         if ( i >= 0 ) { exit_reason = EXIT_OK; break; }
428         /* Check if a command ran out of time. */
429         i = try_kill_one();
430         if ( i >= 0 ) { exit_reason = EXIT_TIMEOUT; break; }
431     }
432 
433     /* We have a command... process it. */
434     {
435         DWORD exit_code;
436         timing_info time;
437         int rstat;
438 
439         /* The time data for the command. */
440         record_times( cmdtab[ i ].pi.hProcess, &time );
441 
442         /* Removed the used temporary command file. */
443         if ( cmdtab[ i ].command_file->size )
444             unlink( cmdtab[ i ].command_file->value );
445 
446         /* Find out the process exit code. */
447         GetExitCodeProcess( cmdtab[ i ].pi.hProcess, &exit_code );
448 
449         /* The dispossition of the command. */
450         if ( interrupted() )
451             rstat = EXEC_CMD_INTR;
452         else if ( exit_code )
453             rstat = EXEC_CMD_FAIL;
454         else
455             rstat = EXEC_CMD_OK;
456 
457         /* Call the callback, may call back to jam rule land. */
458         (*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time,
459             cmdtab[ i ].buffer_out->value, cmdtab[ i ].buffer_err->value,
460             exit_reason );
461 
462         /* Clean up our child process tracking data. No need to clear the
463          * temporary command file name as it gets reused.
464          */
465         closeWinHandle( &cmdtab[ i ].pi.hProcess );
466         closeWinHandle( &cmdtab[ i ].pi.hThread );
467         closeWinHandle( &cmdtab[ i ].pipe_out[ EXECCMD_PIPE_READ ] );
468         closeWinHandle( &cmdtab[ i ].pipe_out[ EXECCMD_PIPE_WRITE ] );
469         closeWinHandle( &cmdtab[ i ].pipe_err[ EXECCMD_PIPE_READ ] );
470         closeWinHandle( &cmdtab[ i ].pipe_err[ EXECCMD_PIPE_WRITE ] );
471         string_renew( cmdtab[ i ].buffer_out );
472         string_renew( cmdtab[ i ].buffer_err );
473     }
474 }
475 
476 
477 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
478 
479 /*
480  * Invoke the actual external process using the given command line. Track the
481  * process in our running commands table.
482  */
483 
invoke_cmd(char const * const command,int const slot)484 static void invoke_cmd( char const * const command, int const slot )
485 {
486     SECURITY_ATTRIBUTES sa = { sizeof( SECURITY_ATTRIBUTES ), 0, 0 };
487     SECURITY_DESCRIPTOR sd;
488     STARTUPINFO si = { sizeof( STARTUPINFO ), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
489         0, 0, 0, 0, 0, 0 };
490 
491     /* Init the security data. */
492     InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION );
493     SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE );
494     sa.lpSecurityDescriptor = &sd;
495     sa.bInheritHandle = TRUE;
496 
497     /* Create output buffers. */
498     string_new( cmdtab[ slot ].buffer_out );
499     string_new( cmdtab[ slot ].buffer_err );
500 
501     /* Create pipes for communicating with the child process. */
502     if ( !CreatePipe( &cmdtab[ slot ].pipe_out[ EXECCMD_PIPE_READ ],
503         &cmdtab[ slot ].pipe_out[ EXECCMD_PIPE_WRITE ], &sa, IO_BUFFER_SIZE ) )
504     {
505         reportWindowsError( "CreatePipe", slot );
506         return;
507     }
508     if ( globs.pipe_action && !CreatePipe( &cmdtab[ slot ].pipe_err[
509         EXECCMD_PIPE_READ ], &cmdtab[ slot ].pipe_err[ EXECCMD_PIPE_WRITE ],
510         &sa, IO_BUFFER_SIZE ) )
511     {
512         reportWindowsError( "CreatePipe", slot );
513         return;
514     }
515 
516     /* Set handle inheritance off for the pipe ends the parent reads from. */
517     SetHandleInformation( cmdtab[ slot ].pipe_out[ EXECCMD_PIPE_READ ],
518         HANDLE_FLAG_INHERIT, 0 );
519     if ( globs.pipe_action )
520         SetHandleInformation( cmdtab[ slot ].pipe_err[ EXECCMD_PIPE_READ ],
521             HANDLE_FLAG_INHERIT, 0 );
522 
523     /* Hide the child window, if any. */
524     si.dwFlags |= STARTF_USESHOWWINDOW;
525     si.wShowWindow = SW_HIDE;
526 
527     /* Redirect the child's output streams to our pipes. */
528     si.dwFlags |= STARTF_USESTDHANDLES;
529     si.hStdOutput = cmdtab[ slot ].pipe_out[ EXECCMD_PIPE_WRITE ];
530     si.hStdError = globs.pipe_action
531         ? cmdtab[ slot ].pipe_err[ EXECCMD_PIPE_WRITE ]
532         : cmdtab[ slot ].pipe_out[ EXECCMD_PIPE_WRITE ];
533 
534     /* Let the child inherit stdin, as some commands assume it is available. */
535     si.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
536 
537     if ( DEBUG_EXECCMD )
538         out_printf( "Command string for CreateProcessA(): '%s'\n", command );
539 
540     /* Run the command by creating a sub-process for it. */
541     if ( !CreateProcessA(
542         NULL                    ,  /* application name                     */
543         (char *)command         ,  /* command line                         */
544         NULL                    ,  /* process attributes                   */
545         NULL                    ,  /* thread attributes                    */
546         TRUE                    ,  /* inherit handles                      */
547         CREATE_NEW_PROCESS_GROUP,  /* create flags                         */
548         NULL                    ,  /* env vars, null inherits env          */
549         NULL                    ,  /* current dir, null is our current dir */
550         &si                     ,  /* startup info                         */
551         &cmdtab[ slot ].pi ) )     /* child process info, if created       */
552     {
553         reportWindowsError( "CreateProcessA", slot );
554         return;
555     }
556 
557     register_wait( slot );
558 }
559 
560 
561 /*
562  * For more details on Windows cmd.exe shell command-line length limitations see
563  * the following MSDN article:
564  *     http://support.microsoft.com/default.aspx?scid=kb;en-us;830473
565  */
566 
raw_maxline()567 static int raw_maxline()
568 {
569     OSVERSIONINFO os_info;
570     os_info.dwOSVersionInfoSize = sizeof( os_info );
571     GetVersionEx( &os_info );
572 
573     if ( os_info.dwMajorVersion >= 5 ) return 8191;  /* XP       */
574     if ( os_info.dwMajorVersion == 4 ) return 2047;  /* NT 4.x   */
575     return 996;                                      /* NT 3.5.1 */
576 }
577 
maxline()578 static int maxline()
579 {
580     static int result;
581     if ( !result ) result = raw_maxline();
582     return result;
583 }
584 
585 
586 /*
587  * Closes a Windows HANDLE and resets its variable to 0.
588  */
589 
closeWinHandle(HANDLE * const handle)590 static void closeWinHandle( HANDLE * const handle )
591 {
592     if ( *handle )
593     {
594         CloseHandle( *handle );
595         *handle = 0;
596     }
597 }
598 
599 
600 /*
601  * Frees and renews the given string.
602  */
603 
string_renew(string * const s)604 static void string_renew( string * const s )
605 {
606     string_free( s );
607     string_new( s );
608 }
609 
610 
611 /*
612  * raw_command_length() - valid raw command string length
613  *
614  * Checks whether the given command may be executed as a raw command. If yes,
615  * returns the corresponding command string length. If not, returns -1.
616  *
617  * Rules for constructing raw command strings:
618  *   - Command may not contain unquoted shell I/O redirection characters.
619  *   - May have at most one command line with non-whitespace content.
620  *   - Leading whitespace trimmed.
621  *   - Trailing all-whitespace lines trimmed.
622  *   - Trailing whitespace on the sole command line kept (may theoretically
623  *     affect the executed command).
624  */
625 
raw_command_length(char const * command)626 static long raw_command_length( char const * command )
627 {
628     char const * p;
629     char const * escape = 0;
630     char inquote = 0;
631     char const * newline = 0;
632 
633     /* Skip leading whitespace. */
634     while ( isspace( *command ) )
635         ++command;
636 
637     p = command;
638 
639     /* Look for newlines and unquoted I/O redirection. */
640     do
641     {
642         p += strcspn( p, "\n\"'<>|\\" );
643         switch ( *p )
644         {
645         case '\n':
646             /* If our command contains non-whitespace content split over
647              * multiple lines we can not execute it directly.
648              */
649             newline = p;
650             while ( isspace( *++p ) );
651             if ( *p ) return -1;
652             break;
653 
654         case '\\':
655             escape = escape && escape == p - 1 ? 0 : p;
656             ++p;
657             break;
658 
659         case '"':
660         case '\'':
661             if ( escape && escape == p - 1 )
662                 escape = 0;
663             else if ( inquote == *p )
664                 inquote = 0;
665             else if ( !inquote )
666                 inquote = *p;
667             ++p;
668             break;
669 
670         case '<':
671         case '>':
672         case '|':
673             if ( !inquote )
674                 return -1;
675             ++p;
676             break;
677         }
678     }
679     while ( *p );
680 
681     /* Return the number of characters the command will occupy. */
682     return ( newline ? newline : p ) - command;
683 }
684 
685 
686 /* 64-bit arithmetic helpers. */
687 
688 /* Compute the carry bit from the addition of two 32-bit unsigned numbers. */
689 #define add_carry_bit( a, b ) ((((a) | (b)) >> 31) & (~((a) + (b)) >> 31) & 0x1)
690 
691 /* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1
692  * and h2l2.
693  */
694 #define add_64_hi( h1, l1, h2, l2 ) ((h1) + (h2) + add_carry_bit(l1, l2))
695 
696 
697 /*
698  * Add two 64-bit unsigned numbers, h1l1 and h2l2.
699  */
700 
add_64(unsigned long h1,unsigned long l1,unsigned long h2,unsigned long l2)701 static FILETIME add_64
702 (
703     unsigned long h1, unsigned long l1,
704     unsigned long h2, unsigned long l2
705 )
706 {
707     FILETIME result;
708     result.dwLowDateTime = l1 + l2;
709     result.dwHighDateTime = add_64_hi( h1, l1, h2, l2 );
710     return result;
711 }
712 
713 
add_FILETIME(FILETIME t1,FILETIME t2)714 static FILETIME add_FILETIME( FILETIME t1, FILETIME t2 )
715 {
716     return add_64( t1.dwHighDateTime, t1.dwLowDateTime, t2.dwHighDateTime,
717         t2.dwLowDateTime );
718 }
719 
720 
negate_FILETIME(FILETIME t)721 static FILETIME negate_FILETIME( FILETIME t )
722 {
723     /* 2s complement negation */
724     return add_64( ~t.dwHighDateTime, ~t.dwLowDateTime, 0, 1 );
725 }
726 
727 
728 /*
729  * filetime_to_seconds() - Windows FILETIME --> number of seconds conversion
730  */
731 
filetime_to_seconds(FILETIME const ft)732 static double filetime_to_seconds( FILETIME const ft )
733 {
734     return ft.dwHighDateTime * ( (double)( 1UL << 31 ) * 2.0 * 1.0e-7 ) +
735         ft.dwLowDateTime * 1.0e-7;
736 }
737 
738 
record_times(HANDLE const process,timing_info * const time)739 static void record_times( HANDLE const process, timing_info * const time )
740 {
741     FILETIME creation;
742     FILETIME exit;
743     FILETIME kernel;
744     FILETIME user;
745     if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) )
746     {
747         time->system = filetime_to_seconds( kernel );
748         time->user = filetime_to_seconds( user );
749         timestamp_from_filetime( &time->start, &creation );
750         timestamp_from_filetime( &time->end, &exit );
751     }
752 }
753 
754 
755 static char ioBuffer[ IO_BUFFER_SIZE + 1 ];
756 
757 #define FORWARD_PIPE_NONE 0
758 #define FORWARD_PIPE_STDOUT 1
759 #define FORWARD_PIPE_STDERR 2
760 
read_pipe(HANDLE in,string * out,int forwarding_mode)761 static void read_pipe
762 (
763     HANDLE   in,  /* the pipe to read from */
764     string * out,
765     int      forwarding_mode
766 )
767 {
768     DWORD bytesInBuffer = 0;
769     DWORD bytesAvailable = 0;
770     int i;
771 
772     for (;;)
773     {
774         /* check if we have any data to read */
775         if ( !PeekNamedPipe( in, NULL, IO_BUFFER_SIZE, NULL,
776             &bytesAvailable, NULL ) || bytesAvailable == 0 )
777             return;
778 
779         /* we only read in the available bytes, to avoid blocking */
780         if ( !ReadFile( in, ioBuffer, bytesAvailable <= IO_BUFFER_SIZE ?
781             bytesAvailable : IO_BUFFER_SIZE, &bytesInBuffer, NULL ) || bytesInBuffer == 0 )
782             return;
783 
784         /* Clean up some illegal chars. */
785         for ( i = 0; i < bytesInBuffer; ++i )
786         {
787             if ( ( (unsigned char)ioBuffer[ i ] < 1 ) )
788                 ioBuffer[ i ] = '?';
789         }
790         /* Null, terminate. */
791         ioBuffer[ bytesInBuffer ] = '\0';
792         /* Append to the output. */
793         string_append( out, ioBuffer );
794         /* Copy it to our output if appropriate */
795         if ( forwarding_mode == FORWARD_PIPE_STDOUT )
796             out_data( ioBuffer );
797         else if ( forwarding_mode == FORWARD_PIPE_STDERR )
798             err_data( ioBuffer );
799     }
800 }
801 
802 #define EARLY_OUTPUT( cmd ) \
803     ( ! ( cmd.flags & EXEC_CMD_QUIET ) )
804 
805 #define FORWARD_STDOUT( c )                                 \
806     ( ( EARLY_OUTPUT( c ) && ( globs.pipe_action != 2 ) ) ? \
807         FORWARD_PIPE_STDOUT : FORWARD_PIPE_NONE )
808 #define FORWARD_STDERR( c )                                 \
809     ( ( EARLY_OUTPUT( c ) && ( globs.pipe_action & 2 ) ) ?  \
810         FORWARD_PIPE_STDERR : FORWARD_PIPE_NONE )
811 
read_output()812 static void read_output()
813 {
814     int i;
815     for ( i = 0; i < globs.jobs; ++i )
816         if ( cmdtab[ i ].pi.hProcess )
817         {
818             /* Read stdout data. */
819             if ( cmdtab[ i ].pipe_out[ EXECCMD_PIPE_READ ] )
820                 read_pipe( cmdtab[ i ].pipe_out[ EXECCMD_PIPE_READ ],
821                     cmdtab[ i ].buffer_out, FORWARD_STDOUT( cmdtab[ i ] ) );
822             /* Read stderr data. */
823             if ( cmdtab[ i ].pipe_err[ EXECCMD_PIPE_READ ] )
824                 read_pipe( cmdtab[ i ].pipe_err[ EXECCMD_PIPE_READ ],
825                     cmdtab[ i ].buffer_err, FORWARD_STDERR( cmdtab[ i ] ) );
826         }
827 }
828 
try_wait_callback(void * data,BOOLEAN is_timeout)829 static void CALLBACK try_wait_callback( void * data, BOOLEAN is_timeout )
830 {
831     struct _cmdtab_t * slot = ( struct _cmdtab_t * )data;
832     WaitForSingleObject( process_queue.write_okay, INFINITE );
833     process_queue.job_index = slot - cmdtab;
834     assert( !is_timeout );
835     SetEvent( process_queue.read_okay );
836     /* Okay.  Non-blocking. */
837     UnregisterWait( slot->wait_handle );
838 }
839 
try_wait_impl(DWORD timeout)840 static int try_wait_impl( DWORD timeout )
841 {
842     int job_index;
843     int timed_out;
844     int res = WaitForSingleObject( process_queue.read_okay, timeout );
845     if ( res != WAIT_OBJECT_0 )
846         return -1;
847     job_index = process_queue.job_index;
848     SetEvent( process_queue.write_okay );
849     return job_index;
850 }
851 
register_wait(int job_id)852 static void register_wait( int job_id )
853 {
854     if ( globs.jobs > MAXIMUM_WAIT_OBJECTS )
855     {
856         HANDLE ignore;
857         RegisterWaitForSingleObject( &cmdtab[ job_id ].wait_handle,
858             cmdtab[ job_id ].pi.hProcess,
859             &try_wait_callback, &cmdtab[ job_id ], INFINITE,
860             WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE );
861     }
862 }
863 
864 /*
865  * Waits for a single child process command to complete, or the timeout,
866  * whichever comes first. Returns the index of the completed command in the
867  * cmdtab array, or -1.
868  */
869 
try_wait(int const timeoutMillis)870 static int try_wait( int const timeoutMillis )
871 {
872     if ( globs.jobs <= MAXIMUM_WAIT_OBJECTS )
873     {
874         int i;
875         HANDLE active_handles[ MAXIMUM_WAIT_OBJECTS ];
876         int job_ids[ MAXIMUM_WAIT_OBJECTS ];
877         DWORD num_handles = 0;
878         DWORD wait_api_result;
879         for ( i = 0; i < globs.jobs; ++i )
880         {
881             if( cmdtab[ i ].pi.hProcess )
882             {
883                 job_ids[ num_handles ] = i;
884                 active_handles[ num_handles ] = cmdtab[ i ].pi.hProcess;
885                 ++num_handles;
886             }
887         }
888         wait_api_result = WaitForMultipleObjects( num_handles, active_handles, FALSE, timeoutMillis );
889         if ( WAIT_OBJECT_0 <= wait_api_result && wait_api_result < WAIT_OBJECT_0 + globs.jobs )
890         {
891             return job_ids[ wait_api_result - WAIT_OBJECT_0 ];
892         }
893         else
894         {
895             return -1;
896         }
897     }
898     else
899     {
900         return try_wait_impl( timeoutMillis );
901     }
902 
903 }
904 
905 
try_kill_one()906 static int try_kill_one()
907 {
908     /* Only need to check if a timeout was specified with the -l option. */
909     if ( globs.timeout > 0 )
910     {
911         int i;
912         for ( i = 0; i < globs.jobs; ++i )
913             if ( cmdtab[ i ].pi.hProcess )
914             {
915                 double const t = running_time( cmdtab[ i ].pi.hProcess );
916                 if ( t > (double)globs.timeout )
917                 {
918                     /* The job may have left an alert dialog around, try and get
919                      * rid of it before killing the job itself.
920                      */
921                     close_alert( &cmdtab[ i ].pi );
922                     /* We have a "runaway" job, kill it. */
923                     kill_process_tree( cmdtab[ i ].pi.dwProcessId,
924                         cmdtab[ i ].pi.hProcess );
925                     /* And return its running commands table slot. */
926                     return i;
927                 }
928             }
929     }
930     return -1;
931 }
932 
933 
close_alerts()934 static void close_alerts()
935 {
936     /* We only attempt this every 5 seconds or so, because it is not a cheap
937      * operation, and we will catch the alerts eventually. This check uses
938      * floats as some compilers define CLOCKS_PER_SEC as a float or double.
939      */
940     if ( ( (float)clock() / (float)( CLOCKS_PER_SEC * 5 ) ) < ( 1.0 / 5.0 ) )
941     {
942         int i;
943         for ( i = 0; i < globs.jobs; ++i )
944             if ( cmdtab[ i ].pi.hProcess )
945                 close_alert( &cmdtab[ i ].pi );
946     }
947 }
948 
949 
950 /*
951  * Calc the current running time of an *active* process.
952  */
953 
running_time(HANDLE const process)954 static double running_time( HANDLE const process )
955 {
956     FILETIME creation;
957     FILETIME exit;
958     FILETIME kernel;
959     FILETIME user;
960     if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) )
961     {
962         /* Compute the elapsed time. */
963         FILETIME current;
964         GetSystemTimeAsFileTime( &current );
965         return filetime_to_seconds( add_FILETIME( current,
966             negate_FILETIME( creation ) ) );
967     }
968     return 0.0;
969 }
970 
971 
972 /*
973  * Not really optimal, or efficient, but it is easier this way, and it is not
974  * like we are going to be killing thousands, or even tens of processes.
975  */
976 
kill_process_tree(DWORD const pid,HANDLE const process)977 static void kill_process_tree( DWORD const pid, HANDLE const process )
978 {
979     HANDLE const process_snapshot_h = CreateToolhelp32Snapshot(
980         TH32CS_SNAPPROCESS, 0 );
981     if ( INVALID_HANDLE_VALUE != process_snapshot_h )
982     {
983         BOOL ok = TRUE;
984         PROCESSENTRY32 pinfo;
985         pinfo.dwSize = sizeof( PROCESSENTRY32 );
986         for (
987             ok = Process32First( process_snapshot_h, &pinfo );
988             ok == TRUE;
989             ok = Process32Next( process_snapshot_h, &pinfo ) )
990         {
991             if ( pinfo.th32ParentProcessID == pid )
992             {
993                 /* Found a child, recurse to kill it and anything else below it.
994                  */
995                 HANDLE const ph = OpenProcess( PROCESS_ALL_ACCESS, FALSE,
996                     pinfo.th32ProcessID );
997                 if ( ph )
998                 {
999                     kill_process_tree( pinfo.th32ProcessID, ph );
1000                     CloseHandle( ph );
1001                 }
1002             }
1003         }
1004         CloseHandle( process_snapshot_h );
1005     }
1006     /* Now that the children are all dead, kill the root. */
1007     TerminateProcess( process, -2 );
1008 }
1009 
1010 
creation_time(HANDLE const process)1011 static double creation_time( HANDLE const process )
1012 {
1013     FILETIME creation;
1014     FILETIME exit;
1015     FILETIME kernel;
1016     FILETIME user;
1017     return GetProcessTimes( process, &creation, &exit, &kernel, &user )
1018         ? filetime_to_seconds( creation )
1019         : 0.0;
1020 }
1021 
1022 
1023 /*
1024  * Recursive check if first process is parent (directly or indirectly) of the
1025  * second one. Both processes are passed as process ids, not handles. Special
1026  * return value 2 means that the second process is smss.exe and its parent
1027  * process is System (first argument is ignored).
1028  */
1029 
is_parent_child(DWORD const parent,DWORD const child)1030 static int is_parent_child( DWORD const parent, DWORD const child )
1031 {
1032     HANDLE process_snapshot_h = INVALID_HANDLE_VALUE;
1033 
1034     if ( !child )
1035         return 0;
1036     if ( parent == child )
1037         return 1;
1038 
1039     process_snapshot_h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
1040     if ( INVALID_HANDLE_VALUE != process_snapshot_h )
1041     {
1042         BOOL ok = TRUE;
1043         PROCESSENTRY32 pinfo;
1044         pinfo.dwSize = sizeof( PROCESSENTRY32 );
1045         for (
1046             ok = Process32First( process_snapshot_h, &pinfo );
1047             ok == TRUE;
1048             ok = Process32Next( process_snapshot_h, &pinfo ) )
1049         {
1050             if ( pinfo.th32ProcessID == child )
1051             {
1052                 /* Unfortunately, process ids are not really unique. There might
1053                  * be spurious "parent and child" relationship match between two
1054                  * non-related processes if real parent process of a given
1055                  * process has exited (while child process kept running as an
1056                  * "orphan") and the process id of such parent process has been
1057                  * reused by internals of the operating system when creating
1058                  * another process.
1059                  *
1060                  * Thus an additional check is needed - process creation time.
1061                  * This check may fail (i.e. return 0) for system processes due
1062                  * to insufficient privileges, and that is OK.
1063                  */
1064                 double tchild = 0.0;
1065                 double tparent = 0.0;
1066                 HANDLE const hchild = OpenProcess( PROCESS_QUERY_INFORMATION,
1067                     FALSE, pinfo.th32ProcessID );
1068                 CloseHandle( process_snapshot_h );
1069 
1070                 /* csrss.exe may display message box like following:
1071                  *   xyz.exe - Unable To Locate Component
1072                  *   This application has failed to start because
1073                  *   boost_foo-bar.dll was not found. Re-installing the
1074                  *   application may fix the problem
1075                  * This actually happens when starting a test process that
1076                  * depends on a dynamic library which failed to build. We want
1077                  * to automatically close these message boxes even though
1078                  * csrss.exe is not our child process. We may depend on the fact
1079                  * that (in all current versions of Windows) csrss.exe is a
1080                  * direct child of the smss.exe process, which in turn is a
1081                  * direct child of the System process, which always has process
1082                  * id == 4. This check must be performed before comparing
1083                  * process creation times.
1084                  */
1085                 if ( !stricmp( pinfo.szExeFile, "csrss.exe" ) &&
1086                     is_parent_child( parent, pinfo.th32ParentProcessID ) == 2 )
1087                     return 1;
1088                 if ( !stricmp( pinfo.szExeFile, "smss.exe" ) &&
1089                     ( pinfo.th32ParentProcessID == 4 ) )
1090                     return 2;
1091 
1092                 if ( hchild )
1093                 {
1094                     HANDLE hparent = OpenProcess( PROCESS_QUERY_INFORMATION,
1095                         FALSE, pinfo.th32ParentProcessID );
1096                     if ( hparent )
1097                     {
1098                         tchild = creation_time( hchild );
1099                         tparent = creation_time( hparent );
1100                         CloseHandle( hparent );
1101                     }
1102                     CloseHandle( hchild );
1103                 }
1104 
1105                 /* Return 0 if one of the following is true:
1106                  *  1. we failed to read process creation time
1107                  *  2. child was created before alleged parent
1108                  */
1109                 if ( ( tchild == 0.0 ) || ( tparent == 0.0 ) ||
1110                     ( tchild < tparent ) )
1111                     return 0;
1112 
1113                 return is_parent_child( parent, pinfo.th32ParentProcessID ) & 1;
1114             }
1115         }
1116 
1117         CloseHandle( process_snapshot_h );
1118     }
1119 
1120     return 0;
1121 }
1122 
1123 
1124 /*
1125  * Called by the OS for each topmost window.
1126  */
1127 
close_alert_window_enum(HWND hwnd,LPARAM lParam)1128 BOOL CALLBACK close_alert_window_enum( HWND hwnd, LPARAM lParam )
1129 {
1130     char buf[ 7 ] = { 0 };
1131     PROCESS_INFORMATION const * const pi = (PROCESS_INFORMATION *)lParam;
1132     DWORD pid;
1133     DWORD tid;
1134 
1135     /* We want to find and close any window that:
1136      *  1. is visible and
1137      *  2. is a dialog and
1138      *  3. is displayed by any of our child processes
1139      */
1140     if (
1141         /* We assume hidden windows do not require user interaction. */
1142         !IsWindowVisible( hwnd )
1143         /* Failed to read class name; presume it is not a dialog. */
1144         || !GetClassNameA( hwnd, buf, sizeof( buf ) )
1145         /* All Windows system dialogs use the same Window class name. */
1146         || strcmp( buf, "#32770" ) )
1147         return TRUE;
1148 
1149     /* GetWindowThreadProcessId() returns 0 on error, otherwise thread id of
1150      * the window's message pump thread.
1151      */
1152     tid = GetWindowThreadProcessId( hwnd, &pid );
1153     if ( !tid || !is_parent_child( pi->dwProcessId, pid ) )
1154         return TRUE;
1155 
1156     /* Ask real nice. */
1157     PostMessageA( hwnd, WM_CLOSE, 0, 0 );
1158 
1159     /* Wait and see if it worked. If not, insist. */
1160     if ( WaitForSingleObject( pi->hProcess, 200 ) == WAIT_TIMEOUT )
1161     {
1162         PostThreadMessageA( tid, WM_QUIT, 0, 0 );
1163         WaitForSingleObject( pi->hProcess, 300 );
1164     }
1165 
1166     /* Done, we do not want to check any other windows now. */
1167     return FALSE;
1168 }
1169 
1170 
close_alert(PROCESS_INFORMATION const * const pi)1171 static void close_alert( PROCESS_INFORMATION const * const pi )
1172 {
1173     EnumWindows( &close_alert_window_enum, (LPARAM)pi );
1174 }
1175 
1176 
1177 /*
1178  * Open a command file to store the command into for executing using an external
1179  * shell. Returns a pointer to a FILE open for writing or 0 in case such a file
1180  * could not be opened. The file name used is stored back in the corresponding
1181  * running commands table slot.
1182  *
1183  * Expects the running commands table slot's command_file attribute to contain
1184  * either a zeroed out string object or one prepared previously by this same
1185  * function.
1186  */
1187 
open_command_file(int const slot)1188 static FILE * open_command_file( int const slot )
1189 {
1190     string * const command_file = cmdtab[ slot ].command_file;
1191 
1192     /* If the temporary command file name has not already been prepared for this
1193      * slot number, prepare a new one containing a '##' place holder that will
1194      * be changed later and needs to be located at a fixed distance from the
1195      * end.
1196      */
1197     if ( !command_file->value )
1198     {
1199         DWORD const procID = GetCurrentProcessId();
1200         string const * const tmpdir = path_tmpdir();
1201         string_new( command_file );
1202         string_reserve( command_file, tmpdir->size + 64 );
1203         command_file->size = sprintf( command_file->value,
1204             "%s\\jam%d-%02d-##.bat", tmpdir->value, procID, slot );
1205     }
1206 
1207     /* For some reason opening a command file can fail intermittently. But doing
1208      * some retries works. Most likely this is due to a previously existing file
1209      * of the same name that happens to still be opened by an active virus
1210      * scanner. Originally pointed out and fixed by Bronek Kozicki.
1211      *
1212      * We first try to open several differently named files to avoid having to
1213      * wait idly if not absolutely necessary. Our temporary command file names
1214      * contain a fixed position place holder we use for generating different
1215      * file names.
1216      */
1217     {
1218         char * const index1 = command_file->value + command_file->size - 6;
1219         char * const index2 = index1 + 1;
1220         int waits_remaining;
1221         assert( command_file->value < index1 );
1222         assert( index2 + 1 < command_file->value + command_file->size );
1223         assert( index2[ 1 ] == '.' );
1224         for ( waits_remaining = 3; ; --waits_remaining )
1225         {
1226             int index;
1227             for ( index = 0; index != 20; ++index )
1228             {
1229                 FILE * f;
1230                 *index1 = '0' + index / 10;
1231                 *index2 = '0' + index % 10;
1232                 f = fopen( command_file->value, "w" );
1233                 if ( f ) return f;
1234             }
1235             if ( !waits_remaining ) break;
1236             Sleep( 250 );
1237         }
1238     }
1239 
1240     return 0;
1241 }
1242 
1243 
1244 /*
1245  * Prepare a command file to be executed using an external shell.
1246  */
1247 
prepare_command_file(string const * command,int slot)1248 static char const * prepare_command_file( string const * command, int slot )
1249 {
1250     FILE * const f = open_command_file( slot );
1251     if ( !f )
1252     {
1253         err_printf( "failed to write command file!\n" );
1254         exit( EXITBAD );
1255     }
1256     fputs( command->value, f );
1257     fclose( f );
1258     return cmdtab[ slot ].command_file->value;
1259 }
1260 
1261 
1262 /*
1263  * Find a free slot in the running commands table.
1264  */
1265 
get_free_cmdtab_slot()1266 static int get_free_cmdtab_slot()
1267 {
1268     int slot;
1269     for ( slot = 0; slot < globs.jobs; ++slot )
1270         if ( !cmdtab[ slot ].pi.hProcess )
1271             return slot;
1272     err_printf( "no slots for child!\n" );
1273     exit( EXITBAD );
1274 }
1275 
1276 
1277 /*
1278  * Put together the final command string we are to run.
1279  */
1280 
string_new_from_argv(string * result,char const * const * argv)1281 static void string_new_from_argv( string * result, char const * const * argv )
1282 {
1283     assert( argv );
1284     assert( argv[ 0 ] );
1285     string_copy( result, *(argv++) );
1286     while ( *argv )
1287     {
1288         string_push_back( result, ' ' );
1289         string_push_back( result, '"' );
1290         string_append( result, *(argv++) );
1291         string_push_back( result, '"' );
1292     }
1293 }
1294 
1295 
1296 /*
1297  * Reports the last failed Windows API related error message.
1298  */
1299 
reportWindowsError(char const * const apiName,int slot)1300 static void reportWindowsError( char const * const apiName, int slot )
1301 {
1302     char * errorMessage;
1303     char buf[24];
1304     string * err_buf;
1305     timing_info time;
1306     DWORD const errorCode = GetLastError();
1307     DWORD apiResult = FormatMessageA(
1308         FORMAT_MESSAGE_ALLOCATE_BUFFER |  /* __in      DWORD dwFlags       */
1309         FORMAT_MESSAGE_FROM_SYSTEM |
1310         FORMAT_MESSAGE_IGNORE_INSERTS,
1311         NULL,                             /* __in_opt  LPCVOID lpSource    */
1312         errorCode,                        /* __in      DWORD dwMessageId   */
1313         0,                                /* __in      DWORD dwLanguageId  */
1314         (LPSTR)&errorMessage,             /* __out     LPTSTR lpBuffer     */
1315         0,                                /* __in      DWORD nSize         */
1316         0 );                              /* __in_opt  va_list * Arguments */
1317 
1318     /* Build a message as if the process had written to stderr. */
1319     if ( globs.pipe_action )
1320         err_buf = cmdtab[ slot ].buffer_err;
1321     else
1322         err_buf = cmdtab[ slot ].buffer_out;
1323     string_append( err_buf, apiName );
1324     string_append( err_buf, "() Windows API failed: " );
1325     sprintf( buf, "%d", errorCode );
1326     string_append( err_buf, buf );
1327 
1328     if ( !apiResult )
1329         string_append( err_buf, ".\n" );
1330     else
1331     {
1332         string_append( err_buf, " - " );
1333         string_append( err_buf, errorMessage );
1334         /* Make sure that the buffer is terminated with a newline */
1335         if( err_buf->value[ err_buf->size - 1 ] != '\n' )
1336             string_push_back( err_buf, '\n' );
1337         LocalFree( errorMessage );
1338     }
1339 
1340     /* Since the process didn't actually start, use a blank timing_info. */
1341     time.system = 0;
1342     time.user = 0;
1343     timestamp_current( &time.start );
1344     timestamp_current( &time.end );
1345 
1346     /* Invoke the callback with a failure status. */
1347     (*cmdtab[ slot ].func)( cmdtab[ slot ].closure, EXEC_CMD_FAIL, &time,
1348         cmdtab[ slot ].buffer_out->value, cmdtab[ slot ].buffer_err->value,
1349         EXIT_OK );
1350 
1351     /* Clean up any handles that were opened. */
1352     closeWinHandle( &cmdtab[ slot ].pi.hProcess );
1353     closeWinHandle( &cmdtab[ slot ].pi.hThread );
1354     closeWinHandle( &cmdtab[ slot ].pipe_out[ EXECCMD_PIPE_READ ] );
1355     closeWinHandle( &cmdtab[ slot ].pipe_out[ EXECCMD_PIPE_WRITE ] );
1356     closeWinHandle( &cmdtab[ slot ].pipe_err[ EXECCMD_PIPE_READ ] );
1357     closeWinHandle( &cmdtab[ slot ].pipe_err[ EXECCMD_PIPE_WRITE ] );
1358     string_renew( cmdtab[ slot ].buffer_out );
1359     string_renew( cmdtab[ slot ].buffer_err );
1360 }
1361 
1362 
1363 #endif /* USE_EXECNT */
1364