VirtualBox

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

最後變更 在這個檔案從2099是 1993,由 bird 提交於 16 年 前

Merged in current GNU Make code (CVS from 2008-10-28). Ref #55.

  • 屬性 svn:eol-style 設為 native
檔案大小: 31.0 KB
 
1/* Process handling for Windows.
2Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
32006, 2007 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 3 of the License, or (at your option) any later
9version.
10
11GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License along with
16this program. If not, see <http://www.gnu.org/licenses/>. */
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}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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