summaryrefslogtreecommitdiff
path: root/w32/subproc/sub_proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'w32/subproc/sub_proc.c')
-rw-r--r--w32/subproc/sub_proc.c1100
1 files changed, 1100 insertions, 0 deletions
diff --git a/w32/subproc/sub_proc.c b/w32/subproc/sub_proc.c
new file mode 100644
index 0000000..52cd9d7
--- /dev/null
+++ b/w32/subproc/sub_proc.c
@@ -0,0 +1,1100 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <process.h> /* for msvc _beginthreadex, _endthreadex */
+#include <windows.h>
+
+#include "sub_proc.h"
+#include "proc.h"
+#include "w32err.h"
+
+static char *make_command_line( char *shell_name, char *exec_path, char **argv);
+
+typedef struct sub_process_t {
+ int sv_stdin[2];
+ int sv_stdout[2];
+ int sv_stderr[2];
+ int using_pipes;
+ char *inp;
+ DWORD incnt;
+ char * volatile outp;
+ volatile DWORD outcnt;
+ char * volatile errp;
+ volatile DWORD errcnt;
+ int pid;
+ int exit_code;
+ int signal;
+ long last_err;
+ long lerrno;
+} sub_process;
+
+/* keep track of children so we can implement a waitpid-like routine */
+static sub_process *proc_array[256];
+static int proc_index = 0;
+static int fake_exits_pending = 0;
+
+/*
+ * When a process has been waited for, adjust the wait state
+ * array so that we don't wait for it again
+ */
+static void
+process_adjust_wait_state(sub_process* pproc)
+{
+ int i;
+
+ if (!proc_index)
+ return;
+
+ for (i = 0; i < proc_index; i++)
+ if (proc_array[i]->pid == pproc->pid)
+ break;
+
+ if (i < proc_index) {
+ proc_index--;
+ if (i != proc_index)
+ memmove(&proc_array[i], &proc_array[i+1],
+ (proc_index-i) * sizeof(sub_process*));
+ proc_array[proc_index] = NULL;
+ }
+}
+
+/*
+ * Waits for any of the registered child processes to finish.
+ */
+static sub_process *
+process_wait_for_any_private(void)
+{
+ HANDLE handles[256];
+ DWORD retval, which;
+ int i;
+
+ if (!proc_index)
+ return NULL;
+
+ /* build array of handles to wait for */
+ for (i = 0; i < proc_index; i++) {
+ handles[i] = (HANDLE) proc_array[i]->pid;
+
+ if (fake_exits_pending && proc_array[i]->exit_code)
+ break;
+ }
+
+ /* wait for someone to exit */
+ if (!fake_exits_pending) {
+ retval = WaitForMultipleObjects(proc_index, handles, FALSE, INFINITE);
+ which = retval - WAIT_OBJECT_0;
+ } else {
+ fake_exits_pending--;
+ retval = !WAIT_FAILED;
+ which = i;
+ }
+
+ /* return pointer to process */
+ if (retval != WAIT_FAILED) {
+ sub_process* pproc = proc_array[which];
+ process_adjust_wait_state(pproc);
+ return pproc;
+ } else
+ return NULL;
+}
+
+/*
+ * Terminate a process.
+ */
+BOOL
+process_kill(HANDLE proc, int signal)
+{
+ sub_process* pproc = (sub_process*) proc;
+ pproc->signal = signal;
+ return (TerminateProcess((HANDLE) pproc->pid, signal));
+}
+
+/*
+ * Use this function to register processes you wish to wait for by
+ * calling process_file_io(NULL) or process_wait_any(). This must be done
+ * because it is possible for callers of this library to reuse the same
+ * handle for multiple processes launches :-(
+ */
+void
+process_register(HANDLE proc)
+{
+ proc_array[proc_index++] = (sub_process *) proc;
+}
+
+/*
+ * Public function which works kind of like waitpid(). Wait for any
+ * of the children to die and return results. To call this function,
+ * you must do 1 of things:
+ *
+ * x = process_easy(...);
+ *
+ * or
+ *
+ * x = process_init_fd();
+ * process_register(x);
+ *
+ * or
+ *
+ * x = process_init();
+ * process_register(x);
+ *
+ * You must NOT then call process_pipe_io() because this function is
+ * not capable of handling automatic notification of any child
+ * death.
+ */
+
+HANDLE
+process_wait_for_any(void)
+{
+ sub_process* pproc = process_wait_for_any_private();
+
+ if (!pproc)
+ return NULL;
+ else {
+ /*
+ * Ouch! can't tell caller if this fails directly. Caller
+ * will have to use process_last_err()
+ */
+ (void) process_file_io(pproc);
+ return ((HANDLE) pproc);
+ }
+}
+
+long
+process_errno(HANDLE proc)
+{
+ return (((sub_process *)proc)->lerrno);
+}
+
+long
+process_signal(HANDLE proc)
+{
+ return (((sub_process *)proc)->signal);
+}
+
+ long
+process_last_err(HANDLE proc)
+{
+ return (((sub_process *)proc)->last_err);
+}
+
+ long
+process_exit_code(HANDLE proc)
+{
+ return (((sub_process *)proc)->exit_code);
+}
+
+ char *
+process_outbuf(HANDLE proc)
+{
+ return (((sub_process *)proc)->outp);
+}
+
+ char *
+process_errbuf(HANDLE proc)
+{
+ return (((sub_process *)proc)->errp);
+}
+
+ int
+process_outcnt(HANDLE proc)
+{
+ return (((sub_process *)proc)->outcnt);
+}
+
+ int
+process_errcnt(HANDLE proc)
+{
+ return (((sub_process *)proc)->errcnt);
+}
+
+ void
+process_pipes(HANDLE proc, int pipes[3])
+{
+ pipes[0] = ((sub_process *)proc)->sv_stdin[0];
+ pipes[1] = ((sub_process *)proc)->sv_stdout[0];
+ pipes[2] = ((sub_process *)proc)->sv_stderr[0];
+ return;
+}
+
+
+ HANDLE
+process_init()
+{
+ sub_process *pproc;
+ /*
+ * open file descriptors for attaching stdin/stdout/sterr
+ */
+ HANDLE stdin_pipes[2];
+ HANDLE stdout_pipes[2];
+ HANDLE stderr_pipes[2];
+ SECURITY_ATTRIBUTES inherit;
+ BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
+
+ pproc = malloc(sizeof(*pproc));
+ memset(pproc, 0, sizeof(*pproc));
+
+ /* We can't use NULL for lpSecurityDescriptor because that
+ uses the default security descriptor of the calling process.
+ Instead we use a security descriptor with no DACL. This
+ allows nonrestricted access to the associated objects. */
+
+ if (!InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR)(&sd),
+ SECURITY_DESCRIPTOR_REVISION)) {
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ return((HANDLE)pproc);
+ }
+
+ inherit.nLength = sizeof(inherit);
+ inherit.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)(&sd);
+ inherit.bInheritHandle = TRUE;
+
+ // By convention, parent gets pipe[0], and child gets pipe[1]
+ // This means the READ side of stdin pipe goes into pipe[1]
+ // and the WRITE side of the stdout and stderr pipes go into pipe[1]
+ if (CreatePipe( &stdin_pipes[1], &stdin_pipes[0], &inherit, 0) == FALSE ||
+ CreatePipe( &stdout_pipes[0], &stdout_pipes[1], &inherit, 0) == FALSE ||
+ CreatePipe( &stderr_pipes[0], &stderr_pipes[1], &inherit, 0) == FALSE) {
+
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ return((HANDLE)pproc);
+ }
+
+ //
+ // Mark the parent sides of the pipes as non-inheritable
+ //
+ if (SetHandleInformation(stdin_pipes[0],
+ HANDLE_FLAG_INHERIT, 0) == FALSE ||
+ SetHandleInformation(stdout_pipes[0],
+ HANDLE_FLAG_INHERIT, 0) == FALSE ||
+ SetHandleInformation(stderr_pipes[0],
+ HANDLE_FLAG_INHERIT, 0) == FALSE) {
+
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ return((HANDLE)pproc);
+ }
+ pproc->sv_stdin[0] = (int) stdin_pipes[0];
+ pproc->sv_stdin[1] = (int) stdin_pipes[1];
+ pproc->sv_stdout[0] = (int) stdout_pipes[0];
+ pproc->sv_stdout[1] = (int) stdout_pipes[1];
+ pproc->sv_stderr[0] = (int) stderr_pipes[0];
+ pproc->sv_stderr[1] = (int) stderr_pipes[1];
+
+ pproc->using_pipes = 1;
+
+ pproc->lerrno = 0;
+
+ return((HANDLE)pproc);
+}
+
+
+ HANDLE
+process_init_fd(HANDLE stdinh, HANDLE stdouth, HANDLE stderrh)
+{
+ sub_process *pproc;
+
+ pproc = malloc(sizeof(*pproc));
+ memset(pproc, 0, sizeof(*pproc));
+
+ /*
+ * Just pass the provided file handles to the 'child side' of the
+ * pipe, bypassing pipes altogether.
+ */
+ pproc->sv_stdin[1] = (int) stdinh;
+ pproc->sv_stdout[1] = (int) stdouth;
+ pproc->sv_stderr[1] = (int) stderrh;
+
+ pproc->last_err = pproc->lerrno = 0;
+
+ return((HANDLE)pproc);
+}
+
+
+static HANDLE
+find_file(char *exec_path, LPOFSTRUCT file_info)
+{
+ HANDLE exec_handle;
+ char *fname;
+ char *ext;
+
+ if ((exec_handle = (HANDLE)OpenFile(exec_path, file_info,
+ OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
+ return(exec_handle);
+ }
+
+ fname = malloc(strlen(exec_path) + 5);
+ strcpy(fname, exec_path);
+ ext = fname + strlen(fname);
+ strcpy(ext, ".exe");
+ if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
+ OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
+ free(fname);
+ return(exec_handle);
+ }
+
+ strcpy(ext, ".bat");
+ if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
+ OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
+ free(fname);
+ return(exec_handle);
+ }
+
+ strcpy(ext, ".com");
+ if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
+ OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
+ free(fname);
+ return(exec_handle);
+ }
+
+ free(fname);
+ return(exec_handle);
+}
+
+
+/*
+ * Description: Create the child process to be helped
+ *
+ * Returns:
+ *
+ * Notes/Dependencies:
+ */
+long
+process_begin(
+ HANDLE proc,
+ char **argv,
+ char **envp,
+ char *exec_path,
+ char *as_user)
+{
+ sub_process *pproc = (sub_process *)proc;
+ char *shell_name = 0;
+ int file_not_found=0;
+ HANDLE exec_handle;
+ char buf[256];
+ DWORD bytes_returned;
+ DWORD flags;
+ char *command_line;
+ STARTUPINFO startInfo;
+ PROCESS_INFORMATION procInfo;
+ char *envblk=NULL;
+ OFSTRUCT file_info;
+
+
+ /*
+ * Shell script detection... if the exec_path starts with #! then
+ * we want to exec shell-script-name exec-path, not just exec-path
+ * NT doesn't recognize #!/bin/sh or #!/etc/Tivoli/bin/perl. We do not
+ * hard-code the path to the shell or perl or whatever: Instead, we
+ * assume it's in the path somewhere (generally, the NT tools
+ * bin directory)
+ * We use OpenFile here because it is capable of searching the Path.
+ */
+
+ exec_handle = find_file(exec_path, &file_info);
+
+ /*
+ * If we couldn't open the file, just assume that Win32 will be able
+ * to find and execute it.
+ */
+ if (exec_handle == (HANDLE)HFILE_ERROR) {
+ file_not_found++;
+ }
+ else {
+ /* Attempt to read the first line of the file */
+ if (ReadFile( exec_handle,
+ buf, sizeof(buf) - 1, /* leave room for trailing NULL */
+ &bytes_returned, 0) == FALSE || bytes_returned < 2) {
+
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_IO;
+ CloseHandle(exec_handle);
+ return(-1);
+ }
+ if (buf[0] == '#' && buf[1] == '!') {
+ /*
+ * This is a shell script... Change the command line from
+ * exec_path args to shell_name exec_path args
+ */
+ char *p;
+
+ /* Make sure buf is NULL terminated */
+ buf[bytes_returned] = 0;
+ /*
+ * Depending on the file system type, etc. the first line
+ * of the shell script may end with newline or newline-carriage-return
+ * Whatever it ends with, cut it off.
+ */
+ p= strchr(buf, '\n');
+ if (p)
+ *p = 0;
+ p = strchr(buf, '\r');
+ if (p)
+ *p = 0;
+
+ /*
+ * Find base name of shell
+ */
+ shell_name = strrchr( buf, '/');
+ if (shell_name) {
+ shell_name++;
+ } else {
+ shell_name = &buf[2];/* skipping "#!" */
+ }
+
+ }
+ CloseHandle(exec_handle);
+ }
+
+ flags = 0;
+
+ if (file_not_found)
+ command_line = make_command_line( shell_name, exec_path, argv);
+ else
+ command_line = make_command_line( shell_name, file_info.szPathName,
+ argv);
+
+ if ( command_line == NULL ) {
+ pproc->last_err = 0;
+ pproc->lerrno = E_NO_MEM;
+ return(-1);
+ }
+
+ if (envp) {
+ if (arr2envblk(envp, &envblk) ==FALSE) {
+ pproc->last_err = 0;
+ pproc->lerrno = E_NO_MEM;
+ free( command_line );
+ return(-1);
+ }
+ }
+
+ if ((shell_name) || (file_not_found)) {
+ exec_path = 0; /* Search for the program in %Path% */
+ } else {
+ exec_path = file_info.szPathName;
+ }
+
+ /*
+ * Set up inherited stdin, stdout, stderr for child
+ */
+ GetStartupInfo(&startInfo);
+ startInfo.dwFlags = STARTF_USESTDHANDLES;
+ startInfo.lpReserved = 0;
+ startInfo.cbReserved2 = 0;
+ startInfo.lpReserved2 = 0;
+ startInfo.lpTitle = shell_name ? shell_name : exec_path;
+ startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1];
+ startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1];
+ startInfo.hStdError = (HANDLE)pproc->sv_stderr[1];
+
+ /*
+ * See if we need to setuid to a different user.
+ */
+ if (as_user) {
+ return -1;
+ }
+
+ if (as_user) {
+ return -1;
+ } else {
+ if (CreateProcess(
+ exec_path,
+ command_line,
+ NULL,
+ 0, /* default security attributes for thread */
+ TRUE, /* inherit handles (e.g. helper pipes, oserv socket) */
+ flags,
+ envblk,
+ 0, /* default starting directory */
+ &startInfo,
+ &procInfo) == FALSE) {
+
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_FORK;
+ fprintf(stderr, "process_begin: CreateProcess(%s, %s, ...) failed.\n", exec_path, command_line);
+ free( command_line );
+ return(-1);
+ }
+ }
+
+ pproc->pid = (int)procInfo.hProcess;
+ /* Close the thread handle -- we'll just watch the process */
+ CloseHandle(procInfo.hThread);
+
+ /* Close the halves of the pipes we don't need */
+ if (pproc->sv_stdin) {
+ CloseHandle((HANDLE)pproc->sv_stdin[1]);
+ (HANDLE)pproc->sv_stdin[1] = 0;
+ }
+ if (pproc->sv_stdout) {
+ CloseHandle((HANDLE)pproc->sv_stdout[1]);
+ (HANDLE)pproc->sv_stdout[1] = 0;
+ }
+ if (pproc->sv_stderr) {
+ CloseHandle((HANDLE)pproc->sv_stderr[1]);
+ (HANDLE)pproc->sv_stderr[1] = 0;
+ }
+
+ free( command_line );
+ pproc->lerrno=0;
+ return 0;
+}
+
+
+
+static DWORD
+proc_stdin_thread(sub_process *pproc)
+{
+ DWORD in_done;
+ for (;;) {
+ if (WriteFile( (HANDLE) pproc->sv_stdin[0], pproc->inp, pproc->incnt,
+ &in_done, NULL) == FALSE)
+ _endthreadex(0);
+ // This if should never be true for anonymous pipes, but gives
+ // us a chance to change I/O mechanisms later
+ if (in_done < pproc->incnt) {
+ pproc->incnt -= in_done;
+ pproc->inp += in_done;
+ } else {
+ _endthreadex(0);
+ }
+ }
+ return 0; // for compiler warnings only.. not reached
+}
+
+static DWORD
+proc_stdout_thread(sub_process *pproc)
+{
+ DWORD bufsize = 1024;
+ char c;
+ DWORD nread;
+ pproc->outp = malloc(bufsize);
+ if (pproc->outp == NULL)
+ _endthreadex(0);
+ pproc->outcnt = 0;
+
+ for (;;) {
+ if (ReadFile( (HANDLE)pproc->sv_stdout[0], &c, 1, &nread, NULL)
+ == FALSE) {
+/* map_win32_error_to_string(GetLastError());*/
+ _endthreadex(0);
+ }
+ if (nread == 0)
+ _endthreadex(0);
+ if (pproc->outcnt + nread > bufsize) {
+ bufsize += nread + 512;
+ pproc->outp = realloc(pproc->outp, bufsize);
+ if (pproc->outp == NULL) {
+ pproc->outcnt = 0;
+ _endthreadex(0);
+ }
+ }
+ pproc->outp[pproc->outcnt++] = c;
+ }
+ return 0;
+}
+
+static DWORD
+proc_stderr_thread(sub_process *pproc)
+{
+ DWORD bufsize = 1024;
+ char c;
+ DWORD nread;
+ pproc->errp = malloc(bufsize);
+ if (pproc->errp == NULL)
+ _endthreadex(0);
+ pproc->errcnt = 0;
+
+ for (;;) {
+ if (ReadFile( (HANDLE)pproc->sv_stderr[0], &c, 1, &nread, NULL) == FALSE) {
+ map_win32_error_to_string(GetLastError());
+ _endthreadex(0);
+ }
+ if (nread == 0)
+ _endthreadex(0);
+ if (pproc->errcnt + nread > bufsize) {
+ bufsize += nread + 512;
+ pproc->errp = realloc(pproc->errp, bufsize);
+ if (pproc->errp == NULL) {
+ pproc->errcnt = 0;
+ _endthreadex(0);
+ }
+ }
+ pproc->errp[pproc->errcnt++] = c;
+ }
+ return 0;
+}
+
+
+/*
+ * Purpose: collects output from child process and returns results
+ *
+ * Description:
+ *
+ * Returns:
+ *
+ * Notes/Dependencies:
+ */
+ long
+process_pipe_io(
+ HANDLE proc,
+ char *stdin_data,
+ int stdin_data_len)
+{
+ sub_process *pproc = (sub_process *)proc;
+ bool_t stdin_eof = FALSE, stdout_eof = FALSE, stderr_eof = FALSE;
+ HANDLE childhand = (HANDLE) pproc->pid;
+ HANDLE tStdin, tStdout, tStderr;
+ DWORD dwStdin, dwStdout, dwStderr;
+ HANDLE wait_list[4];
+ DWORD wait_count;
+ DWORD wait_return;
+ HANDLE ready_hand;
+ bool_t child_dead = FALSE;
+
+
+ /*
+ * Create stdin thread, if needed
+ */
+ pproc->inp = stdin_data;
+ pproc->incnt = stdin_data_len;
+ if (!pproc->inp) {
+ stdin_eof = TRUE;
+ CloseHandle((HANDLE)pproc->sv_stdin[0]);
+ (HANDLE)pproc->sv_stdin[0] = 0;
+ } else {
+ tStdin = (HANDLE) _beginthreadex( 0, 1024,
+ (unsigned (__stdcall *) (void *))proc_stdin_thread, pproc, 0,
+ (unsigned int *) &dwStdin);
+ if (tStdin == 0) {
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done;
+ }
+ }
+
+ /*
+ * Assume child will produce stdout and stderr
+ */
+ tStdout = (HANDLE) _beginthreadex( 0, 1024,
+ (unsigned (__stdcall *) (void *))proc_stdout_thread, pproc, 0,
+ (unsigned int *) &dwStdout);
+ tStderr = (HANDLE) _beginthreadex( 0, 1024,
+ (unsigned (__stdcall *) (void *))proc_stderr_thread, pproc, 0,
+ (unsigned int *) &dwStderr);
+
+ if (tStdout == 0 || tStderr == 0) {
+
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done;
+ }
+
+
+ /*
+ * Wait for all I/O to finish and for the child process to exit
+ */
+
+ while (!stdin_eof || !stdout_eof || !stderr_eof || !child_dead) {
+ wait_count = 0;
+ if (!stdin_eof) {
+ wait_list[wait_count++] = tStdin;
+ }
+ if (!stdout_eof) {
+ wait_list[wait_count++] = tStdout;
+ }
+ if (!stderr_eof) {
+ wait_list[wait_count++] = tStderr;
+ }
+ if (!child_dead) {
+ wait_list[wait_count++] = childhand;
+ }
+
+ wait_return = WaitForMultipleObjects(wait_count, wait_list,
+ FALSE, /* don't wait for all: one ready will do */
+ child_dead? 1000 :INFINITE); /* after the child dies, subthreads have
+ one second to collect all remaining output */
+
+ if (wait_return == WAIT_FAILED) {
+/* map_win32_error_to_string(GetLastError());*/
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done;
+ }
+
+ ready_hand = wait_list[wait_return - WAIT_OBJECT_0];
+
+ if (ready_hand == tStdin) {
+ CloseHandle((HANDLE)pproc->sv_stdin[0]);
+ (HANDLE)pproc->sv_stdin[0] = 0;
+ CloseHandle(tStdin);
+ tStdin = 0;
+ stdin_eof = TRUE;
+
+ } else if (ready_hand == tStdout) {
+
+ CloseHandle((HANDLE)pproc->sv_stdout[0]);
+ (HANDLE)pproc->sv_stdout[0] = 0;
+ CloseHandle(tStdout);
+ tStdout = 0;
+ stdout_eof = TRUE;
+
+ } else if (ready_hand == tStderr) {
+
+ CloseHandle((HANDLE)pproc->sv_stderr[0]);
+ (HANDLE)pproc->sv_stderr[0] = 0;
+ CloseHandle(tStderr);
+ tStderr = 0;
+ stderr_eof = TRUE;
+
+ } else if (ready_hand == childhand) {
+
+ if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) {
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done;
+ }
+ child_dead = TRUE;
+
+ } else {
+
+ /* ?? Got back a handle we didn't query ?? */
+ pproc->last_err = 0;
+ pproc->lerrno = E_FAIL;
+ goto done;
+ }
+ }
+
+ done:
+ if (tStdin != 0)
+ CloseHandle(tStdin);
+ if (tStdout != 0)
+ CloseHandle(tStdout);
+ if (tStderr != 0)
+ CloseHandle(tStderr);
+
+ if (pproc->lerrno)
+ return(-1);
+ else
+ return(0);
+
+}
+
+/*
+ * Purpose: collects output from child process and returns results
+ *
+ * Description:
+ *
+ * Returns:
+ *
+ * Notes/Dependencies:
+ */
+ long
+process_file_io(
+ HANDLE proc)
+{
+ sub_process *pproc;
+ HANDLE childhand;
+ DWORD wait_return;
+
+ if (proc == NULL)
+ pproc = process_wait_for_any_private();
+ else
+ pproc = (sub_process *)proc;
+
+ /* some sort of internal error */
+ if (!pproc)
+ return -1;
+
+ childhand = (HANDLE) pproc->pid;
+
+ /*
+ * This function is poorly named, and could also be used just to wait
+ * for child death if you're doing your own pipe I/O. If that is
+ * the case, close the pipe handles here.
+ */
+ if (pproc->sv_stdin[0]) {
+ CloseHandle((HANDLE)pproc->sv_stdin[0]);
+ pproc->sv_stdin[0] = 0;
+ }
+ if (pproc->sv_stdout[0]) {
+ CloseHandle((HANDLE)pproc->sv_stdout[0]);
+ pproc->sv_stdout[0] = 0;
+ }
+ if (pproc->sv_stderr[0]) {
+ CloseHandle((HANDLE)pproc->sv_stderr[0]);
+ pproc->sv_stderr[0] = 0;
+ }
+
+ /*
+ * Wait for the child process to exit
+ */
+
+ wait_return = WaitForSingleObject(childhand, INFINITE);
+
+ if (wait_return != WAIT_OBJECT_0) {
+/* map_win32_error_to_string(GetLastError());*/
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done2;
+ }
+
+ if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) {
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ }
+
+done2:
+ if (pproc->lerrno)
+ return(-1);
+ else
+ return(0);
+
+}
+
+/*
+ * Description: Clean up any leftover handles, etc. It is up to the
+ * caller to manage and free the input, ouput, and stderr buffers.
+ */
+ void
+process_cleanup(
+ HANDLE proc)
+{
+ sub_process *pproc = (sub_process *)proc;
+ int i;
+
+ if (pproc->using_pipes) {
+ for (i= 0; i <= 1; i++) {
+ if ((HANDLE)pproc->sv_stdin[i])
+ CloseHandle((HANDLE)pproc->sv_stdin[i]);
+ if ((HANDLE)pproc->sv_stdout[i])
+ CloseHandle((HANDLE)pproc->sv_stdout[i]);
+ if ((HANDLE)pproc->sv_stderr[i])
+ CloseHandle((HANDLE)pproc->sv_stderr[i]);
+ }
+ }
+ if ((HANDLE)pproc->pid)
+ CloseHandle((HANDLE)pproc->pid);
+
+ free(pproc);
+}
+
+
+/*
+ * Try to protect against WIN32 argument munging. This function takes
+ * an argv vector and outputs a 'protected' string as a return
+ * value. The return code can be safely passed to CreateProcess().
+ *
+ * The caller should free the return value.
+ */
+
+#define TRACE(x)
+static char *fix_command_line(char *args[])
+{
+ int i;
+ char *narg;
+ char *nargp;
+ char *p;
+ char *q;
+ int alloc_len = 0;
+
+ for (i = 0; args[i]; i++)
+ alloc_len += ((strlen(args[i]) * 2) + 1);
+ /* account for possible enclosing quotes and null termination */
+ alloc_len += 3;
+
+ nargp = narg = malloc(alloc_len);
+
+ for (i = 0; args[i]; i++) {
+ p = args[i];
+ TRACE(("original arg: %s\n", p));
+
+ if (*p == '\0') {
+ *nargp++ = '"';
+ *nargp++ = '"';
+ *nargp = '\0';
+ TRACE(("empty string arg: %s\n", nargp-2));
+ } else if (strpbrk(p, "\" \t")) {
+ /* point to end of copy buffer */
+ q = narg;
+ q += (alloc_len-1);
+ *q-- = '\0'; /* ensure null terminated string */
+ *q-- = '"'; /* terminating quote of argument */
+
+ /* point to end of the input string */
+ p = args[i];
+ p += strlen(args[i]);
+ p--;
+
+ /*
+ * Because arg is quoted, escape any backslashes
+ * that might occur at the end of the string which
+ * proceed the closing quote.
+ * Example:
+ * foo c:\
+ * Becomes:
+ * "foo c:\\"
+ */
+ while (*p == '\\')
+ *q-- = *p--, *q-- = '\\';
+
+ /* copy the string in reverse */
+ while (p >= args[i]) {
+ /* copy the character */
+ *q-- = *p--;
+
+ /*
+ * Escape any double quote found. Also escape
+ * each backslash preceding the double quote.
+ */
+ if (*(p+1) == '"') {
+ *q-- = '\\';
+ if (p >= args[i] && *p == '\\')
+ while (p >= args[i] && *p == '\\')
+ *q-- = *p--, *q-- = '\\';
+ }
+ }
+
+ /* finish quoting arg, q now points to complete arg */
+ *q = '"';
+
+ /* rejustify */
+ memmove(nargp, q, strlen(q) + 1);
+ TRACE(("arg with white space or doublequotes: %s\n", nargp));
+ nargp += strlen(nargp);
+ } else {
+ /* just copy the argument, no protection needed */
+ strcpy(nargp, args[i]);
+ TRACE(("plain arg: %s\n", nargp));
+ nargp += strlen(nargp);
+ }
+
+ /* separate arguments with spaces (if more args to gather) */
+ if (args[i+1])
+ *nargp++ = ' ';
+ *nargp = '\0';
+ } /* end for */
+
+ /* NULL terminate the arg list */
+ *nargp = '\0';
+
+ return (narg);
+}
+#undef TRACE
+
+/*
+ * Description:
+ * Create a command line buffer to pass to CreateProcess
+ *
+ * Returns: the buffer or NULL for failure
+ * Shell case: sh_name a:/full/path/to/script argv[1] argv[2] ...
+ * Otherwise: argv[0] argv[1] argv[2] ...
+ *
+ * Notes/Dependencies:
+ * CreateProcess does not take an argv, so this command creates a
+ * command line for the executable.
+ */
+
+static char *
+make_command_line( char *shell_name, char *exec_path, char **argv)
+{
+ char** nargv;
+ char* buf;
+ int i;
+
+ if (shell_name) {
+ for (i = 0; argv[i]; i++);
+ i += 2;
+ nargv = (char **) malloc(i * sizeof (char *));
+ nargv[0] = shell_name;
+ for (i = 1; argv[i-1]; i++)
+ nargv[i] = argv[i-1];
+ nargv[i] = NULL;
+ } else
+ nargv = argv;
+
+ /* create string suitable for CreateProcess() */
+ buf = fix_command_line(nargv);
+
+ if (shell_name)
+ free(nargv);
+
+ return buf;
+}
+
+/*
+ * Description: Given an argv and optional envp, launch the process
+ * using the default stdin, stdout, and stderr handles.
+ * Also, register process so that process_wait_for_any_private()
+ * can be used via process_file_io(NULL) or
+ * process_wait_for_any().
+ *
+ * Returns:
+ *
+ * Notes/Dependencies:
+ */
+HANDLE
+process_easy(
+ char **argv,
+ char **envp)
+{
+ HANDLE hIn;
+ HANDLE hOut;
+ HANDLE hErr;
+ HANDLE hProcess;
+
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_INPUT_HANDLE),
+ GetCurrentProcess(),
+ &hIn,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ fprintf(stderr,
+ "process_easy: DuplicateHandle(In) failed (e=%d)\n",
+ GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ GetCurrentProcess(),
+ &hOut,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ fprintf(stderr,
+ "process_easy: DuplicateHandle(Out) failed (e=%d)\n",
+ GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_ERROR_HANDLE),
+ GetCurrentProcess(),
+ &hErr,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ fprintf(stderr,
+ "process_easy: DuplicateHandle(Err) failed (e=%d)\n",
+ GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+
+ hProcess = process_init_fd(hIn, hOut, hErr);
+
+ if (process_begin(hProcess, argv, envp, argv[0], NULL)) {
+ fake_exits_pending++;
+ ((sub_process*) hProcess)->exit_code = process_last_err(hProcess);
+
+ /* close up unused handles */
+ CloseHandle(hIn);
+ CloseHandle(hOut);
+ CloseHandle(hErr);
+ }
+
+ process_register(hProcess);
+
+ return hProcess;
+}