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.c1561
1 files changed, 1561 insertions, 0 deletions
diff --git a/w32/subproc/sub_proc.c b/w32/subproc/sub_proc.c
new file mode 100644
index 0000000..5496c7e
--- /dev/null
+++ b/w32/subproc/sub_proc.c
@@ -0,0 +1,1561 @@
+/* Process handling for Windows.
+Copyright (C) 1996-2014 Free Software Foundation, Inc.
+This file is part of GNU Make.
+
+GNU Make is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 3 of the License, or (at your option) any later
+version.
+
+GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h> /* for _get_osfhandle */
+#ifdef _MSC_VER
+# include <stddef.h> /* for intptr_t */
+#else
+# include <stdint.h>
+#endif
+#include <string.h>
+#include <process.h> /* for msvc _beginthreadex, _endthreadex */
+#include <signal.h>
+#include <windows.h>
+
+#include "makeint.h"
+#include "filedef.h"
+#include "variable.h"
+#include "sub_proc.h"
+#include "proc.h"
+#include "w32err.h"
+#include "debug.h"
+
+static char *make_command_line(char *shell_name, char *exec_path, char **argv);
+
+typedef struct sub_process_t {
+ intptr_t sv_stdin[2];
+ intptr_t sv_stdout[2];
+ intptr_t sv_stderr[2];
+ int using_pipes;
+ char *inp;
+ DWORD incnt;
+ char * volatile outp;
+ volatile DWORD outcnt;
+ char * volatile errp;
+ volatile DWORD errcnt;
+ pid_t 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[MAXIMUM_WAIT_OBJECTS];
+static int proc_index = 0;
+static int fake_exits_pending = 0;
+
+/* Windows jobserver implementation variables */
+static char jobserver_semaphore_name[MAX_PATH + 1];
+static HANDLE jobserver_semaphore = NULL;
+
+/* Open existing jobserver semaphore */
+int open_jobserver_semaphore(const char* name)
+{
+ jobserver_semaphore = OpenSemaphore(
+ SEMAPHORE_ALL_ACCESS, // Semaphore access setting
+ FALSE, // Child processes DON'T inherit
+ name); // Semaphore name
+
+ if (jobserver_semaphore == NULL)
+ return 0;
+
+ return 1;
+}
+
+/* Create new jobserver semaphore */
+int create_jobserver_semaphore(int tokens)
+{
+ sprintf(jobserver_semaphore_name, "gmake_semaphore_%d", _getpid());
+
+ jobserver_semaphore = CreateSemaphore(
+ NULL, // Use default security descriptor
+ tokens, // Initial count
+ tokens, // Maximum count
+ jobserver_semaphore_name); // Semaphore name
+
+ if (jobserver_semaphore == NULL)
+ return 0;
+
+ return 1;
+}
+
+/* Close jobserver semaphore */
+void free_jobserver_semaphore()
+{
+ if (jobserver_semaphore != NULL)
+ {
+ CloseHandle(jobserver_semaphore);
+ jobserver_semaphore = NULL;
+ }
+}
+
+/* Decrement semaphore count */
+int acquire_jobserver_semaphore()
+{
+ DWORD dwEvent = WaitForSingleObject(
+ jobserver_semaphore, // Handle to semaphore
+ 0); // DON'T wait on semaphore
+
+ return (dwEvent == WAIT_OBJECT_0);
+}
+
+/* Increment semaphore count */
+int release_jobserver_semaphore()
+{
+ BOOL bResult = ReleaseSemaphore(
+ jobserver_semaphore, // handle to semaphore
+ 1, // increase count by one
+ NULL); // not interested in previous count
+
+ return (bResult);
+}
+
+int has_jobserver_semaphore()
+{
+ return (jobserver_semaphore != NULL);
+}
+
+char* get_jobserver_semaphore_name()
+{
+ return (jobserver_semaphore_name);
+}
+
+/* Wait for either the jobserver semaphore to become signalled or one of our
+ * child processes to terminate.
+ */
+int wait_for_semaphore_or_child_process()
+{
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+ DWORD dwHandleCount = 1;
+ DWORD dwEvent;
+ int i;
+
+ /* Add jobserver semaphore to first slot. */
+ handles[0] = jobserver_semaphore;
+
+ /* Build array of handles to wait for */
+ for (i = 0; i < proc_index; i++)
+ {
+ /* Don't wait on child processes that have already finished */
+ if (fake_exits_pending && proc_array[i]->exit_code)
+ continue;
+
+ handles[dwHandleCount++] = (HANDLE) proc_array[i]->pid;
+ }
+
+ dwEvent = WaitForMultipleObjects(
+ dwHandleCount, // number of objects in array
+ handles, // array of objects
+ FALSE, // wait for any object
+ INFINITE); // wait until object is signalled
+
+ switch(dwEvent)
+ {
+ case WAIT_FAILED:
+ return -1;
+
+ case WAIT_OBJECT_0:
+ /* Indicate that the semaphore was signalled */
+ return 1;
+
+ default:
+ /* Assume that one or more of the child processes terminated. */
+ return 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(int block, DWORD* pdwWaitStatus)
+{
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+ 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, (block ? INFINITE : 0));
+ which = retval - WAIT_OBJECT_0;
+ } else {
+ fake_exits_pending--;
+ retval = !WAIT_FAILED;
+ which = i;
+ }
+
+ /* If the pointer is not NULL, set the wait status result variable. */
+ if (pdwWaitStatus)
+ *pdwWaitStatus = retval;
+
+ /* return pointer to process */
+ if ((retval == WAIT_TIMEOUT) || (retval == WAIT_FAILED)) {
+ return NULL;
+ }
+ else {
+ sub_process* pproc = proc_array[which];
+ process_adjust_wait_state(pproc);
+ return pproc;
+ }
+}
+
+/*
+ * 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)
+{
+ if (proc_index < MAXIMUM_WAIT_OBJECTS)
+ proc_array[proc_index++] = (sub_process *) proc;
+}
+
+/*
+ * Return the number of processes that we are still waiting for.
+ */
+int
+process_used_slots(void)
+{
+ return proc_index;
+}
+
+/*
+ * 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(int block, DWORD* pdwWaitStatus)
+{
+ sub_process* pproc = process_wait_for_any_private(block, pdwWaitStatus);
+
+ 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_signal(HANDLE proc)
+{
+ if (proc == INVALID_HANDLE_VALUE) return 0;
+ return (((sub_process *)proc)->signal);
+}
+
+long
+process_last_err(HANDLE proc)
+{
+ if (proc == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
+ return (((sub_process *)proc)->last_err);
+}
+
+long
+process_exit_code(HANDLE proc)
+{
+ if (proc == INVALID_HANDLE_VALUE) return EXIT_FAILURE;
+ return (((sub_process *)proc)->exit_code);
+}
+
+void
+process_noinherit(int fd)
+{
+ HANDLE fh = (HANDLE)_get_osfhandle(fd);
+
+ if (fh && fh != INVALID_HANDLE_VALUE)
+ SetHandleInformation(fh, HANDLE_FLAG_INHERIT, 0);
+}
+
+/*
+2006-02:
+All the following functions are currently unused.
+All of them would crash gmake if called with argument INVALID_HANDLE_VALUE.
+Hence whoever wants to use one of this functions must invent and implement
+a reasonable error handling for this function.
+
+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] = (intptr_t) stdin_pipes[0];
+ pproc->sv_stdin[1] = (intptr_t) stdin_pipes[1];
+ pproc->sv_stdout[0] = (intptr_t) stdout_pipes[0];
+ pproc->sv_stdout[1] = (intptr_t) stdout_pipes[1];
+ pproc->sv_stderr[0] = (intptr_t) stderr_pipes[0];
+ pproc->sv_stderr[1] = (intptr_t) 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));
+ if (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] = (intptr_t) stdinh;
+ pproc->sv_stdout[1] = (intptr_t) stdouth;
+ pproc->sv_stderr[1] = (intptr_t) stderrh;
+
+ pproc->last_err = pproc->lerrno = 0;
+ }
+
+ return((HANDLE)pproc);
+}
+
+
+static HANDLE
+find_file(const char *exec_path, const char *path_var,
+ char *full_fname, DWORD full_len)
+{
+ HANDLE exec_handle;
+ char *fname;
+ char *ext;
+ DWORD req_len;
+ int i;
+ static const char *extensions[] =
+ /* Should .com come before no-extension case? */
+ { ".exe", ".cmd", ".bat", "", ".com", NULL };
+
+ fname = xmalloc(strlen(exec_path) + 5);
+ strcpy(fname, exec_path);
+ ext = fname + strlen(fname);
+
+ for (i = 0; extensions[i]; i++) {
+ strcpy(ext, extensions[i]);
+ if (((req_len = SearchPath (path_var, fname, NULL, full_len,
+ full_fname, NULL)) > 0
+ /* For compatibility with previous code, which
+ used OpenFile, and with Windows operation in
+ general, also look in various default
+ locations, such as Windows directory and
+ Windows System directory. Warning: this also
+ searches PATH in the Make's environment, which
+ might not be what the Makefile wants, but it
+ seems to be OK as a fallback, after the
+ previous SearchPath failed to find on child's
+ PATH. */
+ || (req_len = SearchPath (NULL, fname, NULL, full_len,
+ full_fname, NULL)) > 0)
+ && req_len <= full_len
+ && (exec_handle =
+ CreateFile(full_fname,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL)) != INVALID_HANDLE_VALUE) {
+ free(fname);
+ return(exec_handle);
+ }
+ }
+
+ free(fname);
+ return INVALID_HANDLE_VALUE;
+}
+
+/*
+ * Return non-zero of FNAME specifies a batch file and its name
+ * includes embedded whitespace.
+ */
+
+static int
+batch_file_with_spaces(const char *fname)
+{
+ size_t fnlen = strlen(fname);
+
+ return (fnlen > 4
+ && (_strnicmp(fname + fnlen - 4, ".bat", 4) == 0
+ || _strnicmp(fname + fnlen - 4, ".cmd", 4) == 0)
+ /* The set of characters in the 2nd arg to strpbrk
+ should be the same one used by make_command_line
+ below to decide whether an argv[] element needs
+ quoting. */
+ && strpbrk(fname, " \t") != NULL);
+}
+
+
+/*
+ * Description: Create the child process to be helped
+ *
+ * Returns: success <=> 0
+ *
+ * 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 exec_fname[MAX_PATH];
+ const char *path_var = NULL;
+ char **ep;
+ char buf[MAX_PATH];
+ DWORD bytes_returned;
+ DWORD flags;
+ char *command_line;
+ STARTUPINFO startInfo;
+ PROCESS_INFORMATION procInfo;
+ char *envblk=NULL;
+ int envsize_needed = 0;
+ int pass_null_exec_path = 0;
+
+ /*
+ * 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)
+ */
+
+ /* Use the Makefile's value of PATH to look for the program to
+ execute, because it could be different from Make's PATH
+ (e.g., if the target sets its own value. */
+ if (envp)
+ for (ep = envp; *ep; ep++) {
+ if (strncmp (*ep, "PATH=", 5) == 0
+ || strncmp (*ep, "Path=", 5) == 0) {
+ path_var = *ep + 5;
+ break;
+ }
+ }
+ exec_handle = find_file(exec_path, path_var,
+ exec_fname, sizeof(exec_fname));
+
+ /*
+ * If we couldn't open the file, just assume that Windows will be
+ * somehow able to find and execute it. If the first character
+ * of the command is '/', assume they set SHELL to a Unixy shell
+ * that have some magic mounts known only to it, and run the whole
+ * command via $SHELL -c "COMMAND" instead.
+ */
+ if (exec_handle == INVALID_HANDLE_VALUE) {
+ if (exec_path[0] == '/') {
+ char *new_argv0;
+ char **argvi = argv;
+ int arglen = 0;
+
+ strcpy(buf, variable_expand ("$(SHELL)"));
+ shell_name = &buf[0];
+ strcpy(exec_fname, "-c");
+ /* Construct a single command string in argv[0]. */
+ while (*argvi) {
+ arglen += strlen(*argvi) + 1;
+ argvi++;
+ }
+ new_argv0 = xmalloc(arglen + 1);
+ new_argv0[0] = '\0';
+ for (argvi = argv; *argvi; argvi++) {
+ strcat(new_argv0, *argvi);
+ strcat(new_argv0, " ");
+ }
+ /* Remove the extra blank at the end. */
+ new_argv0[arglen-1] = '\0';
+ free(argv[0]);
+ argv[0] = new_argv0;
+ argv[1] = NULL;
+ }
+ else
+ 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 {
+ /* If exec_fname includes whitespace, CreateProcess
+ behaves erratically and unreliably, and often fails
+ if argv[0] also includes whitespace (and thus will
+ be quoted by make_command_line below). So in that
+ case, we don't pass exec_fname as the 1st arg to
+ CreateProcess, but instead replace argv[0] with
+ exec_fname (to keep its leading directories and
+ extension as found by find_file), and pass NULL to
+ CreateProcess as its 1st arg. This works around
+ the bugs in CreateProcess, which are probably
+ caused by its passing the command to cmd.exe with
+ some incorrect quoting. */
+ if (!shell_name
+ && batch_file_with_spaces(exec_fname)
+ && _stricmp(exec_path, argv[0]) == 0) {
+ pass_null_exec_path = 1;
+ free (argv[0]);
+ argv[0] = xstrdup(exec_fname);
+ }
+ command_line = make_command_line( shell_name, exec_fname, argv);
+ }
+
+ if ( command_line == NULL ) {
+ pproc->last_err = 0;
+ pproc->lerrno = E_NO_MEM;
+ return(-1);
+ }
+
+ if (envp) {
+ if (arr2envblk(envp, &envblk, &envsize_needed) == FALSE) {
+ pproc->last_err = 0;
+ pproc->lerrno = E_NO_MEM;
+ free( command_line );
+ if (pproc->last_err == ERROR_INVALID_PARAMETER
+ && envsize_needed > 32*1024) {
+ fprintf (stderr, "CreateProcess failed, probably because environment is too large (%d bytes).\n",
+ envsize_needed);
+ }
+ return(-1);
+ }
+ }
+
+ if (shell_name || file_not_found || pass_null_exec_path) {
+ exec_path = 0; /* Search for the program in %Path% */
+ } else {
+ exec_path = exec_fname;
+ }
+
+ /*
+ * Set up inherited stdin, stdout, stderr for child
+ */
+ GetStartupInfo(&startInfo);
+ startInfo.dwFlags = STARTF_USESTDHANDLES;
+ startInfo.lpReserved = 0;
+ startInfo.cbReserved2 = 0;
+ startInfo.lpReserved2 = 0;
+ startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1];
+ startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1];
+ startInfo.hStdError = (HANDLE)pproc->sv_stderr[1];
+
+ if (as_user) {
+ free(envblk);
+ return -1;
+ } else {
+ DB (DB_JOBS, ("CreateProcess(%s,%s,...)\n",
+ exec_path ? exec_path : "NULL",
+ command_line ? command_line : "NULL"));
+ 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 ? exec_path : "NULL", command_line);
+ free(envblk);
+ free( command_line );
+ return(-1);
+ }
+ }
+
+ pproc->pid = (pid_t)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 ((HANDLE)pproc->sv_stdin[1] != INVALID_HANDLE_VALUE)
+ CloseHandle((HANDLE)pproc->sv_stdin[1]);
+ if ((HANDLE)pproc->sv_stdout[1] != INVALID_HANDLE_VALUE)
+ CloseHandle((HANDLE)pproc->sv_stdout[1]);
+ if ((HANDLE)pproc->sv_stderr[1] != INVALID_HANDLE_VALUE)
+ CloseHandle((HANDLE)pproc->sv_stderr[1]);
+ pproc->sv_stdin[1] = 0;
+ pproc->sv_stdout[1] = 0;
+ pproc->sv_stderr[1] = 0;
+
+ free( command_line );
+ free(envblk);
+ pproc->lerrno=0;
+ return 0;
+}
+
+
+
+#if 0 /* unused */
+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_windows32_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_windows32_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 = NULL, tStdout = NULL, tStderr = NULL;
+ unsigned int dwStdin, dwStdout, dwStderr;
+ HANDLE wait_list[4];
+ DWORD wait_count;
+ DWORD wait_return;
+ HANDLE ready_hand;
+ bool_t child_dead = FALSE;
+ BOOL GetExitCodeResult;
+
+ /*
+ * 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]);
+ pproc->sv_stdin[0] = 0;
+ } else {
+ tStdin = (HANDLE) _beginthreadex( 0, 1024,
+ (unsigned (__stdcall *) (void *))proc_stdin_thread,
+ pproc, 0, &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,
+ &dwStdout);
+ tStderr = (HANDLE) _beginthreadex( 0, 1024,
+ (unsigned (__stdcall *) (void *))proc_stderr_thread, pproc, 0,
+ &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_windows32_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]);
+ pproc->sv_stdin[0] = 0;
+ CloseHandle(tStdin);
+ tStdin = 0;
+ stdin_eof = TRUE;
+
+ } else if (ready_hand == tStdout) {
+
+ CloseHandle((HANDLE)pproc->sv_stdout[0]);
+ pproc->sv_stdout[0] = 0;
+ CloseHandle(tStdout);
+ tStdout = 0;
+ stdout_eof = TRUE;
+
+ } else if (ready_hand == tStderr) {
+
+ CloseHandle((HANDLE)pproc->sv_stderr[0]);
+ pproc->sv_stderr[0] = 0;
+ CloseHandle(tStderr);
+ tStderr = 0;
+ stderr_eof = TRUE;
+
+ } else if (ready_hand == childhand) {
+
+ DWORD ierr;
+ GetExitCodeResult = GetExitCodeProcess(childhand, &ierr);
+ if (ierr == CONTROL_C_EXIT) {
+ pproc->signal = SIGINT;
+ } else {
+ pproc->exit_code = ierr;
+ }
+ if (GetExitCodeResult == 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);
+
+}
+#endif /* unused */
+
+/*
+ * 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;
+ BOOL GetExitCodeResult;
+ DWORD ierr;
+
+ if (proc == NULL)
+ pproc = process_wait_for_any_private(1, 0);
+ 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_windows32_error_to_string(GetLastError());*/
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done2;
+ }
+
+ GetExitCodeResult = GetExitCodeProcess(childhand, &ierr);
+ if (ierr == CONTROL_C_EXIT) {
+ pproc->signal = SIGINT;
+ } else {
+ pproc->exit_code = ierr;
+ }
+ if (GetExitCodeResult == 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, output, 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]
+ && (HANDLE)pproc->sv_stdin[i] != INVALID_HANDLE_VALUE)
+ CloseHandle((HANDLE)pproc->sv_stdin[i]);
+ if ((HANDLE)pproc->sv_stdout[i]
+ && (HANDLE)pproc->sv_stdout[i] != INVALID_HANDLE_VALUE)
+ CloseHandle((HANDLE)pproc->sv_stdout[i]);
+ if ((HANDLE)pproc->sv_stderr[i]
+ && (HANDLE)pproc->sv_stderr[i] != INVALID_HANDLE_VALUE)
+ CloseHandle((HANDLE)pproc->sv_stderr[i]);
+ }
+ }
+ if ((HANDLE)pproc->pid)
+ CloseHandle((HANDLE)pproc->pid);
+
+ free(pproc);
+}
+
+
+/*
+ * 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 *full_exec_path, char **argv)
+{
+ int argc = 0;
+ char** argvi;
+ int* enclose_in_quotes = NULL;
+ int* enclose_in_quotes_i;
+ unsigned int bytes_required = 0;
+ char* command_line;
+ char* command_line_i;
+ int cygwin_mode = 0; /* HAVE_CYGWIN_SHELL */
+ int have_sh = 0; /* HAVE_CYGWIN_SHELL */
+
+#ifdef HAVE_CYGWIN_SHELL
+ have_sh = (shell_name != NULL || strstr(full_exec_path, "sh.exe"));
+ cygwin_mode = 1;
+#endif
+
+ if (shell_name && full_exec_path) {
+ bytes_required
+ = strlen(shell_name) + 1 + strlen(full_exec_path);
+ /*
+ * Skip argv[0] if any, when shell_name is given.
+ * The special case of "-c" in full_exec_path means
+ * argv[0] is not the shell name, but the command string
+ * to pass to the shell.
+ */
+ if (*argv && strcmp(full_exec_path, "-c")) argv++;
+ /*
+ * Add one for the intervening space.
+ */
+ if (*argv) bytes_required++;
+ }
+
+ argvi = argv;
+ while (*(argvi++)) argc++;
+
+ if (argc) {
+ enclose_in_quotes = (int*) calloc(1, argc * sizeof(int));
+
+ if (!enclose_in_quotes) {
+ return NULL;
+ }
+ }
+
+ /* We have to make one pass through each argv[i] to see if we need
+ * to enclose it in ", so we might as well figure out how much
+ * memory we'll need on the same pass.
+ */
+
+ argvi = argv;
+ enclose_in_quotes_i = enclose_in_quotes;
+ while(*argvi) {
+ char* p = *argvi;
+ unsigned int backslash_count = 0;
+
+ /*
+ * We have to enclose empty arguments in ".
+ */
+ if (!(*p)) *enclose_in_quotes_i = 1;
+
+ while(*p) {
+ switch (*p) {
+ case '\"':
+ /*
+ * We have to insert a backslash for each "
+ * and each \ that precedes the ".
+ */
+ bytes_required += (backslash_count + 1);
+ backslash_count = 0;
+ break;
+
+#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
+ case '\\':
+ backslash_count++;
+ break;
+#endif
+ /*
+ * At one time we set *enclose_in_quotes_i for '*' or '?' to suppress
+ * wildcard expansion in programs linked with MSVC's SETARGV.OBJ so
+ * that argv in always equals argv out. This was removed. Say you have
+ * such a program named glob.exe. You enter
+ * glob '*'
+ * at the sh command prompt. Obviously the intent is to make glob do the
+ * wildcarding instead of sh. If we set *enclose_in_quotes_i for '*' or '?',
+ * then the command line that glob would see would be
+ * glob "*"
+ * and the _setargv in SETARGV.OBJ would _not_ expand the *.
+ */
+ case ' ':
+ case '\t':
+ *enclose_in_quotes_i = 1;
+ /* fall through */
+
+ default:
+ backslash_count = 0;
+ break;
+ }
+
+ /*
+ * Add one for each character in argv[i].
+ */
+ bytes_required++;
+
+ p++;
+ }
+
+ if (*enclose_in_quotes_i) {
+ /*
+ * Add one for each enclosing ",
+ * and one for each \ that precedes the
+ * closing ".
+ */
+ bytes_required += (backslash_count + 2);
+ }
+
+ /*
+ * Add one for the intervening space.
+ */
+ if (*(++argvi)) bytes_required++;
+ enclose_in_quotes_i++;
+ }
+
+ /*
+ * Add one for the terminating NULL.
+ */
+ bytes_required++;
+
+ command_line = (char*) malloc(bytes_required);
+
+ if (!command_line) {
+ free(enclose_in_quotes);
+ return NULL;
+ }
+
+ command_line_i = command_line;
+
+ if (shell_name && full_exec_path) {
+ while(*shell_name) {
+ *(command_line_i++) = *(shell_name++);
+ }
+
+ *(command_line_i++) = ' ';
+
+ while(*full_exec_path) {
+ *(command_line_i++) = *(full_exec_path++);
+ }
+
+ if (*argv) {
+ *(command_line_i++) = ' ';
+ }
+ }
+
+ argvi = argv;
+ enclose_in_quotes_i = enclose_in_quotes;
+
+ while(*argvi) {
+ char* p = *argvi;
+ unsigned int backslash_count = 0;
+
+ if (*enclose_in_quotes_i) {
+ *(command_line_i++) = '\"';
+ }
+
+ while(*p) {
+ if (*p == '\"') {
+ if (cygwin_mode && have_sh) { /* HAVE_CYGWIN_SHELL */
+ /* instead of a \", cygwin likes "" */
+ *(command_line_i++) = '\"';
+ } else {
+
+ /*
+ * We have to insert a backslash for the "
+ * and each \ that precedes the ".
+ */
+ backslash_count++;
+
+ while(backslash_count) {
+ *(command_line_i++) = '\\';
+ backslash_count--;
+ };
+ }
+#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
+ } else if (*p == '\\') {
+ backslash_count++;
+ } else {
+ backslash_count = 0;
+#endif
+ }
+
+ /*
+ * Copy the character.
+ */
+ *(command_line_i++) = *(p++);
+ }
+
+ if (*enclose_in_quotes_i) {
+#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
+ /*
+ * Add one \ for each \ that precedes the
+ * closing ".
+ */
+ while(backslash_count--) {
+ *(command_line_i++) = '\\';
+ };
+#endif
+ *(command_line_i++) = '\"';
+ }
+
+ /*
+ * Append an intervening space.
+ */
+ if (*(++argvi)) {
+ *(command_line_i++) = ' ';
+ }
+
+ enclose_in_quotes_i++;
+ }
+
+ /*
+ * Append the terminating NULL.
+ */
+ *command_line_i = '\0';
+
+ free(enclose_in_quotes);
+ return command_line;
+}
+
+/*
+ * 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,
+ int outfd,
+ int errfd)
+{
+ HANDLE hIn = INVALID_HANDLE_VALUE;
+ HANDLE hOut = INVALID_HANDLE_VALUE;
+ HANDLE hErr = INVALID_HANDLE_VALUE;
+ HANDLE hProcess, tmpIn, tmpOut, tmpErr;
+ DWORD e;
+
+ if (proc_index >= MAXIMUM_WAIT_OBJECTS) {
+ DB (DB_JOBS, ("process_easy: All process slots used up\n"));
+ return INVALID_HANDLE_VALUE;
+ }
+ /* Standard handles returned by GetStdHandle can be NULL or
+ INVALID_HANDLE_VALUE if the parent process closed them. If that
+ happens, we open the null device and pass its handle to
+ CreateProcess as the corresponding handle to inherit. */
+ tmpIn = GetStdHandle(STD_INPUT_HANDLE);
+ if (DuplicateHandle(GetCurrentProcess(),
+ tmpIn,
+ GetCurrentProcess(),
+ &hIn,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ if ((e = GetLastError()) == ERROR_INVALID_HANDLE) {
+ tmpIn = CreateFile("NUL", GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (tmpIn != INVALID_HANDLE_VALUE
+ && DuplicateHandle(GetCurrentProcess(),
+ tmpIn,
+ GetCurrentProcess(),
+ &hIn,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE)
+ CloseHandle(tmpIn);
+ }
+ if (hIn == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "process_easy: DuplicateHandle(In) failed (e=%ld)\n", e);
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+ if (outfd >= 0)
+ tmpOut = (HANDLE)_get_osfhandle (outfd);
+ else
+ tmpOut = GetStdHandle (STD_OUTPUT_HANDLE);
+ if (DuplicateHandle(GetCurrentProcess(),
+ tmpOut,
+ GetCurrentProcess(),
+ &hOut,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ if ((e = GetLastError()) == ERROR_INVALID_HANDLE) {
+ tmpOut = CreateFile("NUL", GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (tmpOut != INVALID_HANDLE_VALUE
+ && DuplicateHandle(GetCurrentProcess(),
+ tmpOut,
+ GetCurrentProcess(),
+ &hOut,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE)
+ CloseHandle(tmpOut);
+ }
+ if (hOut == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "process_easy: DuplicateHandle(Out) failed (e=%ld)\n", e);
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+ if (errfd >= 0)
+ tmpErr = (HANDLE)_get_osfhandle (errfd);
+ else
+ tmpErr = GetStdHandle(STD_ERROR_HANDLE);
+ if (DuplicateHandle(GetCurrentProcess(),
+ tmpErr,
+ GetCurrentProcess(),
+ &hErr,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ if ((e = GetLastError()) == ERROR_INVALID_HANDLE) {
+ tmpErr = CreateFile("NUL", GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (tmpErr != INVALID_HANDLE_VALUE
+ && DuplicateHandle(GetCurrentProcess(),
+ tmpErr,
+ GetCurrentProcess(),
+ &hErr,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE)
+ CloseHandle(tmpErr);
+ }
+ if (hErr == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "process_easy: DuplicateHandle(Err) failed (e=%ld)\n", e);
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+
+ hProcess = process_init_fd(hIn, hOut, hErr);
+
+ if (process_begin(hProcess, argv, envp, argv[0], NULL)) {
+ fake_exits_pending++;
+ /* process_begin() failed: make a note of that. */
+ if (!((sub_process*) hProcess)->last_err)
+ ((sub_process*) hProcess)->last_err = -1;
+ ((sub_process*) hProcess)->exit_code = process_last_err(hProcess);
+
+ /* close up unused handles */
+ if (hIn != INVALID_HANDLE_VALUE)
+ CloseHandle(hIn);
+ if (hOut != INVALID_HANDLE_VALUE)
+ CloseHandle(hOut);
+ if (hErr != INVALID_HANDLE_VALUE)
+ CloseHandle(hErr);
+ }
+
+ process_register(hProcess);
+
+ return hProcess;
+}