#include <stdlib.h> | |
#include <stdio.h> | |
#include <process.h> /* for msvc _beginthreadex, _endthreadex */ | |
#include <windows.h> | |
#include "sub_proc.h" | |
#include "proc.h" | |
#include "w32err.h" | |
static char *make_command_line( char *shell_name, char *exec_path, char **argv); | |
typedef struct sub_process_t { | |
int sv_stdin[2]; | |
int sv_stdout[2]; | |
int sv_stderr[2]; | |
int using_pipes; | |
char *inp; | |
DWORD incnt; | |
char * volatile outp; | |
volatile DWORD outcnt; | |
char * volatile errp; | |
volatile DWORD errcnt; | |
int pid; | |
int exit_code; | |
int signal; | |
long last_err; | |
long lerrno; | |
} sub_process; | |
/* keep track of children so we can implement a waitpid-like routine */ | |
static sub_process *proc_array[256]; | |
static int proc_index = 0; | |
static int fake_exits_pending = 0; | |
/* | |
* When a process has been waited for, adjust the wait state | |
* array so that we don't wait for it again | |
*/ | |
static void | |
process_adjust_wait_state(sub_process* pproc) | |
{ | |
int i; | |
if (!proc_index) | |
return; | |
for (i = 0; i < proc_index; i++) | |
if (proc_array[i]->pid == pproc->pid) | |
break; | |
if (i < proc_index) { | |
proc_index--; | |
if (i != proc_index) | |
memmove(&proc_array[i], &proc_array[i+1], | |
(proc_index-i) * sizeof(sub_process*)); | |
proc_array[proc_index] = NULL; | |
} | |
} | |
/* | |
* Waits for any of the registered child processes to finish. | |
*/ | |
static sub_process * | |
process_wait_for_any_private(void) | |
{ | |
HANDLE handles[256]; | |
DWORD retval, which; | |
int i; | |
if (!proc_index) | |
return NULL; | |
/* build array of handles to wait for */ | |
for (i = 0; i < proc_index; i++) { | |
handles[i] = (HANDLE) proc_array[i]->pid; | |
if (fake_exits_pending && proc_array[i]->exit_code) | |
break; | |
} | |
/* wait for someone to exit */ | |
if (!fake_exits_pending) { | |
retval = WaitForMultipleObjects(proc_index, handles, FALSE, INFINITE); | |
which = retval - WAIT_OBJECT_0; | |
} else { | |
fake_exits_pending--; | |
retval = !WAIT_FAILED; | |
which = i; | |
} | |
/* return pointer to process */ | |
if (retval != WAIT_FAILED) { | |
sub_process* pproc = proc_array[which]; | |
process_adjust_wait_state(pproc); | |
return pproc; | |
} else | |
return NULL; | |
} | |
/* | |
* Terminate a process. | |
*/ | |
BOOL | |
process_kill(HANDLE proc, int signal) | |
{ | |
sub_process* pproc = (sub_process*) proc; | |
pproc->signal = signal; | |
return (TerminateProcess((HANDLE) pproc->pid, signal)); | |
} | |
/* | |
* Use this function to register processes you wish to wait for by | |
* calling process_file_io(NULL) or process_wait_any(). This must be done | |
* because it is possible for callers of this library to reuse the same | |
* handle for multiple processes launches :-( | |
*/ | |
void | |
process_register(HANDLE proc) | |
{ | |
proc_array[proc_index++] = (sub_process *) proc; | |
} | |
/* | |
* Public function which works kind of like waitpid(). Wait for any | |
* of the children to die and return results. To call this function, | |
* you must do 1 of things: | |
* | |
* x = process_easy(...); | |
* | |
* or | |
* | |
* x = process_init_fd(); | |
* process_register(x); | |
* | |
* or | |
* | |
* x = process_init(); | |
* process_register(x); | |
* | |
* You must NOT then call process_pipe_io() because this function is | |
* not capable of handling automatic notification of any child | |
* death. | |
*/ | |
HANDLE | |
process_wait_for_any(void) | |
{ | |
sub_process* pproc = process_wait_for_any_private(); | |
if (!pproc) | |
return NULL; | |
else { | |
/* | |
* Ouch! can't tell caller if this fails directly. Caller | |
* will have to use process_last_err() | |
*/ | |
(void) process_file_io(pproc); | |
return ((HANDLE) pproc); | |
} | |
} | |
long | |
process_errno(HANDLE proc) | |
{ | |
return (((sub_process *)proc)->lerrno); | |
} | |
long | |
process_signal(HANDLE proc) | |
{ | |
return (((sub_process *)proc)->signal); | |
} | |
long | |
process_last_err(HANDLE proc) | |
{ | |
return (((sub_process *)proc)->last_err); | |
} | |
long | |
process_exit_code(HANDLE proc) | |
{ | |
return (((sub_process *)proc)->exit_code); | |
} | |
char * | |
process_outbuf(HANDLE proc) | |
{ | |
return (((sub_process *)proc)->outp); | |
} | |
char * | |
process_errbuf(HANDLE proc) | |
{ | |
return (((sub_process *)proc)->errp); | |
} | |
int | |
process_outcnt(HANDLE proc) | |
{ | |
return (((sub_process *)proc)->outcnt); | |
} | |
int | |
process_errcnt(HANDLE proc) | |
{ | |
return (((sub_process *)proc)->errcnt); | |
} | |
void | |
process_pipes(HANDLE proc, int pipes[3]) | |
{ | |
pipes[0] = ((sub_process *)proc)->sv_stdin[0]; | |
pipes[1] = ((sub_process *)proc)->sv_stdout[0]; | |
pipes[2] = ((sub_process *)proc)->sv_stderr[0]; | |
return; | |
} | |
HANDLE | |
process_init() | |
{ | |
sub_process *pproc; | |
/* | |
* open file descriptors for attaching stdin/stdout/sterr | |
*/ | |
HANDLE stdin_pipes[2]; | |
HANDLE stdout_pipes[2]; | |
HANDLE stderr_pipes[2]; | |
SECURITY_ATTRIBUTES inherit; | |
BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH]; | |
pproc = malloc(sizeof(*pproc)); | |
memset(pproc, 0, sizeof(*pproc)); | |
/* We can't use NULL for lpSecurityDescriptor because that | |
uses the default security descriptor of the calling process. | |
Instead we use a security descriptor with no DACL. This | |
allows nonrestricted access to the associated objects. */ | |
if (!InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR)(&sd), | |
SECURITY_DESCRIPTOR_REVISION)) { | |
pproc->last_err = GetLastError(); | |
pproc->lerrno = E_SCALL; | |
return((HANDLE)pproc); | |
} | |
inherit.nLength = sizeof(inherit); | |
inherit.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)(&sd); | |
inherit.bInheritHandle = TRUE; | |
// By convention, parent gets pipe[0], and child gets pipe[1] | |
// This means the READ side of stdin pipe goes into pipe[1] | |
// and the WRITE side of the stdout and stderr pipes go into pipe[1] | |
if (CreatePipe( &stdin_pipes[1], &stdin_pipes[0], &inherit, 0) == FALSE || | |
CreatePipe( &stdout_pipes[0], &stdout_pipes[1], &inherit, 0) == FALSE || | |
CreatePipe( &stderr_pipes[0], &stderr_pipes[1], &inherit, 0) == FALSE) { | |
pproc->last_err = GetLastError(); | |
pproc->lerrno = E_SCALL; | |
return((HANDLE)pproc); | |
} | |
// | |
// Mark the parent sides of the pipes as non-inheritable | |
// | |
if (SetHandleInformation(stdin_pipes[0], | |
HANDLE_FLAG_INHERIT, 0) == FALSE || | |
SetHandleInformation(stdout_pipes[0], | |
HANDLE_FLAG_INHERIT, 0) == FALSE || | |
SetHandleInformation(stderr_pipes[0], | |
HANDLE_FLAG_INHERIT, 0) == FALSE) { | |
pproc->last_err = GetLastError(); | |
pproc->lerrno = E_SCALL; | |
return((HANDLE)pproc); | |
} | |
pproc->sv_stdin[0] = (int) stdin_pipes[0]; | |
pproc->sv_stdin[1] = (int) stdin_pipes[1]; | |
pproc->sv_stdout[0] = (int) stdout_pipes[0]; | |
pproc->sv_stdout[1] = (int) stdout_pipes[1]; | |
pproc->sv_stderr[0] = (int) stderr_pipes[0]; | |
pproc->sv_stderr[1] = (int) stderr_pipes[1]; | |
pproc->using_pipes = 1; | |
pproc->lerrno = 0; | |
return((HANDLE)pproc); | |
} | |
HANDLE | |
process_init_fd(HANDLE stdinh, HANDLE stdouth, HANDLE stderrh) | |
{ | |
sub_process *pproc; | |
pproc = malloc(sizeof(*pproc)); | |
memset(pproc, 0, sizeof(*pproc)); | |
/* | |
* Just pass the provided file handles to the 'child side' of the | |
* pipe, bypassing pipes altogether. | |
*/ | |
pproc->sv_stdin[1] = (int) stdinh; | |
pproc->sv_stdout[1] = (int) stdouth; | |
pproc->sv_stderr[1] = (int) stderrh; | |
pproc->last_err = pproc->lerrno = 0; | |
return((HANDLE)pproc); | |
} | |
static HANDLE | |
find_file(char *exec_path, LPOFSTRUCT file_info) | |
{ | |
HANDLE exec_handle; | |
char *fname; | |
char *ext; | |
if ((exec_handle = (HANDLE)OpenFile(exec_path, file_info, | |
OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { | |
return(exec_handle); | |
} | |
fname = malloc(strlen(exec_path) + 5); | |
strcpy(fname, exec_path); | |
ext = fname + strlen(fname); | |
strcpy(ext, ".exe"); | |
if ((exec_handle = (HANDLE)OpenFile(fname, file_info, | |
OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { | |
free(fname); | |
return(exec_handle); | |
} | |
strcpy(ext, ".bat"); | |
if ((exec_handle = (HANDLE)OpenFile(fname, file_info, | |
OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { | |
free(fname); | |
return(exec_handle); | |
} | |
strcpy(ext, ".com"); | |
if ((exec_handle = (HANDLE)OpenFile(fname, file_info, | |
OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { | |
free(fname); | |
return(exec_handle); | |
} | |
free(fname); | |
return(exec_handle); | |
} | |
/* | |
* Description: Create the child process to be helped | |
* | |
* Returns: | |
* | |
* Notes/Dependencies: | |
*/ | |
long | |
process_begin( | |
HANDLE proc, | |
char **argv, | |
char **envp, | |
char *exec_path, | |
char *as_user) | |
{ | |
sub_process *pproc = (sub_process *)proc; | |
char *shell_name = 0; | |
int file_not_found=0; | |
HANDLE exec_handle; | |
char buf[256]; | |
DWORD bytes_returned; | |
DWORD flags; | |
char *command_line; | |
STARTUPINFO startInfo; | |
PROCESS_INFORMATION procInfo; | |
char *envblk=NULL; | |
OFSTRUCT file_info; | |
/* | |
* Shell script detection... if the exec_path starts with #! then | |
* we want to exec shell-script-name exec-path, not just exec-path | |
* NT doesn't recognize #!/bin/sh or #!/etc/Tivoli/bin/perl. We do not | |
* hard-code the path to the shell or perl or whatever: Instead, we | |
* assume it's in the path somewhere (generally, the NT tools | |
* bin directory) | |
* We use OpenFile here because it is capable of searching the Path. | |
*/ | |
exec_handle = find_file(exec_path, &file_info); | |
/* | |
* If we couldn't open the file, just assume that Windows32 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_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, 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_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]); | |
(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_windows32_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 WINDOWS32 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; | |
} |