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