From 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:21:29 +0200 Subject: Adding upstream version 1:0.1.9998svn3589+dfsg. Signed-off-by: Daniel Baumann --- src/kmk/w32/subproc/sub_proc.c | 1714 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1714 insertions(+) create mode 100644 src/kmk/w32/subproc/sub_proc.c (limited to 'src/kmk/w32/subproc/sub_proc.c') diff --git a/src/kmk/w32/subproc/sub_proc.c b/src/kmk/w32/subproc/sub_proc.c new file mode 100644 index 0000000..6ccfa47 --- /dev/null +++ b/src/kmk/w32/subproc/sub_proc.c @@ -0,0 +1,1714 @@ +/* Process handling for Windows. +Copyright (C) 1996-2016 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 . */ + +#include +#include +#include +#include /* for _get_osfhandle */ +#ifdef _MSC_VER +# include /* for intptr_t */ +#else +# include +#endif +#include +#include /* for msvc _beginthreadex, _endthreadex */ +#include +#include + +#include "makeint.h" +#include "filedef.h" +#include "variable.h" +#include "sub_proc.h" +#include "proc.h" +#include "w32err.h" +#include "debug.h" + +#ifdef KMK +# include +# include "kmkbuiltin.h" +extern void kmk_cache_exec_image_a(const char *); /* imagecache.c */ +#endif + +static char *make_command_line(char *shell_name, char *exec_path, char **argv); + +typedef struct sub_process_t { +#ifdef KMK + enum { kRegular = 0, kSubmit, kSubProcFreed } enmType; + intptr_t clue; +#endif + 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; + +static long process_file_io_private(sub_process *pproc, BOOL fNeedToWait); /* bird */ + +/* 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; + + +/* + * Fill a HANDLE list with handles to wait for. + */ +DWORD +process_set_handles(HANDLE *handles) +{ + DWORD count = 0; + int i; + + /* 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[count++] = (HANDLE) proc_array[i]->pid; + } + + return count; +} + +#ifndef KMK /* Inefficient! */ +/* + * 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; + } +} + +#endif /* !KMK */ + +/* + * 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) { +#ifdef KMK +l_wait_again: +#endif + 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]; +#ifdef KMK + if (pproc->enmType == kSubmit) { + /* Try get the result from kSubmit.c. This may not succeed if the whole + result hasn't arrived yet, in which we just restart the wait. */ + if (kSubmitSubProcGetResult(pproc->clue, &pproc->exit_code, &pproc->signal) != 0) { + goto l_wait_again; + } + } +#endif +#ifndef KMK /* Inefficient! */ + process_adjust_wait_state(pproc); +#else + proc_index--; + if ((int)which < proc_index) + proc_array[which] = proc_array[proc_index]; + proc_array[proc_index] = NULL; +#endif + return pproc; + } +} + +/* + * Terminate a process. + */ +BOOL + process_kill(HANDLE proc, int signal) +{ + sub_process* pproc = (sub_process*) proc; +#ifdef KMK + if (pproc->enmType == kRegular) { +#endif + pproc->signal = signal; + return (TerminateProcess((HANDLE) pproc->pid, signal)); +#ifdef KMK + } else if (pproc->enmType == kSubmit) + return kSubmitSubProcKill(pproc->clue, signal) == 0; + assert(0); + return FALSE; +#endif +} + +/* + * 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) +{ +#ifdef KMK + assert(((sub_process *)proc)->enmType == kRegular); +#endif + if (proc_index < MAXIMUM_WAIT_OBJECTS) + proc_array[proc_index++] = (sub_process *) proc; +} + +#ifdef KMK + +/** + * Interface used by kmkbuiltin/kSubmit.c to register stuff going down in a + * worker process. + * + * @returns 0 on success, -1 if there are too many sub-processes already. + * @param hEvent The event semaphore to wait on. + * @param clue The clue to base. + * @param pPid Where to return the pid that job.c expects. + */ +int +process_kmk_register_submit(HANDLE hEvent, intptr_t clue, pid_t *pPid) +{ + if (proc_index < MAXIMUM_WAIT_OBJECTS) { + sub_process *pSubProc = (sub_process *)xcalloc(sizeof(*pSubProc)); + pSubProc->enmType = kSubmit; + pSubProc->clue = clue; + pSubProc->pid = (intptr_t)hEvent; + + proc_array[proc_index++] = pSubProc; + *pPid = (intptr_t)pSubProc; + return 0; + } + return -1; +} + +/** + * Interface used by kmkbuiltin/kRedirect.c to register a spawned process. + * + * @returns 0 on success, -1 if there are too many sub-processes already. + * @param hProcess The process handle. + * @param pPid Where to return the pid that job.c expects. + */ +int +process_kmk_register_redirect(HANDLE hProcess, pid_t *pPid) +{ + if (proc_index < MAXIMUM_WAIT_OBJECTS) { + sub_process *pSubProc = (sub_process *)xcalloc(sizeof(*pSubProc)); + pSubProc->enmType = kRegular; + pSubProc->pid = (intptr_t)hProcess; + + proc_array[proc_index++] = pSubProc; + *pPid = (intptr_t)pSubProc; + return 0; + } + return -1; +} + +#endif /* KMK */ + +/* + * 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() + */ +#ifdef KMK + /* Invalidate negative directory cache entries now that a + job has completed and possibly created new files that + was missing earlier. */ + dir_cache_invalid_after_job (); + + if (pproc->enmType == kRegular) { + (void)process_file_io_private(pproc, FALSE); + } +#else + (void) process_file_io(pproc); +#endif + 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; +#ifdef KMK + size_t exec_path_len; + extern int process_priority; + + assert (pproc->enmType == kRegular); +#endif + + /* + * 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) + */ + +#ifdef KMK + /* kmk performance: Don't bother looking for shell scripts in .exe files. */ + exec_path_len = strlen(exec_path); + if (exec_path_len > 4 + && exec_path[exec_path_len - 4] == '.' + && !stricmp(exec_path + exec_path_len - 3, "exe")) { + exec_handle = INVALID_HANDLE_VALUE; + exec_fname[0] = '\0'; + } + else { +#endif /* KMK */ + + /* 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)); +#ifdef KMK + } +#endif + + /* + * 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) { + char *new_argv, *p; + char **argvi; + int arglen, i; + pass_null_exec_path = 1; + /* Rewrite argv[] replacing argv[0] with exec_fname. */ + for (argvi = argv + 1, arglen = strlen(exec_fname) + 1; + *argvi; + argvi++) { + arglen += strlen(*argvi) + 1; + } + new_argv = xmalloc(arglen); + p = strcpy(new_argv, exec_fname) + strlen(exec_fname) + 1; + for (argvi = argv + 1, i = 1; *argvi; argvi++, i++) { + strcpy(p, *argvi); + argv[i] = p; + p += strlen(*argvi) + 1; + } + argv[i] = NULL; + free (argv[0]); + argv[0] = new_argv; + } + 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->lerrno = E_NO_MEM; + free( command_line ); + if ((pproc->last_err == ERROR_INVALID_PARAMETER + || pproc->last_err == ERROR_MORE_DATA) + && envsize_needed > 32*1024) { + fprintf (stderr, "CreateProcess failed, probably because environment is too large (%d bytes).\n", + envsize_needed); + } + pproc->last_err = 0; + 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 + */ + memset(&startInfo, '\0', sizeof(startInfo)); + GetStartupInfo(&startInfo); +#ifndef KMK + startInfo.dwFlags = STARTF_USESTDHANDLES; +#endif + startInfo.lpReserved = 0; + startInfo.cbReserved2 = 0; + startInfo.lpReserved2 = 0; +#ifndef KMK + startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1]; + startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1]; + startInfo.hStdError = (HANDLE)pproc->sv_stderr[1]; +#else + if ( ((HANDLE)pproc->sv_stdin[1] != INVALID_HANDLE_VALUE && pproc->sv_stdin[1]) + || ((HANDLE)pproc->sv_stdout[1] != INVALID_HANDLE_VALUE && pproc->sv_stdout[1]) + || ((HANDLE)pproc->sv_stderr[1] != INVALID_HANDLE_VALUE && pproc->sv_stderr[1]) ) { + startInfo.dwFlags = STARTF_USESTDHANDLES; + startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1]; + startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1]; + startInfo.hStdError = (HANDLE)pproc->sv_stderr[1]; + } else { + startInfo.dwFlags = 0; + startInfo.hStdInput = 0; + startInfo.hStdOutput = 0; + startInfo.hStdError = 0; + } +#endif + + 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")); +#ifdef KMK + if (exec_fname[0]) + kmk_cache_exec_image_a(exec_fname); + else if (exec_path) + kmk_cache_exec_image_a(exec_path); + else if (argv[0]) + kmk_cache_exec_image_a(argv[0]); + + switch (process_priority) { + case 1: flags |= CREATE_SUSPENDED | IDLE_PRIORITY_CLASS; break; + case 2: flags |= CREATE_SUSPENDED | BELOW_NORMAL_PRIORITY_CLASS; break; + case 3: flags |= CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS; break; + case 4: flags |= CREATE_SUSPENDED | HIGH_PRIORITY_CLASS; break; + case 5: flags |= CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS; break; + } +#endif + 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); + } +#ifdef KMK + switch (process_priority) { + case 1: SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_IDLE); break; + case 2: SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_BELOW_NORMAL); break; + case 3: SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_NORMAL); break; + case 4: SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_HIGHEST); break; + case 5: SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL); break; + } + ResumeThread(procInfo.hThread); +#endif + } + + 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 && pproc->sv_stdin[1]) + CloseHandle((HANDLE)pproc->sv_stdin[1]); + if ((HANDLE)pproc->sv_stdout[1] != INVALID_HANDLE_VALUE && pproc->sv_stdout[1]) + CloseHandle((HANDLE)pproc->sv_stdout[1]); + if ((HANDLE)pproc->sv_stderr[1] != INVALID_HANDLE_VALUE && pproc->sv_stderr[1]) + 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; +#ifdef KMK + assert (pproc->enmType == kRegular); +#endif + + /* + * 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 */ + +#ifndef KMK /* unused */ +/* + * Purpose: collects output from child process and returns results + * + * Description: + * + * Returns: + * + * Notes/Dependencies: + */ + long +process_file_io( + HANDLE proc) +{ + sub_process *pproc; + + 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; + + return process_file_io_private(proc, TRUE); +} +#endif /* !KMK - unused */ + +/* private function, avoid some kernel calls. (bird) */ +static long +process_file_io_private( + sub_process *pproc, + BOOL fNeedToWait) +{ + HANDLE childhand; + DWORD wait_return; + BOOL GetExitCodeResult; + DWORD ierr; + + 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; + } + +#ifdef KMK + if (childhand == NULL || childhand == INVALID_HANDLE_VALUE) { + goto done2; + } +#endif + + /* + * Wait for the child process to exit + */ + + if (fNeedToWait) { /* bird */ + 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; + } + } /* bird */ + + 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; + +#ifdef KMK + if (pproc->enmType == kRegular) { +#endif + 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); +#ifdef KMK + } else if (pproc->enmType == kSubmit) { + kSubmitSubProcCleanup(pproc->clue); + } else { + assert(0); + return; + } + pproc->enmType = kSubProcFreed; +#endif + + 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 */ +#undef HAVE_CYGWIN_SHELL /* bird: paranoia */ +#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++; +#ifdef KMK /* for the space before the final " in case we need it. */ + bytes_required++; +#endif + + 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 +#ifdef KMK + /* + * ash it put off by echo "hello world" ending up as: + * G:/.../kmk_ash.exe -c "echo ""hello world""" + * It wants a space before the last '"'. + * (The 'test_shell' goals in Makefile.kmk tests this problem.) + */ + if (command_line_i[-1] == '\"' /* && cygwin_mode && have_sh*/ && !argvi[1]) { + *(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; + } +#ifdef KMK /* We can effort here by lettering CreateProcess/kernel do the standard handle duplication. */ + if (outfd != -1 || errfd != -1) { +#endif + /* 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; + } + } +#ifdef KMK /* saving effort */ + } +#endif + + 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; +#ifdef KMK + if (!((sub_process*) hProcess)->exit_code) +#endif + ((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; +} -- cgit v1.2.3