VirtualBox

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

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

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

  • 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.

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