VirtualBox

source: kBuild/trunk/src/gmake/w32/subproc/sub_proc.c@ 154

Last change on this file since 154 was 154, checked in by bird, 20 years ago

This commit was generated by cvs2svn to compensate for changes in r153,
which included commits to RCS files with non-trunk default branches.

  • Property svn:eol-style set to native
File size: 26.6 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
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 if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) {
766 pproc->last_err = GetLastError();
767 pproc->lerrno = E_SCALL;
768 goto done;
769 }
770 child_dead = TRUE;
771
772 } else {
773
774 /* ?? Got back a handle we didn't query ?? */
775 pproc->last_err = 0;
776 pproc->lerrno = E_FAIL;
777 goto done;
778 }
779 }
780
781 done:
782 if (tStdin != 0)
783 CloseHandle(tStdin);
784 if (tStdout != 0)
785 CloseHandle(tStdout);
786 if (tStderr != 0)
787 CloseHandle(tStderr);
788
789 if (pproc->lerrno)
790 return(-1);
791 else
792 return(0);
793
794}
795
796/*
797 * Purpose: collects output from child process and returns results
798 *
799 * Description:
800 *
801 * Returns:
802 *
803 * Notes/Dependencies:
804 */
805 long
806process_file_io(
807 HANDLE proc)
808{
809 sub_process *pproc;
810 HANDLE childhand;
811 DWORD wait_return;
812
813 if (proc == NULL)
814 pproc = process_wait_for_any_private();
815 else
816 pproc = (sub_process *)proc;
817
818 /* some sort of internal error */
819 if (!pproc)
820 return -1;
821
822 childhand = (HANDLE) pproc->pid;
823
824 /*
825 * This function is poorly named, and could also be used just to wait
826 * for child death if you're doing your own pipe I/O. If that is
827 * the case, close the pipe handles here.
828 */
829 if (pproc->sv_stdin[0]) {
830 CloseHandle((HANDLE)pproc->sv_stdin[0]);
831 pproc->sv_stdin[0] = 0;
832 }
833 if (pproc->sv_stdout[0]) {
834 CloseHandle((HANDLE)pproc->sv_stdout[0]);
835 pproc->sv_stdout[0] = 0;
836 }
837 if (pproc->sv_stderr[0]) {
838 CloseHandle((HANDLE)pproc->sv_stderr[0]);
839 pproc->sv_stderr[0] = 0;
840 }
841
842 /*
843 * Wait for the child process to exit
844 */
845
846 wait_return = WaitForSingleObject(childhand, INFINITE);
847
848 if (wait_return != WAIT_OBJECT_0) {
849/* map_windows32_error_to_string(GetLastError());*/
850 pproc->last_err = GetLastError();
851 pproc->lerrno = E_SCALL;
852 goto done2;
853 }
854
855 if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) {
856 pproc->last_err = GetLastError();
857 pproc->lerrno = E_SCALL;
858 }
859
860done2:
861 if (pproc->lerrno)
862 return(-1);
863 else
864 return(0);
865
866}
867
868/*
869 * Description: Clean up any leftover handles, etc. It is up to the
870 * caller to manage and free the input, ouput, and stderr buffers.
871 */
872 void
873process_cleanup(
874 HANDLE proc)
875{
876 sub_process *pproc = (sub_process *)proc;
877 int i;
878
879 if (pproc->using_pipes) {
880 for (i= 0; i <= 1; i++) {
881 if ((HANDLE)pproc->sv_stdin[i])
882 CloseHandle((HANDLE)pproc->sv_stdin[i]);
883 if ((HANDLE)pproc->sv_stdout[i])
884 CloseHandle((HANDLE)pproc->sv_stdout[i]);
885 if ((HANDLE)pproc->sv_stderr[i])
886 CloseHandle((HANDLE)pproc->sv_stderr[i]);
887 }
888 }
889 if ((HANDLE)pproc->pid)
890 CloseHandle((HANDLE)pproc->pid);
891
892 free(pproc);
893}
894
895
896/*
897 * Description:
898 * Create a command line buffer to pass to CreateProcess
899 *
900 * Returns: the buffer or NULL for failure
901 * Shell case: sh_name a:/full/path/to/script argv[1] argv[2] ...
902 * Otherwise: argv[0] argv[1] argv[2] ...
903 *
904 * Notes/Dependencies:
905 * CreateProcess does not take an argv, so this command creates a
906 * command line for the executable.
907 */
908
909static char *
910make_command_line( char *shell_name, char *full_exec_path, char **argv)
911{
912 int argc = 0;
913 char** argvi;
914 int* enclose_in_quotes = NULL;
915 int* enclose_in_quotes_i;
916 unsigned int bytes_required = 0;
917 char* command_line;
918 char* command_line_i;
919 int cygwin_mode = 0; /* HAVE_CYGWIN_SHELL */
920 int have_sh = 0; /* HAVE_CYGWIN_SHELL */
921
922#ifdef HAVE_CYGWIN_SHELL
923 have_sh = (shell_name != NULL || strstr(full_exec_path, "sh.exe"));
924 cygwin_mode = 1;
925#endif
926
927 if (shell_name && full_exec_path) {
928 bytes_required
929 = strlen(shell_name) + 1 + strlen(full_exec_path);
930 /*
931 * Skip argv[0] if any, when shell_name is given.
932 */
933 if (*argv) argv++;
934 /*
935 * Add one for the intervening space.
936 */
937 if (*argv) bytes_required++;
938 }
939
940 argvi = argv;
941 while (*(argvi++)) argc++;
942
943 if (argc) {
944 enclose_in_quotes = (int*) calloc(1, argc * sizeof(int));
945
946 if (!enclose_in_quotes) {
947 return NULL;
948 }
949 }
950
951 /* We have to make one pass through each argv[i] to see if we need
952 * to enclose it in ", so we might as well figure out how much
953 * memory we'll need on the same pass.
954 */
955
956 argvi = argv;
957 enclose_in_quotes_i = enclose_in_quotes;
958 while(*argvi) {
959 char* p = *argvi;
960 unsigned int backslash_count = 0;
961
962 /*
963 * We have to enclose empty arguments in ".
964 */
965 if (!(*p)) *enclose_in_quotes_i = 1;
966
967 while(*p) {
968 switch (*p) {
969 case '\"':
970 /*
971 * We have to insert a backslash for each "
972 * and each \ that precedes the ".
973 */
974 bytes_required += (backslash_count + 1);
975 backslash_count = 0;
976 break;
977
978#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
979 case '\\':
980 backslash_count++;
981 break;
982#endif
983 /*
984 * At one time we set *enclose_in_quotes_i for '*' or '?' to suppress
985 * wildcard expansion in programs linked with MSVC's SETARGV.OBJ so
986 * that argv in always equals argv out. This was removed. Say you have
987 * such a program named glob.exe. You enter
988 * glob '*'
989 * at the sh command prompt. Obviously the intent is to make glob do the
990 * wildcarding instead of sh. If we set *enclose_in_quotes_i for '*' or '?',
991 * then the command line that glob would see would be
992 * glob "*"
993 * and the _setargv in SETARGV.OBJ would _not_ expand the *.
994 */
995 case ' ':
996 case '\t':
997 *enclose_in_quotes_i = 1;
998 /* fall through */
999
1000 default:
1001 backslash_count = 0;
1002 break;
1003 }
1004
1005 /*
1006 * Add one for each character in argv[i].
1007 */
1008 bytes_required++;
1009
1010 p++;
1011 }
1012
1013 if (*enclose_in_quotes_i) {
1014 /*
1015 * Add one for each enclosing ",
1016 * and one for each \ that precedes the
1017 * closing ".
1018 */
1019 bytes_required += (backslash_count + 2);
1020 }
1021
1022 /*
1023 * Add one for the intervening space.
1024 */
1025 if (*(++argvi)) bytes_required++;
1026 enclose_in_quotes_i++;
1027 }
1028
1029 /*
1030 * Add one for the terminating NULL.
1031 */
1032 bytes_required++;
1033
1034 command_line = (char*) malloc(bytes_required);
1035
1036 if (!command_line) {
1037 if (enclose_in_quotes) free(enclose_in_quotes);
1038 return NULL;
1039 }
1040
1041 command_line_i = command_line;
1042
1043 if (shell_name && full_exec_path) {
1044 while(*shell_name) {
1045 *(command_line_i++) = *(shell_name++);
1046 }
1047
1048 *(command_line_i++) = ' ';
1049
1050 while(*full_exec_path) {
1051 *(command_line_i++) = *(full_exec_path++);
1052 }
1053
1054 if (*argv) {
1055 *(command_line_i++) = ' ';
1056 }
1057 }
1058
1059 argvi = argv;
1060 enclose_in_quotes_i = enclose_in_quotes;
1061
1062 while(*argvi) {
1063 char* p = *argvi;
1064 unsigned int backslash_count = 0;
1065
1066 if (*enclose_in_quotes_i) {
1067 *(command_line_i++) = '\"';
1068 }
1069
1070 while(*p) {
1071 if (*p == '\"') {
1072 if (cygwin_mode && have_sh) { /* HAVE_CYGWIN_SHELL */
1073 /* instead of a \", cygwin likes "" */
1074 *(command_line_i++) = '\"';
1075 } else {
1076
1077 /*
1078 * We have to insert a backslash for the "
1079 * and each \ that precedes the ".
1080 */
1081 backslash_count++;
1082
1083 while(backslash_count) {
1084 *(command_line_i++) = '\\';
1085 backslash_count--;
1086 };
1087 }
1088#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
1089 } else if (*p == '\\') {
1090 backslash_count++;
1091 } else {
1092 backslash_count = 0;
1093#endif
1094 }
1095
1096 /*
1097 * Copy the character.
1098 */
1099 *(command_line_i++) = *(p++);
1100 }
1101
1102 if (*enclose_in_quotes_i) {
1103#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
1104 /*
1105 * Add one \ for each \ that precedes the
1106 * closing ".
1107 */
1108 while(backslash_count--) {
1109 *(command_line_i++) = '\\';
1110 };
1111#endif
1112 *(command_line_i++) = '\"';
1113 }
1114
1115 /*
1116 * Append an intervening space.
1117 */
1118 if (*(++argvi)) {
1119 *(command_line_i++) = ' ';
1120 }
1121
1122 enclose_in_quotes_i++;
1123 }
1124
1125 /*
1126 * Append the terminating NULL.
1127 */
1128 *command_line_i = '\0';
1129
1130 if (enclose_in_quotes) free(enclose_in_quotes);
1131 return command_line;
1132}
1133
1134/*
1135 * Description: Given an argv and optional envp, launch the process
1136 * using the default stdin, stdout, and stderr handles.
1137 * Also, register process so that process_wait_for_any_private()
1138 * can be used via process_file_io(NULL) or
1139 * process_wait_for_any().
1140 *
1141 * Returns:
1142 *
1143 * Notes/Dependencies:
1144 */
1145HANDLE
1146process_easy(
1147 char **argv,
1148 char **envp)
1149{
1150 HANDLE hIn;
1151 HANDLE hOut;
1152 HANDLE hErr;
1153 HANDLE hProcess;
1154
1155 if (DuplicateHandle(GetCurrentProcess(),
1156 GetStdHandle(STD_INPUT_HANDLE),
1157 GetCurrentProcess(),
1158 &hIn,
1159 0,
1160 TRUE,
1161 DUPLICATE_SAME_ACCESS) == FALSE) {
1162 fprintf(stderr,
1163 "process_easy: DuplicateHandle(In) failed (e=%d)\n",
1164 GetLastError());
1165 return INVALID_HANDLE_VALUE;
1166 }
1167 if (DuplicateHandle(GetCurrentProcess(),
1168 GetStdHandle(STD_OUTPUT_HANDLE),
1169 GetCurrentProcess(),
1170 &hOut,
1171 0,
1172 TRUE,
1173 DUPLICATE_SAME_ACCESS) == FALSE) {
1174 fprintf(stderr,
1175 "process_easy: DuplicateHandle(Out) failed (e=%d)\n",
1176 GetLastError());
1177 return INVALID_HANDLE_VALUE;
1178 }
1179 if (DuplicateHandle(GetCurrentProcess(),
1180 GetStdHandle(STD_ERROR_HANDLE),
1181 GetCurrentProcess(),
1182 &hErr,
1183 0,
1184 TRUE,
1185 DUPLICATE_SAME_ACCESS) == FALSE) {
1186 fprintf(stderr,
1187 "process_easy: DuplicateHandle(Err) failed (e=%d)\n",
1188 GetLastError());
1189 return INVALID_HANDLE_VALUE;
1190 }
1191
1192 hProcess = process_init_fd(hIn, hOut, hErr);
1193
1194 if (process_begin(hProcess, argv, envp, argv[0], NULL)) {
1195 fake_exits_pending++;
1196 /* process_begin() failed: make a note of that. */
1197 if (!((sub_process*) hProcess)->last_err)
1198 ((sub_process*) hProcess)->last_err = -1;
1199 ((sub_process*) hProcess)->exit_code = process_last_err(hProcess);
1200
1201 /* close up unused handles */
1202 CloseHandle(hIn);
1203 CloseHandle(hOut);
1204 CloseHandle(hErr);
1205 }
1206
1207 process_register(hProcess);
1208
1209 return hProcess;
1210}
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