VirtualBox

source: kBuild/trunk/src/kmk/w32/subproc/sub_proc.c@ 1423

Last change on this file since 1423 was 1423, checked in by bird, 17 years ago

Fixed bug in the CreateProcess failure handling, should now fail correctly with exit code 127 if for example a command cannot be found. Fixes #30.

  • Property svn:eol-style set to native
File size: 31.1 KB
Line 
1/* Process handling for Windows.
2Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
32006 Free Software Foundation, Inc.
4This file is part of GNU Make.
5
6GNU Make is free software; you can redistribute it and/or modify it under the
7terms of the GNU General Public License as published by the Free Software
8Foundation; either version 2, or (at your option) any later version.
9
10GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
11WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15GNU Make; see the file COPYING. If not, write to the Free Software
16Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */
17
18#include <stdlib.h>
19#include <stdio.h>
20#include <process.h> /* for msvc _beginthreadex, _endthreadex */
21#include <signal.h>
22#include <windows.h>
23
24#include "sub_proc.h"
25#include "proc.h"
26#include "w32err.h"
27#include "config.h"
28#include "debug.h"
29
30static char *make_command_line(char *shell_name, char *exec_path, char **argv);
31
32typedef struct sub_process_t {
33 int sv_stdin[2];
34 int sv_stdout[2];
35 int sv_stderr[2];
36 int using_pipes;
37 char *inp;
38 DWORD incnt;
39 char * volatile outp;
40 volatile DWORD outcnt;
41 char * volatile errp;
42 volatile DWORD errcnt;
43 int pid;
44 int exit_code;
45 int signal;
46 long last_err;
47 long lerrno;
48} sub_process;
49
50static long process_file_io_private(sub_process *pproc, BOOL fNeedToWait); /* bird */
51
52/* keep track of children so we can implement a waitpid-like routine */
53static sub_process *proc_array[MAXIMUM_WAIT_OBJECTS];
54static int proc_index = 0;
55static int fake_exits_pending = 0;
56
57/*
58 * When a process has been waited for, adjust the wait state
59 * array so that we don't wait for it again
60 */
61static void
62process_adjust_wait_state(sub_process* pproc)
63{
64 int i;
65
66 if (!proc_index)
67 return;
68
69 for (i = 0; i < proc_index; i++)
70 if (proc_array[i]->pid == pproc->pid)
71 break;
72
73 if (i < proc_index) {
74 proc_index--;
75 if (i != proc_index)
76 memmove(&proc_array[i], &proc_array[i+1],
77 (proc_index-i) * sizeof(sub_process*));
78 proc_array[proc_index] = NULL;
79 }
80}
81
82/*
83 * Waits for any of the registered child processes to finish.
84 */
85static sub_process *
86process_wait_for_any_private(void)
87{
88 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
89 DWORD retval, which;
90 int i;
91
92 if (!proc_index)
93 return NULL;
94
95 /* build array of handles to wait for */
96 for (i = 0; i < proc_index; i++) {
97 handles[i] = (HANDLE) proc_array[i]->pid;
98
99 if (fake_exits_pending && proc_array[i]->exit_code)
100 break;
101 }
102
103 /* wait for someone to exit */
104 if (!fake_exits_pending) {
105 retval = WaitForMultipleObjects(proc_index, handles, FALSE, INFINITE);
106 which = retval - WAIT_OBJECT_0;
107 } else {
108 fake_exits_pending--;
109 retval = !WAIT_FAILED;
110 which = i;
111 }
112
113 /* return pointer to process */
114 if (retval != WAIT_FAILED) {
115 sub_process* pproc = proc_array[which];
116 process_adjust_wait_state(pproc);
117 return pproc;
118 } else
119 return NULL;
120}
121
122/*
123 * Terminate a process.
124 */
125BOOL
126process_kill(HANDLE proc, int signal)
127{
128 sub_process* pproc = (sub_process*) proc;
129 pproc->signal = signal;
130 return (TerminateProcess((HANDLE) pproc->pid, signal));
131}
132
133/*
134 * Use this function to register processes you wish to wait for by
135 * calling process_file_io(NULL) or process_wait_any(). This must be done
136 * because it is possible for callers of this library to reuse the same
137 * handle for multiple processes launches :-(
138 */
139void
140process_register(HANDLE proc)
141{
142 if (proc_index < MAXIMUM_WAIT_OBJECTS)
143 proc_array[proc_index++] = (sub_process *) proc;
144}
145
146/*
147 * Return the number of processes that we are still waiting for.
148 */
149int
150process_used_slots(void)
151{
152 return proc_index;
153}
154
155/*
156 * Public function which works kind of like waitpid(). Wait for any
157 * of the children to die and return results. To call this function,
158 * you must do 1 of things:
159 *
160 * x = process_easy(...);
161 *
162 * or
163 *
164 * x = process_init_fd();
165 * process_register(x);
166 *
167 * or
168 *
169 * x = process_init();
170 * process_register(x);
171 *
172 * You must NOT then call process_pipe_io() because this function is
173 * not capable of handling automatic notification of any child
174 * death.
175 */
176
177HANDLE
178process_wait_for_any(void)
179{
180 sub_process* pproc = process_wait_for_any_private();
181
182 if (!pproc)
183 return NULL;
184 else {
185 /*
186 * Ouch! can't tell caller if this fails directly. Caller
187 * will have to use process_last_err()
188 */
189#ifdef KMK
190 (void) process_file_io_private(pproc, FALSE);
191#else
192 (void) process_file_io(pproc);
193#endif
194 return ((HANDLE) pproc);
195 }
196}
197
198long
199process_signal(HANDLE proc)
200{
201 if (proc == INVALID_HANDLE_VALUE) return 0;
202 return (((sub_process *)proc)->signal);
203}
204
205long
206process_last_err(HANDLE proc)
207{
208 if (proc == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
209 return (((sub_process *)proc)->last_err);
210}
211
212long
213process_exit_code(HANDLE proc)
214{
215 if (proc == INVALID_HANDLE_VALUE) return EXIT_FAILURE;
216 return (((sub_process *)proc)->exit_code);
217}
218
219/*
2202006-02:
221All the following functions are currently unused.
222All of them would crash gmake if called with argument INVALID_HANDLE_VALUE.
223Hence whoever wants to use one of this functions must invent and implement
224a reasonable error handling for this function.
225
226char *
227process_outbuf(HANDLE proc)
228{
229 return (((sub_process *)proc)->outp);
230}
231
232char *
233process_errbuf(HANDLE proc)
234{
235 return (((sub_process *)proc)->errp);
236}
237
238int
239process_outcnt(HANDLE proc)
240{
241 return (((sub_process *)proc)->outcnt);
242}
243
244int
245process_errcnt(HANDLE proc)
246{
247 return (((sub_process *)proc)->errcnt);
248}
249
250void
251process_pipes(HANDLE proc, int pipes[3])
252{
253 pipes[0] = ((sub_process *)proc)->sv_stdin[0];
254 pipes[1] = ((sub_process *)proc)->sv_stdout[0];
255 pipes[2] = ((sub_process *)proc)->sv_stderr[0];
256 return;
257}
258*/
259
260 HANDLE
261process_init()
262{
263 sub_process *pproc;
264 /*
265 * open file descriptors for attaching stdin/stdout/sterr
266 */
267 HANDLE stdin_pipes[2];
268 HANDLE stdout_pipes[2];
269 HANDLE stderr_pipes[2];
270 SECURITY_ATTRIBUTES inherit;
271 BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
272
273 pproc = malloc(sizeof(*pproc));
274 memset(pproc, 0, sizeof(*pproc));
275
276 /* We can't use NULL for lpSecurityDescriptor because that
277 uses the default security descriptor of the calling process.
278 Instead we use a security descriptor with no DACL. This
279 allows nonrestricted access to the associated objects. */
280
281 if (!InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR)(&sd),
282 SECURITY_DESCRIPTOR_REVISION)) {
283 pproc->last_err = GetLastError();
284 pproc->lerrno = E_SCALL;
285 return((HANDLE)pproc);
286 }
287
288 inherit.nLength = sizeof(inherit);
289 inherit.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)(&sd);
290 inherit.bInheritHandle = TRUE;
291
292 // By convention, parent gets pipe[0], and child gets pipe[1]
293 // This means the READ side of stdin pipe goes into pipe[1]
294 // and the WRITE side of the stdout and stderr pipes go into pipe[1]
295 if (CreatePipe( &stdin_pipes[1], &stdin_pipes[0], &inherit, 0) == FALSE ||
296 CreatePipe( &stdout_pipes[0], &stdout_pipes[1], &inherit, 0) == FALSE ||
297 CreatePipe( &stderr_pipes[0], &stderr_pipes[1], &inherit, 0) == FALSE) {
298
299 pproc->last_err = GetLastError();
300 pproc->lerrno = E_SCALL;
301 return((HANDLE)pproc);
302 }
303
304 //
305 // Mark the parent sides of the pipes as non-inheritable
306 //
307 if (SetHandleInformation(stdin_pipes[0],
308 HANDLE_FLAG_INHERIT, 0) == FALSE ||
309 SetHandleInformation(stdout_pipes[0],
310 HANDLE_FLAG_INHERIT, 0) == FALSE ||
311 SetHandleInformation(stderr_pipes[0],
312 HANDLE_FLAG_INHERIT, 0) == FALSE) {
313
314 pproc->last_err = GetLastError();
315 pproc->lerrno = E_SCALL;
316 return((HANDLE)pproc);
317 }
318 pproc->sv_stdin[0] = (int) stdin_pipes[0];
319 pproc->sv_stdin[1] = (int) stdin_pipes[1];
320 pproc->sv_stdout[0] = (int) stdout_pipes[0];
321 pproc->sv_stdout[1] = (int) stdout_pipes[1];
322 pproc->sv_stderr[0] = (int) stderr_pipes[0];
323 pproc->sv_stderr[1] = (int) stderr_pipes[1];
324
325 pproc->using_pipes = 1;
326
327 pproc->lerrno = 0;
328
329 return((HANDLE)pproc);
330}
331
332
333 HANDLE
334process_init_fd(HANDLE stdinh, HANDLE stdouth, HANDLE stderrh)
335{
336 sub_process *pproc;
337
338 pproc = malloc(sizeof(*pproc));
339 memset(pproc, 0, sizeof(*pproc));
340
341 /*
342 * Just pass the provided file handles to the 'child side' of the
343 * pipe, bypassing pipes altogether.
344 */
345 pproc->sv_stdin[1] = (int) stdinh;
346 pproc->sv_stdout[1] = (int) stdouth;
347 pproc->sv_stderr[1] = (int) stderrh;
348
349 pproc->last_err = pproc->lerrno = 0;
350
351 return((HANDLE)pproc);
352}
353
354
355static HANDLE
356find_file(char *exec_path, LPOFSTRUCT file_info)
357{
358 HANDLE exec_handle;
359 char *fname;
360 char *ext;
361#ifdef KMK
362 size_t exec_path_len;
363
364 /*
365 * if there is an .exe extension there already, don't waste time here.
366 * If .exe scripts become common, they can be handled in a CreateProcess
367 * failure path instead of here.
368 */
369 exec_path_len = strlen(exec_path);
370 if ( exec_path_len > 4
371 && exec_path[exec_path_len - 4] == '.'
372 && !stricmp(exec_path + exec_path_len - 3, "exe")){
373 return((HANDLE)HFILE_ERROR);
374 }
375
376 fname = malloc(exec_path_len + 5);
377#else
378 fname = malloc(strlen(exec_path) + 5);
379#endif
380 strcpy(fname, exec_path);
381 ext = fname + strlen(fname);
382
383 strcpy(ext, ".exe");
384 if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
385 OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
386 free(fname);
387 return(exec_handle);
388 }
389
390 strcpy(ext, ".cmd");
391 if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
392 OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
393 free(fname);
394 return(exec_handle);
395 }
396
397 strcpy(ext, ".bat");
398 if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
399 OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
400 free(fname);
401 return(exec_handle);
402 }
403
404 /* should .com come before this case? */
405 if ((exec_handle = (HANDLE)OpenFile(exec_path, file_info,
406 OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
407 free(fname);
408 return(exec_handle);
409 }
410
411 strcpy(ext, ".com");
412 if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
413 OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
414 free(fname);
415 return(exec_handle);
416 }
417
418 free(fname);
419 return(exec_handle);
420}
421
422
423/*
424 * Description: Create the child process to be helped
425 *
426 * Returns: success <=> 0
427 *
428 * Notes/Dependencies:
429 */
430long
431process_begin(
432 HANDLE proc,
433 char **argv,
434 char **envp,
435 char *exec_path,
436 char *as_user)
437{
438 sub_process *pproc = (sub_process *)proc;
439 char *shell_name = 0;
440 int file_not_found=0;
441 HANDLE exec_handle;
442 char buf[256];
443 DWORD bytes_returned;
444 DWORD flags;
445 char *command_line;
446 STARTUPINFO startInfo;
447 PROCESS_INFORMATION procInfo;
448 char *envblk=NULL;
449 OFSTRUCT file_info;
450
451
452 /*
453 * Shell script detection... if the exec_path starts with #! then
454 * we want to exec shell-script-name exec-path, not just exec-path
455 * NT doesn't recognize #!/bin/sh or #!/etc/Tivoli/bin/perl. We do not
456 * hard-code the path to the shell or perl or whatever: Instead, we
457 * assume it's in the path somewhere (generally, the NT tools
458 * bin directory)
459 * We use OpenFile here because it is capable of searching the Path.
460 */
461
462 exec_handle = find_file(exec_path, &file_info);
463
464 /*
465 * If we couldn't open the file, just assume that Windows32 will be able
466 * to find and execute it.
467 */
468 if (exec_handle == (HANDLE)HFILE_ERROR) {
469 file_not_found++;
470 }
471 else {
472 /* Attempt to read the first line of the file */
473 if (ReadFile( exec_handle,
474 buf, sizeof(buf) - 1, /* leave room for trailing NULL */
475 &bytes_returned, 0) == FALSE || bytes_returned < 2) {
476
477 pproc->last_err = GetLastError();
478 pproc->lerrno = E_IO;
479 CloseHandle(exec_handle);
480 return(-1);
481 }
482 if (buf[0] == '#' && buf[1] == '!') {
483 /*
484 * This is a shell script... Change the command line from
485 * exec_path args to shell_name exec_path args
486 */
487 char *p;
488
489 /* Make sure buf is NULL terminated */
490 buf[bytes_returned] = 0;
491 /*
492 * Depending on the file system type, etc. the first line
493 * of the shell script may end with newline or newline-carriage-return
494 * Whatever it ends with, cut it off.
495 */
496 p= strchr(buf, '\n');
497 if (p)
498 *p = 0;
499 p = strchr(buf, '\r');
500 if (p)
501 *p = 0;
502
503 /*
504 * Find base name of shell
505 */
506 shell_name = strrchr( buf, '/');
507 if (shell_name) {
508 shell_name++;
509 } else {
510 shell_name = &buf[2];/* skipping "#!" */
511 }
512
513 }
514 CloseHandle(exec_handle);
515 }
516
517 flags = 0;
518
519 if (file_not_found)
520 command_line = make_command_line( shell_name, exec_path, argv);
521 else
522 command_line = make_command_line( shell_name, file_info.szPathName,
523 argv);
524
525 if ( command_line == NULL ) {
526 pproc->last_err = 0;
527 pproc->lerrno = E_NO_MEM;
528 return(-1);
529 }
530
531 if (envp) {
532 if (arr2envblk(envp, &envblk) ==FALSE) {
533 pproc->last_err = 0;
534 pproc->lerrno = E_NO_MEM;
535 free( command_line );
536 return(-1);
537 }
538 }
539
540 if ((shell_name) || (file_not_found)) {
541 exec_path = 0; /* Search for the program in %Path% */
542 } else {
543 exec_path = file_info.szPathName;
544 }
545
546 /*
547 * Set up inherited stdin, stdout, stderr for child
548 */
549 GetStartupInfo(&startInfo);
550#ifndef KMK
551 startInfo.dwFlags = STARTF_USESTDHANDLES;
552#endif
553 startInfo.lpReserved = 0;
554 startInfo.cbReserved2 = 0;
555 startInfo.lpReserved2 = 0;
556 startInfo.lpTitle = shell_name ? shell_name : exec_path;
557#ifndef KMK
558 startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1];
559 startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1];
560 startInfo.hStdError = (HANDLE)pproc->sv_stderr[1];
561#else
562 if ( pproc->sv_stdin[1]
563 || pproc->sv_stdout[1]
564 || pproc->sv_stderr[1]) {
565 startInfo.dwFlags = STARTF_USESTDHANDLES;
566 startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1];
567 startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1];
568 startInfo.hStdError = (HANDLE)pproc->sv_stderr[1];
569 } else {
570 startInfo.dwFlags = 0;
571 startInfo.hStdInput = 0;
572 startInfo.hStdOutput = 0;
573 startInfo.hStdError = 0;
574 }
575#endif
576
577 if (as_user) {
578 if (envblk) free(envblk);
579 return -1;
580 } else {
581 DB (DB_JOBS, ("CreateProcess(%s,%s,...)\n",
582 exec_path ? exec_path : "NULL",
583 command_line ? command_line : "NULL"));
584 if (CreateProcess(
585 exec_path,
586 command_line,
587 NULL,
588 0, /* default security attributes for thread */
589 TRUE, /* inherit handles (e.g. helper pipes, oserv socket) */
590 flags,
591 envblk,
592 0, /* default starting directory */
593 &startInfo,
594 &procInfo) == FALSE) {
595
596 pproc->last_err = GetLastError();
597 pproc->lerrno = E_FORK;
598#ifdef KMK
599 if (pproc->last_err == ERROR_FILE_NOT_FOUND)
600 pproc->exit_code = 127; /* see execve failure in job.c. */
601#endif
602 fprintf(stderr, "process_begin: CreateProcess(%s, %s, ...) failed.\n",
603 exec_path ? exec_path : "NULL", command_line);
604 if (envblk) free(envblk);
605 free( command_line );
606 return(-1);
607 }
608 }
609
610 pproc->pid = (int)procInfo.hProcess;
611 /* Close the thread handle -- we'll just watch the process */
612 CloseHandle(procInfo.hThread);
613
614 /* Close the halves of the pipes we don't need */
615#ifndef KMK
616 CloseHandle((HANDLE)pproc->sv_stdin[1]);
617 CloseHandle((HANDLE)pproc->sv_stdout[1]);
618 CloseHandle((HANDLE)pproc->sv_stderr[1]);
619 pproc->sv_stdin[1] = 0;
620 pproc->sv_stdout[1] = 0;
621 pproc->sv_stderr[1] = 0;
622#else
623 if ((HANDLE)pproc->sv_stdin[1]) {
624 CloseHandle((HANDLE)pproc->sv_stdin[1]);
625 pproc->sv_stdin[1] = 0;
626 }
627 if ((HANDLE)pproc->sv_stdout[1]) {
628 CloseHandle((HANDLE)pproc->sv_stdout[1]);
629 pproc->sv_stdout[1] = 0;
630 }
631 if ((HANDLE)pproc->sv_stderr[1]) {
632 CloseHandle((HANDLE)pproc->sv_stderr[1]);
633 pproc->sv_stderr[1] = 0;
634 }
635#endif
636
637 free( command_line );
638 if (envblk) free(envblk);
639 pproc->lerrno=0;
640 return 0;
641}
642
643
644
645static DWORD
646proc_stdin_thread(sub_process *pproc)
647{
648 DWORD in_done;
649 for (;;) {
650 if (WriteFile( (HANDLE) pproc->sv_stdin[0], pproc->inp, pproc->incnt,
651 &in_done, NULL) == FALSE)
652 _endthreadex(0);
653 // This if should never be true for anonymous pipes, but gives
654 // us a chance to change I/O mechanisms later
655 if (in_done < pproc->incnt) {
656 pproc->incnt -= in_done;
657 pproc->inp += in_done;
658 } else {
659 _endthreadex(0);
660 }
661 }
662 return 0; // for compiler warnings only.. not reached
663}
664
665static DWORD
666proc_stdout_thread(sub_process *pproc)
667{
668 DWORD bufsize = 1024;
669 char c;
670 DWORD nread;
671 pproc->outp = malloc(bufsize);
672 if (pproc->outp == NULL)
673 _endthreadex(0);
674 pproc->outcnt = 0;
675
676 for (;;) {
677 if (ReadFile( (HANDLE)pproc->sv_stdout[0], &c, 1, &nread, NULL)
678 == FALSE) {
679/* map_windows32_error_to_string(GetLastError());*/
680 _endthreadex(0);
681 }
682 if (nread == 0)
683 _endthreadex(0);
684 if (pproc->outcnt + nread > bufsize) {
685 bufsize += nread + 512;
686 pproc->outp = realloc(pproc->outp, bufsize);
687 if (pproc->outp == NULL) {
688 pproc->outcnt = 0;
689 _endthreadex(0);
690 }
691 }
692 pproc->outp[pproc->outcnt++] = c;
693 }
694 return 0;
695}
696
697static DWORD
698proc_stderr_thread(sub_process *pproc)
699{
700 DWORD bufsize = 1024;
701 char c;
702 DWORD nread;
703 pproc->errp = malloc(bufsize);
704 if (pproc->errp == NULL)
705 _endthreadex(0);
706 pproc->errcnt = 0;
707
708 for (;;) {
709 if (ReadFile( (HANDLE)pproc->sv_stderr[0], &c, 1, &nread, NULL) == FALSE) {
710 map_windows32_error_to_string(GetLastError());
711 _endthreadex(0);
712 }
713 if (nread == 0)
714 _endthreadex(0);
715 if (pproc->errcnt + nread > bufsize) {
716 bufsize += nread + 512;
717 pproc->errp = realloc(pproc->errp, bufsize);
718 if (pproc->errp == NULL) {
719 pproc->errcnt = 0;
720 _endthreadex(0);
721 }
722 }
723 pproc->errp[pproc->errcnt++] = c;
724 }
725 return 0;
726}
727
728
729/*
730 * Purpose: collects output from child process and returns results
731 *
732 * Description:
733 *
734 * Returns:
735 *
736 * Notes/Dependencies:
737 */
738 long
739process_pipe_io(
740 HANDLE proc,
741 char *stdin_data,
742 int stdin_data_len)
743{
744 sub_process *pproc = (sub_process *)proc;
745 bool_t stdin_eof = FALSE, stdout_eof = FALSE, stderr_eof = FALSE;
746 HANDLE childhand = (HANDLE) pproc->pid;
747 HANDLE tStdin = NULL, tStdout = NULL, tStderr = NULL;
748 unsigned int dwStdin, dwStdout, dwStderr;
749 HANDLE wait_list[4];
750 DWORD wait_count;
751 DWORD wait_return;
752 HANDLE ready_hand;
753 bool_t child_dead = FALSE;
754 BOOL GetExitCodeResult;
755
756 /*
757 * Create stdin thread, if needed
758 */
759 pproc->inp = stdin_data;
760 pproc->incnt = stdin_data_len;
761 if (!pproc->inp) {
762 stdin_eof = TRUE;
763 CloseHandle((HANDLE)pproc->sv_stdin[0]);
764 pproc->sv_stdin[0] = 0;
765 } else {
766 tStdin = (HANDLE) _beginthreadex( 0, 1024,
767 (unsigned (__stdcall *) (void *))proc_stdin_thread,
768 pproc, 0, &dwStdin);
769 if (tStdin == 0) {
770 pproc->last_err = GetLastError();
771 pproc->lerrno = E_SCALL;
772 goto done;
773 }
774 }
775
776 /*
777 * Assume child will produce stdout and stderr
778 */
779 tStdout = (HANDLE) _beginthreadex( 0, 1024,
780 (unsigned (__stdcall *) (void *))proc_stdout_thread, pproc, 0,
781 &dwStdout);
782 tStderr = (HANDLE) _beginthreadex( 0, 1024,
783 (unsigned (__stdcall *) (void *))proc_stderr_thread, pproc, 0,
784 &dwStderr);
785
786 if (tStdout == 0 || tStderr == 0) {
787
788 pproc->last_err = GetLastError();
789 pproc->lerrno = E_SCALL;
790 goto done;
791 }
792
793
794 /*
795 * Wait for all I/O to finish and for the child process to exit
796 */
797
798 while (!stdin_eof || !stdout_eof || !stderr_eof || !child_dead) {
799 wait_count = 0;
800 if (!stdin_eof) {
801 wait_list[wait_count++] = tStdin;
802 }
803 if (!stdout_eof) {
804 wait_list[wait_count++] = tStdout;
805 }
806 if (!stderr_eof) {
807 wait_list[wait_count++] = tStderr;
808 }
809 if (!child_dead) {
810 wait_list[wait_count++] = childhand;
811 }
812
813 wait_return = WaitForMultipleObjects(wait_count, wait_list,
814 FALSE, /* don't wait for all: one ready will do */
815 child_dead? 1000 :INFINITE); /* after the child dies, subthreads have
816 one second to collect all remaining output */
817
818 if (wait_return == WAIT_FAILED) {
819/* map_windows32_error_to_string(GetLastError());*/
820 pproc->last_err = GetLastError();
821 pproc->lerrno = E_SCALL;
822 goto done;
823 }
824
825 ready_hand = wait_list[wait_return - WAIT_OBJECT_0];
826
827 if (ready_hand == tStdin) {
828 CloseHandle((HANDLE)pproc->sv_stdin[0]);
829 pproc->sv_stdin[0] = 0;
830 CloseHandle(tStdin);
831 tStdin = 0;
832 stdin_eof = TRUE;
833
834 } else if (ready_hand == tStdout) {
835
836 CloseHandle((HANDLE)pproc->sv_stdout[0]);
837 pproc->sv_stdout[0] = 0;
838 CloseHandle(tStdout);
839 tStdout = 0;
840 stdout_eof = TRUE;
841
842 } else if (ready_hand == tStderr) {
843
844 CloseHandle((HANDLE)pproc->sv_stderr[0]);
845 pproc->sv_stderr[0] = 0;
846 CloseHandle(tStderr);
847 tStderr = 0;
848 stderr_eof = TRUE;
849
850 } else if (ready_hand == childhand) {
851
852 DWORD ierr;
853 GetExitCodeResult = GetExitCodeProcess(childhand, &ierr);
854 if (ierr == CONTROL_C_EXIT) {
855 pproc->signal = SIGINT;
856 } else {
857 pproc->exit_code = ierr;
858 }
859 if (GetExitCodeResult == FALSE) {
860 pproc->last_err = GetLastError();
861 pproc->lerrno = E_SCALL;
862 goto done;
863 }
864 child_dead = TRUE;
865
866 } else {
867
868 /* ?? Got back a handle we didn't query ?? */
869 pproc->last_err = 0;
870 pproc->lerrno = E_FAIL;
871 goto done;
872 }
873 }
874
875 done:
876 if (tStdin != 0)
877 CloseHandle(tStdin);
878 if (tStdout != 0)
879 CloseHandle(tStdout);
880 if (tStderr != 0)
881 CloseHandle(tStderr);
882
883 if (pproc->lerrno)
884 return(-1);
885 else
886 return(0);
887
888}
889
890/*
891 * Purpose: collects output from child process and returns results
892 *
893 * Description:
894 *
895 * Returns:
896 *
897 * Notes/Dependencies:
898 */
899 long
900process_file_io(
901 HANDLE proc)
902{
903 sub_process *pproc;
904 if (proc == NULL)
905 pproc = process_wait_for_any_private();
906 else
907 pproc = (sub_process *)proc;
908
909 /* some sort of internal error */
910 if (!pproc)
911 return -1;
912
913 return process_file_io_private(proc, TRUE);
914}
915
916/* private function, avoid some kernel calls. (bird) */
917static long
918process_file_io_private(
919 sub_process *pproc,
920 BOOL fNeedToWait)
921{
922 HANDLE childhand;
923 DWORD wait_return;
924 BOOL GetExitCodeResult;
925 DWORD ierr;
926
927 childhand = (HANDLE) pproc->pid;
928
929 /*
930 * This function is poorly named, and could also be used just to wait
931 * for child death if you're doing your own pipe I/O. If that is
932 * the case, close the pipe handles here.
933 */
934 if (pproc->sv_stdin[0]) {
935 CloseHandle((HANDLE)pproc->sv_stdin[0]);
936 pproc->sv_stdin[0] = 0;
937 }
938 if (pproc->sv_stdout[0]) {
939 CloseHandle((HANDLE)pproc->sv_stdout[0]);
940 pproc->sv_stdout[0] = 0;
941 }
942 if (pproc->sv_stderr[0]) {
943 CloseHandle((HANDLE)pproc->sv_stderr[0]);
944 pproc->sv_stderr[0] = 0;
945 }
946
947#ifdef KMK
948 if (childhand == NULL || childhand == INVALID_HANDLE_VALUE) {
949 goto done2;
950 }
951#endif
952
953 /*
954 * Wait for the child process to exit it we didn't do that already.
955 */
956 if (fNeedToWait) {
957 wait_return = WaitForSingleObject(childhand, INFINITE);
958 if (wait_return != WAIT_OBJECT_0) {
959/* map_windows32_error_to_string(GetLastError());*/
960 pproc->last_err = GetLastError();
961 pproc->lerrno = E_SCALL;
962 goto done2;
963 }
964 }
965
966 GetExitCodeResult = GetExitCodeProcess(childhand, &ierr);
967 if (ierr == CONTROL_C_EXIT) {
968 pproc->signal = SIGINT;
969 } else {
970 pproc->exit_code = ierr;
971 }
972 if (GetExitCodeResult == FALSE) {
973 pproc->last_err = GetLastError();
974 pproc->lerrno = E_SCALL;
975 }
976
977done2:
978 if (pproc->lerrno)
979 return(-1);
980 else
981 return(0);
982
983}
984
985/*
986 * Description: Clean up any leftover handles, etc. It is up to the
987 * caller to manage and free the input, ouput, and stderr buffers.
988 */
989 void
990process_cleanup(
991 HANDLE proc)
992{
993 sub_process *pproc = (sub_process *)proc;
994 int i;
995
996 if (pproc->using_pipes) {
997 for (i= 0; i <= 1; i++) {
998 if ((HANDLE)pproc->sv_stdin[i])
999 CloseHandle((HANDLE)pproc->sv_stdin[i]);
1000 if ((HANDLE)pproc->sv_stdout[i])
1001 CloseHandle((HANDLE)pproc->sv_stdout[i]);
1002 if ((HANDLE)pproc->sv_stderr[i])
1003 CloseHandle((HANDLE)pproc->sv_stderr[i]);
1004 }
1005 }
1006 if ((HANDLE)pproc->pid)
1007 CloseHandle((HANDLE)pproc->pid);
1008
1009 free(pproc);
1010}
1011
1012
1013/*
1014 * Description:
1015 * Create a command line buffer to pass to CreateProcess
1016 *
1017 * Returns: the buffer or NULL for failure
1018 * Shell case: sh_name a:/full/path/to/script argv[1] argv[2] ...
1019 * Otherwise: argv[0] argv[1] argv[2] ...
1020 *
1021 * Notes/Dependencies:
1022 * CreateProcess does not take an argv, so this command creates a
1023 * command line for the executable.
1024 */
1025
1026static char *
1027make_command_line( char *shell_name, char *full_exec_path, char **argv)
1028{
1029 int argc = 0;
1030 char** argvi;
1031 int* enclose_in_quotes = NULL;
1032 int* enclose_in_quotes_i;
1033 unsigned int bytes_required = 0;
1034 char* command_line;
1035 char* command_line_i;
1036 int cygwin_mode = 0; /* HAVE_CYGWIN_SHELL */
1037 int have_sh = 0; /* HAVE_CYGWIN_SHELL */
1038#undef HAVE_CYGWIN_SHELL
1039#ifdef HAVE_CYGWIN_SHELL
1040 have_sh = (shell_name != NULL || strstr(full_exec_path, "sh.exe"));
1041 cygwin_mode = 1;
1042#endif
1043
1044 if (shell_name && full_exec_path) {
1045 bytes_required
1046 = strlen(shell_name) + 1 + strlen(full_exec_path);
1047 /*
1048 * Skip argv[0] if any, when shell_name is given.
1049 */
1050 if (*argv) argv++;
1051 /*
1052 * Add one for the intervening space.
1053 */
1054 if (*argv) bytes_required++;
1055 }
1056
1057 argvi = argv;
1058 while (*(argvi++)) argc++;
1059
1060 if (argc) {
1061 enclose_in_quotes = (int*) calloc(1, argc * sizeof(int));
1062
1063 if (!enclose_in_quotes) {
1064 return NULL;
1065 }
1066 }
1067
1068 /* We have to make one pass through each argv[i] to see if we need
1069 * to enclose it in ", so we might as well figure out how much
1070 * memory we'll need on the same pass.
1071 */
1072
1073 argvi = argv;
1074 enclose_in_quotes_i = enclose_in_quotes;
1075 while(*argvi) {
1076 char* p = *argvi;
1077 unsigned int backslash_count = 0;
1078
1079 /*
1080 * We have to enclose empty arguments in ".
1081 */
1082 if (!(*p)) *enclose_in_quotes_i = 1;
1083
1084 while(*p) {
1085 switch (*p) {
1086 case '\"':
1087 /*
1088 * We have to insert a backslash for each "
1089 * and each \ that precedes the ".
1090 */
1091 bytes_required += (backslash_count + 1);
1092 backslash_count = 0;
1093 break;
1094
1095#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
1096 case '\\':
1097 backslash_count++;
1098 break;
1099#endif
1100 /*
1101 * At one time we set *enclose_in_quotes_i for '*' or '?' to suppress
1102 * wildcard expansion in programs linked with MSVC's SETARGV.OBJ so
1103 * that argv in always equals argv out. This was removed. Say you have
1104 * such a program named glob.exe. You enter
1105 * glob '*'
1106 * at the sh command prompt. Obviously the intent is to make glob do the
1107 * wildcarding instead of sh. If we set *enclose_in_quotes_i for '*' or '?',
1108 * then the command line that glob would see would be
1109 * glob "*"
1110 * and the _setargv in SETARGV.OBJ would _not_ expand the *.
1111 */
1112 case ' ':
1113 case '\t':
1114 *enclose_in_quotes_i = 1;
1115 /* fall through */
1116
1117 default:
1118 backslash_count = 0;
1119 break;
1120 }
1121
1122 /*
1123 * Add one for each character in argv[i].
1124 */
1125 bytes_required++;
1126
1127 p++;
1128 }
1129
1130 if (*enclose_in_quotes_i) {
1131 /*
1132 * Add one for each enclosing ",
1133 * and one for each \ that precedes the
1134 * closing ".
1135 */
1136 bytes_required += (backslash_count + 2);
1137 }
1138
1139 /*
1140 * Add one for the intervening space.
1141 */
1142 if (*(++argvi)) bytes_required++;
1143 enclose_in_quotes_i++;
1144 }
1145
1146 /*
1147 * Add one for the terminating NULL.
1148 */
1149 bytes_required++;
1150#ifdef KMK /* for the space before the final " in case we need it. */
1151 bytes_required++;
1152#endif
1153
1154 command_line = (char*) malloc(bytes_required);
1155
1156 if (!command_line) {
1157 if (enclose_in_quotes) free(enclose_in_quotes);
1158 return NULL;
1159 }
1160
1161 command_line_i = command_line;
1162
1163 if (shell_name && full_exec_path) {
1164 while(*shell_name) {
1165 *(command_line_i++) = *(shell_name++);
1166 }
1167
1168 *(command_line_i++) = ' ';
1169
1170 while(*full_exec_path) {
1171 *(command_line_i++) = *(full_exec_path++);
1172 }
1173
1174 if (*argv) {
1175 *(command_line_i++) = ' ';
1176 }
1177 }
1178
1179 argvi = argv;
1180 enclose_in_quotes_i = enclose_in_quotes;
1181
1182 while(*argvi) {
1183 char* p = *argvi;
1184 unsigned int backslash_count = 0;
1185
1186 if (*enclose_in_quotes_i) {
1187 *(command_line_i++) = '\"';
1188 }
1189
1190 while(*p) {
1191 if (*p == '\"') {
1192 if (cygwin_mode && have_sh) { /* HAVE_CYGWIN_SHELL */
1193 /* instead of a \", cygwin likes "" */
1194 *(command_line_i++) = '\"';
1195 } else {
1196
1197 /*
1198 * We have to insert a backslash for the "
1199 * and each \ that precedes the ".
1200 */
1201 backslash_count++;
1202
1203 while(backslash_count) {
1204 *(command_line_i++) = '\\';
1205 backslash_count--;
1206 };
1207 }
1208#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
1209 } else if (*p == '\\') {
1210 backslash_count++;
1211 } else {
1212 backslash_count = 0;
1213#endif
1214 }
1215
1216 /*
1217 * Copy the character.
1218 */
1219 *(command_line_i++) = *(p++);
1220 }
1221
1222 if (*enclose_in_quotes_i) {
1223#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
1224 /*
1225 * Add one \ for each \ that precedes the
1226 * closing ".
1227 */
1228 while(backslash_count--) {
1229 *(command_line_i++) = '\\';
1230 };
1231#endif
1232#ifdef KMK
1233 /*
1234 * ash it put off by echo "hello world" ending up as:
1235 * G:/.../kmk_ash.exe -c "echo ""hello world"""
1236 * It wants a space before the last '"'.
1237 * (The 'test_shell' goals in Makefile.kmk tests this problem.)
1238 */
1239 if (command_line_i[-1] == '\"' /* && cygwin_mode && have_sh*/ && !argvi[1]) {
1240 *(command_line_i++) = ' ';
1241 }
1242#endif
1243 *(command_line_i++) = '\"';
1244 }
1245
1246 /*
1247 * Append an intervening space.
1248 */
1249 if (*(++argvi)) {
1250 *(command_line_i++) = ' ';
1251 }
1252
1253 enclose_in_quotes_i++;
1254 }
1255
1256 /*
1257 * Append the terminating NULL.
1258 */
1259 *command_line_i = '\0';
1260
1261 if (enclose_in_quotes) free(enclose_in_quotes);
1262 return command_line;
1263}
1264
1265/*
1266 * Description: Given an argv and optional envp, launch the process
1267 * using the default stdin, stdout, and stderr handles.
1268 * Also, register process so that process_wait_for_any_private()
1269 * can be used via process_file_io(NULL) or
1270 * process_wait_for_any().
1271 *
1272 * Returns:
1273 *
1274 * Notes/Dependencies:
1275 */
1276HANDLE
1277process_easy(
1278 char **argv,
1279 char **envp)
1280{
1281#ifndef KMK
1282 HANDLE hIn;
1283 HANDLE hOut;
1284 HANDLE hErr;
1285#endif
1286 HANDLE hProcess;
1287
1288 if (proc_index >= MAXIMUM_WAIT_OBJECTS) {
1289 DB (DB_JOBS, ("process_easy: All process slots used up\n"));
1290 return INVALID_HANDLE_VALUE;
1291 }
1292#ifndef KMK
1293 if (DuplicateHandle(GetCurrentProcess(),
1294 GetStdHandle(STD_INPUT_HANDLE),
1295 GetCurrentProcess(),
1296 &hIn,
1297 0,
1298 TRUE,
1299 DUPLICATE_SAME_ACCESS) == FALSE) {
1300 fprintf(stderr,
1301 "process_easy: DuplicateHandle(In) failed (e=%ld)\n",
1302 GetLastError());
1303 return INVALID_HANDLE_VALUE;
1304 }
1305 if (DuplicateHandle(GetCurrentProcess(),
1306 GetStdHandle(STD_OUTPUT_HANDLE),
1307 GetCurrentProcess(),
1308 &hOut,
1309 0,
1310 TRUE,
1311 DUPLICATE_SAME_ACCESS) == FALSE) {
1312 fprintf(stderr,
1313 "process_easy: DuplicateHandle(Out) failed (e=%ld)\n",
1314 GetLastError());
1315 return INVALID_HANDLE_VALUE;
1316 }
1317 if (DuplicateHandle(GetCurrentProcess(),
1318 GetStdHandle(STD_ERROR_HANDLE),
1319 GetCurrentProcess(),
1320 &hErr,
1321 0,
1322 TRUE,
1323 DUPLICATE_SAME_ACCESS) == FALSE) {
1324 fprintf(stderr,
1325 "process_easy: DuplicateHandle(Err) failed (e=%ld)\n",
1326 GetLastError());
1327 return INVALID_HANDLE_VALUE;
1328 }
1329
1330 hProcess = process_init_fd(hIn, hOut, hErr);
1331#else
1332 hProcess = process_init_fd(0, 0, 0);
1333#endif /* !KMK */
1334
1335 if (process_begin(hProcess, argv, envp, argv[0], NULL)) {
1336 fake_exits_pending++;
1337 /* process_begin() failed: make a note of that. */
1338 if (!((sub_process*) hProcess)->last_err)
1339 ((sub_process*) hProcess)->last_err = -1;
1340#ifdef KMK
1341 if (!((sub_process*) hProcess)->exit_code)
1342#endif
1343 ((sub_process*) hProcess)->exit_code = process_last_err(hProcess);
1344
1345#ifndef KMK
1346 /* close up unused handles */
1347 CloseHandle(hIn);
1348 CloseHandle(hOut);
1349 CloseHandle(hErr);
1350#endif
1351 }
1352
1353 process_register(hProcess);
1354
1355 return hProcess;
1356}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette