VirtualBox

source: kBuild/vendor/gnumake/current/w32/subproc/sub_proc.c@ 486

Last change on this file since 486 was 284, checked in by bird, 19 years ago

Current make snaphot, 2005-05-16.

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