From fc47a2c83c4b6c6d47f3c73fd3879fc92eb9c4b3 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 19 Jul 1996 21:41:07 +0000 Subject: updated w32 code by tulloh --- w32/subproc/sub_proc.c | 2200 ++++++++++++++++++++++++------------------------ 1 file changed, 1100 insertions(+), 1100 deletions(-) (limited to 'w32/subproc/sub_proc.c') diff --git a/w32/subproc/sub_proc.c b/w32/subproc/sub_proc.c index 52cd9d7..4f1ded9 100644 --- a/w32/subproc/sub_proc.c +++ b/w32/subproc/sub_proc.c @@ -1,1100 +1,1100 @@ -#include -#include -#include /* for msvc _beginthreadex, _endthreadex */ -#include - -#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; -} +#include +#include +#include /* for msvc _beginthreadex, _endthreadex */ +#include + +#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; +} -- cgit v1.2.3