Wed May 15 10:14:14 CDT 1996 Rob Tulloh <tulloh@tivoli.com>
[make.git] / w32 / subproc / sub_proc.c
blob52cd9d7ec7bfa30c4bd5f9a13bf2cf88eada5e36
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <process.h> /* for msvc _beginthreadex, _endthreadex */
4 #include <windows.h>
6 #include "sub_proc.h"
7 #include "proc.h"
8 #include "w32err.h"
10 static char *make_command_line( char *shell_name, char *exec_path, char **argv);
12 typedef struct sub_process_t {
13 int sv_stdin[2];
14 int sv_stdout[2];
15 int sv_stderr[2];
16 int using_pipes;
17 char *inp;
18 DWORD incnt;
19 char * volatile outp;
20 volatile DWORD outcnt;
21 char * volatile errp;
22 volatile DWORD errcnt;
23 int pid;
24 int exit_code;
25 int signal;
26 long last_err;
27 long lerrno;
28 } sub_process;
30 /* keep track of children so we can implement a waitpid-like routine */
31 static sub_process *proc_array[256];
32 static int proc_index = 0;
33 static int fake_exits_pending = 0;
36 * When a process has been waited for, adjust the wait state
37 * array so that we don't wait for it again
39 static void
40 process_adjust_wait_state(sub_process* pproc)
42 int i;
44 if (!proc_index)
45 return;
47 for (i = 0; i < proc_index; i++)
48 if (proc_array[i]->pid == pproc->pid)
49 break;
51 if (i < proc_index) {
52 proc_index--;
53 if (i != proc_index)
54 memmove(&proc_array[i], &proc_array[i+1],
55 (proc_index-i) * sizeof(sub_process*));
56 proc_array[proc_index] = NULL;
61 * Waits for any of the registered child processes to finish.
63 static sub_process *
64 process_wait_for_any_private(void)
66 HANDLE handles[256];
67 DWORD retval, which;
68 int i;
70 if (!proc_index)
71 return NULL;
73 /* build array of handles to wait for */
74 for (i = 0; i < proc_index; i++) {
75 handles[i] = (HANDLE) proc_array[i]->pid;
77 if (fake_exits_pending && proc_array[i]->exit_code)
78 break;
81 /* wait for someone to exit */
82 if (!fake_exits_pending) {
83 retval = WaitForMultipleObjects(proc_index, handles, FALSE, INFINITE);
84 which = retval - WAIT_OBJECT_0;
85 } else {
86 fake_exits_pending--;
87 retval = !WAIT_FAILED;
88 which = i;
91 /* return pointer to process */
92 if (retval != WAIT_FAILED) {
93 sub_process* pproc = proc_array[which];
94 process_adjust_wait_state(pproc);
95 return pproc;
96 } else
97 return NULL;
101 * Terminate a process.
103 BOOL
104 process_kill(HANDLE proc, int signal)
106 sub_process* pproc = (sub_process*) proc;
107 pproc->signal = signal;
108 return (TerminateProcess((HANDLE) pproc->pid, signal));
112 * Use this function to register processes you wish to wait for by
113 * calling process_file_io(NULL) or process_wait_any(). This must be done
114 * because it is possible for callers of this library to reuse the same
115 * handle for multiple processes launches :-(
117 void
118 process_register(HANDLE proc)
120 proc_array[proc_index++] = (sub_process *) proc;
124 * Public function which works kind of like waitpid(). Wait for any
125 * of the children to die and return results. To call this function,
126 * you must do 1 of things:
128 * x = process_easy(...);
130 * or
132 * x = process_init_fd();
133 * process_register(x);
135 * or
137 * x = process_init();
138 * process_register(x);
140 * You must NOT then call process_pipe_io() because this function is
141 * not capable of handling automatic notification of any child
142 * death.
145 HANDLE
146 process_wait_for_any(void)
148 sub_process* pproc = process_wait_for_any_private();
150 if (!pproc)
151 return NULL;
152 else {
154 * Ouch! can't tell caller if this fails directly. Caller
155 * will have to use process_last_err()
157 (void) process_file_io(pproc);
158 return ((HANDLE) pproc);
162 long
163 process_errno(HANDLE proc)
165 return (((sub_process *)proc)->lerrno);
168 long
169 process_signal(HANDLE proc)
171 return (((sub_process *)proc)->signal);
174 long
175 process_last_err(HANDLE proc)
177 return (((sub_process *)proc)->last_err);
180 long
181 process_exit_code(HANDLE proc)
183 return (((sub_process *)proc)->exit_code);
186 char *
187 process_outbuf(HANDLE proc)
189 return (((sub_process *)proc)->outp);
192 char *
193 process_errbuf(HANDLE proc)
195 return (((sub_process *)proc)->errp);
199 process_outcnt(HANDLE proc)
201 return (((sub_process *)proc)->outcnt);
205 process_errcnt(HANDLE proc)
207 return (((sub_process *)proc)->errcnt);
210 void
211 process_pipes(HANDLE proc, int pipes[3])
213 pipes[0] = ((sub_process *)proc)->sv_stdin[0];
214 pipes[1] = ((sub_process *)proc)->sv_stdout[0];
215 pipes[2] = ((sub_process *)proc)->sv_stderr[0];
216 return;
220 HANDLE
221 process_init()
223 sub_process *pproc;
225 * open file descriptors for attaching stdin/stdout/sterr
227 HANDLE stdin_pipes[2];
228 HANDLE stdout_pipes[2];
229 HANDLE stderr_pipes[2];
230 SECURITY_ATTRIBUTES inherit;
231 BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
233 pproc = malloc(sizeof(*pproc));
234 memset(pproc, 0, sizeof(*pproc));
236 /* We can't use NULL for lpSecurityDescriptor because that
237 uses the default security descriptor of the calling process.
238 Instead we use a security descriptor with no DACL. This
239 allows nonrestricted access to the associated objects. */
241 if (!InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR)(&sd),
242 SECURITY_DESCRIPTOR_REVISION)) {
243 pproc->last_err = GetLastError();
244 pproc->lerrno = E_SCALL;
245 return((HANDLE)pproc);
248 inherit.nLength = sizeof(inherit);
249 inherit.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)(&sd);
250 inherit.bInheritHandle = TRUE;
252 // By convention, parent gets pipe[0], and child gets pipe[1]
253 // This means the READ side of stdin pipe goes into pipe[1]
254 // and the WRITE side of the stdout and stderr pipes go into pipe[1]
255 if (CreatePipe( &stdin_pipes[1], &stdin_pipes[0], &inherit, 0) == FALSE ||
256 CreatePipe( &stdout_pipes[0], &stdout_pipes[1], &inherit, 0) == FALSE ||
257 CreatePipe( &stderr_pipes[0], &stderr_pipes[1], &inherit, 0) == FALSE) {
259 pproc->last_err = GetLastError();
260 pproc->lerrno = E_SCALL;
261 return((HANDLE)pproc);
265 // Mark the parent sides of the pipes as non-inheritable
267 if (SetHandleInformation(stdin_pipes[0],
268 HANDLE_FLAG_INHERIT, 0) == FALSE ||
269 SetHandleInformation(stdout_pipes[0],
270 HANDLE_FLAG_INHERIT, 0) == FALSE ||
271 SetHandleInformation(stderr_pipes[0],
272 HANDLE_FLAG_INHERIT, 0) == FALSE) {
274 pproc->last_err = GetLastError();
275 pproc->lerrno = E_SCALL;
276 return((HANDLE)pproc);
278 pproc->sv_stdin[0] = (int) stdin_pipes[0];
279 pproc->sv_stdin[1] = (int) stdin_pipes[1];
280 pproc->sv_stdout[0] = (int) stdout_pipes[0];
281 pproc->sv_stdout[1] = (int) stdout_pipes[1];
282 pproc->sv_stderr[0] = (int) stderr_pipes[0];
283 pproc->sv_stderr[1] = (int) stderr_pipes[1];
285 pproc->using_pipes = 1;
287 pproc->lerrno = 0;
289 return((HANDLE)pproc);
293 HANDLE
294 process_init_fd(HANDLE stdinh, HANDLE stdouth, HANDLE stderrh)
296 sub_process *pproc;
298 pproc = malloc(sizeof(*pproc));
299 memset(pproc, 0, sizeof(*pproc));
302 * Just pass the provided file handles to the 'child side' of the
303 * pipe, bypassing pipes altogether.
305 pproc->sv_stdin[1] = (int) stdinh;
306 pproc->sv_stdout[1] = (int) stdouth;
307 pproc->sv_stderr[1] = (int) stderrh;
309 pproc->last_err = pproc->lerrno = 0;
311 return((HANDLE)pproc);
315 static HANDLE
316 find_file(char *exec_path, LPOFSTRUCT file_info)
318 HANDLE exec_handle;
319 char *fname;
320 char *ext;
322 if ((exec_handle = (HANDLE)OpenFile(exec_path, file_info,
323 OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
324 return(exec_handle);
327 fname = malloc(strlen(exec_path) + 5);
328 strcpy(fname, exec_path);
329 ext = fname + strlen(fname);
330 strcpy(ext, ".exe");
331 if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
332 OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
333 free(fname);
334 return(exec_handle);
337 strcpy(ext, ".bat");
338 if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
339 OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
340 free(fname);
341 return(exec_handle);
344 strcpy(ext, ".com");
345 if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
346 OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
347 free(fname);
348 return(exec_handle);
351 free(fname);
352 return(exec_handle);
357 * Description: Create the child process to be helped
359 * Returns:
361 * Notes/Dependencies:
363 long
364 process_begin(
365 HANDLE proc,
366 char **argv,
367 char **envp,
368 char *exec_path,
369 char *as_user)
371 sub_process *pproc = (sub_process *)proc;
372 char *shell_name = 0;
373 int file_not_found=0;
374 HANDLE exec_handle;
375 char buf[256];
376 DWORD bytes_returned;
377 DWORD flags;
378 char *command_line;
379 STARTUPINFO startInfo;
380 PROCESS_INFORMATION procInfo;
381 char *envblk=NULL;
382 OFSTRUCT file_info;
386 * Shell script detection... if the exec_path starts with #! then
387 * we want to exec shell-script-name exec-path, not just exec-path
388 * NT doesn't recognize #!/bin/sh or #!/etc/Tivoli/bin/perl. We do not
389 * hard-code the path to the shell or perl or whatever: Instead, we
390 * assume it's in the path somewhere (generally, the NT tools
391 * bin directory)
392 * We use OpenFile here because it is capable of searching the Path.
395 exec_handle = find_file(exec_path, &file_info);
398 * If we couldn't open the file, just assume that Win32 will be able
399 * to find and execute it.
401 if (exec_handle == (HANDLE)HFILE_ERROR) {
402 file_not_found++;
404 else {
405 /* Attempt to read the first line of the file */
406 if (ReadFile( exec_handle,
407 buf, sizeof(buf) - 1, /* leave room for trailing NULL */
408 &bytes_returned, 0) == FALSE || bytes_returned < 2) {
410 pproc->last_err = GetLastError();
411 pproc->lerrno = E_IO;
412 CloseHandle(exec_handle);
413 return(-1);
415 if (buf[0] == '#' && buf[1] == '!') {
417 * This is a shell script... Change the command line from
418 * exec_path args to shell_name exec_path args
420 char *p;
422 /* Make sure buf is NULL terminated */
423 buf[bytes_returned] = 0;
425 * Depending on the file system type, etc. the first line
426 * of the shell script may end with newline or newline-carriage-return
427 * Whatever it ends with, cut it off.
429 p= strchr(buf, '\n');
430 if (p)
431 *p = 0;
432 p = strchr(buf, '\r');
433 if (p)
434 *p = 0;
437 * Find base name of shell
439 shell_name = strrchr( buf, '/');
440 if (shell_name) {
441 shell_name++;
442 } else {
443 shell_name = &buf[2];/* skipping "#!" */
447 CloseHandle(exec_handle);
450 flags = 0;
452 if (file_not_found)
453 command_line = make_command_line( shell_name, exec_path, argv);
454 else
455 command_line = make_command_line( shell_name, file_info.szPathName,
456 argv);
458 if ( command_line == NULL ) {
459 pproc->last_err = 0;
460 pproc->lerrno = E_NO_MEM;
461 return(-1);
464 if (envp) {
465 if (arr2envblk(envp, &envblk) ==FALSE) {
466 pproc->last_err = 0;
467 pproc->lerrno = E_NO_MEM;
468 free( command_line );
469 return(-1);
473 if ((shell_name) || (file_not_found)) {
474 exec_path = 0; /* Search for the program in %Path% */
475 } else {
476 exec_path = file_info.szPathName;
480 * Set up inherited stdin, stdout, stderr for child
482 GetStartupInfo(&startInfo);
483 startInfo.dwFlags = STARTF_USESTDHANDLES;
484 startInfo.lpReserved = 0;
485 startInfo.cbReserved2 = 0;
486 startInfo.lpReserved2 = 0;
487 startInfo.lpTitle = shell_name ? shell_name : exec_path;
488 startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1];
489 startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1];
490 startInfo.hStdError = (HANDLE)pproc->sv_stderr[1];
493 * See if we need to setuid to a different user.
495 if (as_user) {
496 return -1;
499 if (as_user) {
500 return -1;
501 } else {
502 if (CreateProcess(
503 exec_path,
504 command_line,
505 NULL,
506 0, /* default security attributes for thread */
507 TRUE, /* inherit handles (e.g. helper pipes, oserv socket) */
508 flags,
509 envblk,
510 0, /* default starting directory */
511 &startInfo,
512 &procInfo) == FALSE) {
514 pproc->last_err = GetLastError();
515 pproc->lerrno = E_FORK;
516 fprintf(stderr, "process_begin: CreateProcess(%s, %s, ...) failed.\n", exec_path, command_line);
517 free( command_line );
518 return(-1);
522 pproc->pid = (int)procInfo.hProcess;
523 /* Close the thread handle -- we'll just watch the process */
524 CloseHandle(procInfo.hThread);
526 /* Close the halves of the pipes we don't need */
527 if (pproc->sv_stdin) {
528 CloseHandle((HANDLE)pproc->sv_stdin[1]);
529 (HANDLE)pproc->sv_stdin[1] = 0;
531 if (pproc->sv_stdout) {
532 CloseHandle((HANDLE)pproc->sv_stdout[1]);
533 (HANDLE)pproc->sv_stdout[1] = 0;
535 if (pproc->sv_stderr) {
536 CloseHandle((HANDLE)pproc->sv_stderr[1]);
537 (HANDLE)pproc->sv_stderr[1] = 0;
540 free( command_line );
541 pproc->lerrno=0;
542 return 0;
547 static DWORD
548 proc_stdin_thread(sub_process *pproc)
550 DWORD in_done;
551 for (;;) {
552 if (WriteFile( (HANDLE) pproc->sv_stdin[0], pproc->inp, pproc->incnt,
553 &in_done, NULL) == FALSE)
554 _endthreadex(0);
555 // This if should never be true for anonymous pipes, but gives
556 // us a chance to change I/O mechanisms later
557 if (in_done < pproc->incnt) {
558 pproc->incnt -= in_done;
559 pproc->inp += in_done;
560 } else {
561 _endthreadex(0);
564 return 0; // for compiler warnings only.. not reached
567 static DWORD
568 proc_stdout_thread(sub_process *pproc)
570 DWORD bufsize = 1024;
571 char c;
572 DWORD nread;
573 pproc->outp = malloc(bufsize);
574 if (pproc->outp == NULL)
575 _endthreadex(0);
576 pproc->outcnt = 0;
578 for (;;) {
579 if (ReadFile( (HANDLE)pproc->sv_stdout[0], &c, 1, &nread, NULL)
580 == FALSE) {
581 /* map_win32_error_to_string(GetLastError());*/
582 _endthreadex(0);
584 if (nread == 0)
585 _endthreadex(0);
586 if (pproc->outcnt + nread > bufsize) {
587 bufsize += nread + 512;
588 pproc->outp = realloc(pproc->outp, bufsize);
589 if (pproc->outp == NULL) {
590 pproc->outcnt = 0;
591 _endthreadex(0);
594 pproc->outp[pproc->outcnt++] = c;
596 return 0;
599 static DWORD
600 proc_stderr_thread(sub_process *pproc)
602 DWORD bufsize = 1024;
603 char c;
604 DWORD nread;
605 pproc->errp = malloc(bufsize);
606 if (pproc->errp == NULL)
607 _endthreadex(0);
608 pproc->errcnt = 0;
610 for (;;) {
611 if (ReadFile( (HANDLE)pproc->sv_stderr[0], &c, 1, &nread, NULL) == FALSE) {
612 map_win32_error_to_string(GetLastError());
613 _endthreadex(0);
615 if (nread == 0)
616 _endthreadex(0);
617 if (pproc->errcnt + nread > bufsize) {
618 bufsize += nread + 512;
619 pproc->errp = realloc(pproc->errp, bufsize);
620 if (pproc->errp == NULL) {
621 pproc->errcnt = 0;
622 _endthreadex(0);
625 pproc->errp[pproc->errcnt++] = c;
627 return 0;
632 * Purpose: collects output from child process and returns results
634 * Description:
636 * Returns:
638 * Notes/Dependencies:
640 long
641 process_pipe_io(
642 HANDLE proc,
643 char *stdin_data,
644 int stdin_data_len)
646 sub_process *pproc = (sub_process *)proc;
647 bool_t stdin_eof = FALSE, stdout_eof = FALSE, stderr_eof = FALSE;
648 HANDLE childhand = (HANDLE) pproc->pid;
649 HANDLE tStdin, tStdout, tStderr;
650 DWORD dwStdin, dwStdout, dwStderr;
651 HANDLE wait_list[4];
652 DWORD wait_count;
653 DWORD wait_return;
654 HANDLE ready_hand;
655 bool_t child_dead = FALSE;
659 * Create stdin thread, if needed
661 pproc->inp = stdin_data;
662 pproc->incnt = stdin_data_len;
663 if (!pproc->inp) {
664 stdin_eof = TRUE;
665 CloseHandle((HANDLE)pproc->sv_stdin[0]);
666 (HANDLE)pproc->sv_stdin[0] = 0;
667 } else {
668 tStdin = (HANDLE) _beginthreadex( 0, 1024,
669 (unsigned (__stdcall *) (void *))proc_stdin_thread, pproc, 0,
670 (unsigned int *) &dwStdin);
671 if (tStdin == 0) {
672 pproc->last_err = GetLastError();
673 pproc->lerrno = E_SCALL;
674 goto done;
679 * Assume child will produce stdout and stderr
681 tStdout = (HANDLE) _beginthreadex( 0, 1024,
682 (unsigned (__stdcall *) (void *))proc_stdout_thread, pproc, 0,
683 (unsigned int *) &dwStdout);
684 tStderr = (HANDLE) _beginthreadex( 0, 1024,
685 (unsigned (__stdcall *) (void *))proc_stderr_thread, pproc, 0,
686 (unsigned int *) &dwStderr);
688 if (tStdout == 0 || tStderr == 0) {
690 pproc->last_err = GetLastError();
691 pproc->lerrno = E_SCALL;
692 goto done;
697 * Wait for all I/O to finish and for the child process to exit
700 while (!stdin_eof || !stdout_eof || !stderr_eof || !child_dead) {
701 wait_count = 0;
702 if (!stdin_eof) {
703 wait_list[wait_count++] = tStdin;
705 if (!stdout_eof) {
706 wait_list[wait_count++] = tStdout;
708 if (!stderr_eof) {
709 wait_list[wait_count++] = tStderr;
711 if (!child_dead) {
712 wait_list[wait_count++] = childhand;
715 wait_return = WaitForMultipleObjects(wait_count, wait_list,
716 FALSE, /* don't wait for all: one ready will do */
717 child_dead? 1000 :INFINITE); /* after the child dies, subthreads have
718 one second to collect all remaining output */
720 if (wait_return == WAIT_FAILED) {
721 /* map_win32_error_to_string(GetLastError());*/
722 pproc->last_err = GetLastError();
723 pproc->lerrno = E_SCALL;
724 goto done;
727 ready_hand = wait_list[wait_return - WAIT_OBJECT_0];
729 if (ready_hand == tStdin) {
730 CloseHandle((HANDLE)pproc->sv_stdin[0]);
731 (HANDLE)pproc->sv_stdin[0] = 0;
732 CloseHandle(tStdin);
733 tStdin = 0;
734 stdin_eof = TRUE;
736 } else if (ready_hand == tStdout) {
738 CloseHandle((HANDLE)pproc->sv_stdout[0]);
739 (HANDLE)pproc->sv_stdout[0] = 0;
740 CloseHandle(tStdout);
741 tStdout = 0;
742 stdout_eof = TRUE;
744 } else if (ready_hand == tStderr) {
746 CloseHandle((HANDLE)pproc->sv_stderr[0]);
747 (HANDLE)pproc->sv_stderr[0] = 0;
748 CloseHandle(tStderr);
749 tStderr = 0;
750 stderr_eof = TRUE;
752 } else if (ready_hand == childhand) {
754 if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) {
755 pproc->last_err = GetLastError();
756 pproc->lerrno = E_SCALL;
757 goto done;
759 child_dead = TRUE;
761 } else {
763 /* ?? Got back a handle we didn't query ?? */
764 pproc->last_err = 0;
765 pproc->lerrno = E_FAIL;
766 goto done;
770 done:
771 if (tStdin != 0)
772 CloseHandle(tStdin);
773 if (tStdout != 0)
774 CloseHandle(tStdout);
775 if (tStderr != 0)
776 CloseHandle(tStderr);
778 if (pproc->lerrno)
779 return(-1);
780 else
781 return(0);
786 * Purpose: collects output from child process and returns results
788 * Description:
790 * Returns:
792 * Notes/Dependencies:
794 long
795 process_file_io(
796 HANDLE proc)
798 sub_process *pproc;
799 HANDLE childhand;
800 DWORD wait_return;
802 if (proc == NULL)
803 pproc = process_wait_for_any_private();
804 else
805 pproc = (sub_process *)proc;
807 /* some sort of internal error */
808 if (!pproc)
809 return -1;
811 childhand = (HANDLE) pproc->pid;
814 * This function is poorly named, and could also be used just to wait
815 * for child death if you're doing your own pipe I/O. If that is
816 * the case, close the pipe handles here.
818 if (pproc->sv_stdin[0]) {
819 CloseHandle((HANDLE)pproc->sv_stdin[0]);
820 pproc->sv_stdin[0] = 0;
822 if (pproc->sv_stdout[0]) {
823 CloseHandle((HANDLE)pproc->sv_stdout[0]);
824 pproc->sv_stdout[0] = 0;
826 if (pproc->sv_stderr[0]) {
827 CloseHandle((HANDLE)pproc->sv_stderr[0]);
828 pproc->sv_stderr[0] = 0;
832 * Wait for the child process to exit
835 wait_return = WaitForSingleObject(childhand, INFINITE);
837 if (wait_return != WAIT_OBJECT_0) {
838 /* map_win32_error_to_string(GetLastError());*/
839 pproc->last_err = GetLastError();
840 pproc->lerrno = E_SCALL;
841 goto done2;
844 if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) {
845 pproc->last_err = GetLastError();
846 pproc->lerrno = E_SCALL;
849 done2:
850 if (pproc->lerrno)
851 return(-1);
852 else
853 return(0);
858 * Description: Clean up any leftover handles, etc. It is up to the
859 * caller to manage and free the input, ouput, and stderr buffers.
861 void
862 process_cleanup(
863 HANDLE proc)
865 sub_process *pproc = (sub_process *)proc;
866 int i;
868 if (pproc->using_pipes) {
869 for (i= 0; i <= 1; i++) {
870 if ((HANDLE)pproc->sv_stdin[i])
871 CloseHandle((HANDLE)pproc->sv_stdin[i]);
872 if ((HANDLE)pproc->sv_stdout[i])
873 CloseHandle((HANDLE)pproc->sv_stdout[i]);
874 if ((HANDLE)pproc->sv_stderr[i])
875 CloseHandle((HANDLE)pproc->sv_stderr[i]);
878 if ((HANDLE)pproc->pid)
879 CloseHandle((HANDLE)pproc->pid);
881 free(pproc);
886 * Try to protect against WIN32 argument munging. This function takes
887 * an argv vector and outputs a 'protected' string as a return
888 * value. The return code can be safely passed to CreateProcess().
890 * The caller should free the return value.
893 #define TRACE(x)
894 static char *fix_command_line(char *args[])
896 int i;
897 char *narg;
898 char *nargp;
899 char *p;
900 char *q;
901 int alloc_len = 0;
903 for (i = 0; args[i]; i++)
904 alloc_len += ((strlen(args[i]) * 2) + 1);
905 /* account for possible enclosing quotes and null termination */
906 alloc_len += 3;
908 nargp = narg = malloc(alloc_len);
910 for (i = 0; args[i]; i++) {
911 p = args[i];
912 TRACE(("original arg: %s\n", p));
914 if (*p == '\0') {
915 *nargp++ = '"';
916 *nargp++ = '"';
917 *nargp = '\0';
918 TRACE(("empty string arg: %s\n", nargp-2));
919 } else if (strpbrk(p, "\" \t")) {
920 /* point to end of copy buffer */
921 q = narg;
922 q += (alloc_len-1);
923 *q-- = '\0'; /* ensure null terminated string */
924 *q-- = '"'; /* terminating quote of argument */
926 /* point to end of the input string */
927 p = args[i];
928 p += strlen(args[i]);
929 p--;
932 * Because arg is quoted, escape any backslashes
933 * that might occur at the end of the string which
934 * proceed the closing quote.
935 * Example:
936 * foo c:\
937 * Becomes:
938 * "foo c:\\"
940 while (*p == '\\')
941 *q-- = *p--, *q-- = '\\';
943 /* copy the string in reverse */
944 while (p >= args[i]) {
945 /* copy the character */
946 *q-- = *p--;
949 * Escape any double quote found. Also escape
950 * each backslash preceding the double quote.
952 if (*(p+1) == '"') {
953 *q-- = '\\';
954 if (p >= args[i] && *p == '\\')
955 while (p >= args[i] && *p == '\\')
956 *q-- = *p--, *q-- = '\\';
960 /* finish quoting arg, q now points to complete arg */
961 *q = '"';
963 /* rejustify */
964 memmove(nargp, q, strlen(q) + 1);
965 TRACE(("arg with white space or doublequotes: %s\n", nargp));
966 nargp += strlen(nargp);
967 } else {
968 /* just copy the argument, no protection needed */
969 strcpy(nargp, args[i]);
970 TRACE(("plain arg: %s\n", nargp));
971 nargp += strlen(nargp);
974 /* separate arguments with spaces (if more args to gather) */
975 if (args[i+1])
976 *nargp++ = ' ';
977 *nargp = '\0';
978 } /* end for */
980 /* NULL terminate the arg list */
981 *nargp = '\0';
983 return (narg);
985 #undef TRACE
988 * Description:
989 * Create a command line buffer to pass to CreateProcess
991 * Returns: the buffer or NULL for failure
992 * Shell case: sh_name a:/full/path/to/script argv[1] argv[2] ...
993 * Otherwise: argv[0] argv[1] argv[2] ...
995 * Notes/Dependencies:
996 * CreateProcess does not take an argv, so this command creates a
997 * command line for the executable.
1000 static char *
1001 make_command_line( char *shell_name, char *exec_path, char **argv)
1003 char** nargv;
1004 char* buf;
1005 int i;
1007 if (shell_name) {
1008 for (i = 0; argv[i]; i++);
1009 i += 2;
1010 nargv = (char **) malloc(i * sizeof (char *));
1011 nargv[0] = shell_name;
1012 for (i = 1; argv[i-1]; i++)
1013 nargv[i] = argv[i-1];
1014 nargv[i] = NULL;
1015 } else
1016 nargv = argv;
1018 /* create string suitable for CreateProcess() */
1019 buf = fix_command_line(nargv);
1021 if (shell_name)
1022 free(nargv);
1024 return buf;
1028 * Description: Given an argv and optional envp, launch the process
1029 * using the default stdin, stdout, and stderr handles.
1030 * Also, register process so that process_wait_for_any_private()
1031 * can be used via process_file_io(NULL) or
1032 * process_wait_for_any().
1034 * Returns:
1036 * Notes/Dependencies:
1038 HANDLE
1039 process_easy(
1040 char **argv,
1041 char **envp)
1043 HANDLE hIn;
1044 HANDLE hOut;
1045 HANDLE hErr;
1046 HANDLE hProcess;
1048 if (DuplicateHandle(GetCurrentProcess(),
1049 GetStdHandle(STD_INPUT_HANDLE),
1050 GetCurrentProcess(),
1051 &hIn,
1053 TRUE,
1054 DUPLICATE_SAME_ACCESS) == FALSE) {
1055 fprintf(stderr,
1056 "process_easy: DuplicateHandle(In) failed (e=%d)\n",
1057 GetLastError());
1058 return INVALID_HANDLE_VALUE;
1060 if (DuplicateHandle(GetCurrentProcess(),
1061 GetStdHandle(STD_OUTPUT_HANDLE),
1062 GetCurrentProcess(),
1063 &hOut,
1065 TRUE,
1066 DUPLICATE_SAME_ACCESS) == FALSE) {
1067 fprintf(stderr,
1068 "process_easy: DuplicateHandle(Out) failed (e=%d)\n",
1069 GetLastError());
1070 return INVALID_HANDLE_VALUE;
1072 if (DuplicateHandle(GetCurrentProcess(),
1073 GetStdHandle(STD_ERROR_HANDLE),
1074 GetCurrentProcess(),
1075 &hErr,
1077 TRUE,
1078 DUPLICATE_SAME_ACCESS) == FALSE) {
1079 fprintf(stderr,
1080 "process_easy: DuplicateHandle(Err) failed (e=%d)\n",
1081 GetLastError());
1082 return INVALID_HANDLE_VALUE;
1085 hProcess = process_init_fd(hIn, hOut, hErr);
1087 if (process_begin(hProcess, argv, envp, argv[0], NULL)) {
1088 fake_exits_pending++;
1089 ((sub_process*) hProcess)->exit_code = process_last_err(hProcess);
1091 /* close up unused handles */
1092 CloseHandle(hIn);
1093 CloseHandle(hOut);
1094 CloseHandle(hErr);
1097 process_register(hProcess);
1099 return hProcess;