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 
execnt_unit_test()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 
exec_check(string const * command,LIST ** pShell,int * error_length,int * error_max_length)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 
exec_cmd(string const * cmd_orig,ExecCmdCallback func,void * closure,LIST * shell)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 
exec_wait()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 
invoke_cmd(char const * const command,int const slot)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 
raw_maxline()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 
maxline()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 
closeWinHandle(HANDLE * const handle)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 
string_renew(string * const s)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 
raw_command_length(char const * command)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 
add_64(unsigned long h1,unsigned long l1,unsigned long h2,unsigned long l2)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 
add_FILETIME(FILETIME t1,FILETIME t2)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 
negate_FILETIME(FILETIME t)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 
filetime_to_seconds(FILETIME const ft)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 
record_times(HANDLE const process,timing_info * const time)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 
read_pipe(HANDLE in,string * out)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 
read_output()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 
try_wait(int const timeoutMillis)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 
try_wait_helper(twh_params * params)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 
try_kill_one()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 
close_alerts()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 
running_time(HANDLE const process)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 
kill_process_tree(DWORD const pid,HANDLE const process)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 
creation_time(HANDLE const process)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 
is_parent_child(DWORD const parent,DWORD const child)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 
close_alert_window_enum(HWND hwnd,LPARAM lParam)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 
close_alert(PROCESS_INFORMATION const * const pi)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 
open_command_file(int const slot)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 
prepare_command_file(string const * command,int slot)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 
get_free_cmdtab_slot()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 
string_new_from_argv(string * result,char const * const * argv)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 
reportWindowsError(char const * const apiName,int slot)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 */
1402