|
@ -496,16 +496,16 @@ wchar_t* make_program_env(char** env_block) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
/*
|
|
|
* Called on Windows thread-pool thread to indicate that |
|
|
* Called on Windows thread-pool thread to indicate that |
|
|
* a child process has exited. |
|
|
* a child process has exited. |
|
|
*/ |
|
|
*/ |
|
|
static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { |
|
|
static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { |
|
|
uv_process_t* process = (uv_process_t*)data; |
|
|
uv_process_t* process = (uv_process_t*)data; |
|
|
|
|
|
|
|
|
assert(didTimeout == FALSE); |
|
|
assert(didTimeout == FALSE); |
|
|
assert(process); |
|
|
assert(process); |
|
|
|
|
|
|
|
|
memset(&process->exit_req.overlapped, 0, sizeof(process->exit_req.overlapped)); |
|
|
memset(&process->exit_req.overlapped, 0, sizeof(process->exit_req.overlapped)); |
|
|
|
|
|
|
|
|
/* Post completed */ |
|
|
/* Post completed */ |
|
@ -518,13 +518,13 @@ static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
/*
|
|
|
* Called on Windows thread-pool thread to indicate that |
|
|
* Called on Windows thread-pool thread to indicate that |
|
|
* UnregisterWaitEx has completed. |
|
|
* UnregisterWaitEx has completed. |
|
|
*/ |
|
|
*/ |
|
|
static void CALLBACK close_wait_callback(void* data, BOOLEAN didTimeout) { |
|
|
static void CALLBACK close_wait_callback(void* data, BOOLEAN didTimeout) { |
|
|
uv_process_t* process = (uv_process_t*)data; |
|
|
uv_process_t* process = (uv_process_t*)data; |
|
|
|
|
|
|
|
|
assert(didTimeout == FALSE); |
|
|
assert(didTimeout == FALSE); |
|
|
assert(process); |
|
|
assert(process); |
|
|
|
|
|
|
|
@ -540,6 +540,54 @@ static void CALLBACK close_wait_callback(void* data, BOOLEAN didTimeout) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Called on windows thread pool when CreateProcess failed. It writes an error |
|
|
|
|
|
* message to the process' intended stderr and then posts a PROCESS_EXIT |
|
|
|
|
|
* packet to the completion port. |
|
|
|
|
|
*/ |
|
|
|
|
|
static DWORD WINAPI spawn_failure(void* data) { |
|
|
|
|
|
char syscall[] = "CreateProcessW: "; |
|
|
|
|
|
char unknown[] = "unknown error\n"; |
|
|
|
|
|
uv_process_t* process = (uv_process_t*) data; |
|
|
|
|
|
HANDLE child_stderr = process->stdio_pipes[2].child_pipe; |
|
|
|
|
|
char* buf = NULL; |
|
|
|
|
|
DWORD count, written; |
|
|
|
|
|
|
|
|
|
|
|
WriteFile(child_stderr, syscall, sizeof(syscall) - 1, &written, NULL); |
|
|
|
|
|
|
|
|
|
|
|
count = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
|
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM | |
|
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS, |
|
|
|
|
|
NULL, |
|
|
|
|
|
process->spawn_errno, |
|
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
|
|
|
|
|
(LPSTR) &buf, |
|
|
|
|
|
0, |
|
|
|
|
|
NULL); |
|
|
|
|
|
|
|
|
|
|
|
if (buf != NULL && count > 0) { |
|
|
|
|
|
WriteFile(child_stderr, buf, count, &written, NULL); |
|
|
|
|
|
LocalFree(buf); |
|
|
|
|
|
} else { |
|
|
|
|
|
WriteFile(child_stderr, unknown, sizeof(unknown) - 1, &written, NULL); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
FlushFileBuffers(child_stderr); |
|
|
|
|
|
|
|
|
|
|
|
memset(&process->exit_req.overlapped, 0, sizeof(process->exit_req.overlapped)); |
|
|
|
|
|
|
|
|
|
|
|
/* Post completed */ |
|
|
|
|
|
if (!PostQueuedCompletionStatus(LOOP->iocp, |
|
|
|
|
|
0, |
|
|
|
|
|
0, |
|
|
|
|
|
&process->exit_req.overlapped)) { |
|
|
|
|
|
uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Called on main thread after a child process has exited. */ |
|
|
/* Called on main thread after a child process has exited. */ |
|
|
void uv_process_proc_exit(uv_process_t* handle) { |
|
|
void uv_process_proc_exit(uv_process_t* handle) { |
|
|
int i; |
|
|
int i; |
|
@ -559,15 +607,18 @@ void uv_process_proc_exit(uv_process_t* handle) { |
|
|
handle->wait_handle = INVALID_HANDLE_VALUE; |
|
|
handle->wait_handle = INVALID_HANDLE_VALUE; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* Clean-up the process handle. */ |
|
|
|
|
|
if (handle->process_handle != INVALID_HANDLE_VALUE) { |
|
|
if (handle->process_handle != INVALID_HANDLE_VALUE) { |
|
|
/* Get the exit code. */ |
|
|
/* Get the exit code. */ |
|
|
if (!GetExitCodeProcess(handle->process_handle, &exit_code)) { |
|
|
if (!GetExitCodeProcess(handle->process_handle, &exit_code)) { |
|
|
exit_code = 1; |
|
|
exit_code = 127; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Clean-up the process handle. */ |
|
|
CloseHandle(handle->process_handle); |
|
|
CloseHandle(handle->process_handle); |
|
|
handle->process_handle = INVALID_HANDLE_VALUE; |
|
|
handle->process_handle = INVALID_HANDLE_VALUE; |
|
|
|
|
|
} else { |
|
|
|
|
|
/* The process never even started in the first place. */ |
|
|
|
|
|
exit_code = 127; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* Fire the exit callback. */ |
|
|
/* Fire the exit callback. */ |
|
@ -618,10 +669,10 @@ static int uv_create_stdio_pipe_pair(uv_pipe_t* server_pipe, HANDLE* child_pipe, |
|
|
char pipe_name[64]; |
|
|
char pipe_name[64]; |
|
|
DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; |
|
|
DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; |
|
|
|
|
|
|
|
|
if (server_pipe->type != UV_NAMED_PIPE) { |
|
|
if (server_pipe->type != UV_NAMED_PIPE) { |
|
|
uv_set_error(UV_EINVAL, 0); |
|
|
uv_set_error(UV_EINVAL, 0); |
|
|
err = -1; |
|
|
err = -1; |
|
|
goto done; |
|
|
goto done; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* Create server pipe handle. */ |
|
|
/* Create server pipe handle. */ |
|
@ -681,13 +732,13 @@ done: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int uv_spawn(uv_process_t* process, uv_process_options_t options) { |
|
|
int uv_spawn(uv_process_t* process, uv_process_options_t options) { |
|
|
int err, i; |
|
|
int err = 0, i; |
|
|
wchar_t* path; |
|
|
wchar_t* path; |
|
|
int size; |
|
|
int size; |
|
|
wchar_t* application_path, *application, *arguments, *env, *cwd; |
|
|
wchar_t* application_path, *application, *arguments, *env, *cwd; |
|
|
STARTUPINFOW startup; |
|
|
STARTUPINFOW startup; |
|
|
PROCESS_INFORMATION info; |
|
|
PROCESS_INFORMATION info; |
|
|
|
|
|
|
|
|
uv_process_init(process); |
|
|
uv_process_init(process); |
|
|
|
|
|
|
|
|
process->exit_cb = options.exit_cb; |
|
|
process->exit_cb = options.exit_cb; |
|
@ -721,15 +772,15 @@ int uv_spawn(uv_process_t* process, uv_process_options_t options) { |
|
|
GetEnvironmentVariableW(L"PATH", path, size * sizeof(wchar_t)); |
|
|
GetEnvironmentVariableW(L"PATH", path, size * sizeof(wchar_t)); |
|
|
path[size - 1] = L'\0'; |
|
|
path[size - 1] = L'\0'; |
|
|
|
|
|
|
|
|
application_path = search_path(application, |
|
|
application_path = search_path(application, |
|
|
cwd, |
|
|
cwd, |
|
|
path, |
|
|
path, |
|
|
DEFAULT_PATH_EXT); |
|
|
DEFAULT_PATH_EXT); |
|
|
|
|
|
|
|
|
if (!application_path) { |
|
|
if (!application_path) { |
|
|
uv_set_error(UV_EINVAL, 0); |
|
|
/* CreateProcess will fail, but this allows us to pass this error to */ |
|
|
err = -1; |
|
|
/* the user asynchronously. */ |
|
|
goto done; |
|
|
application_path = application; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* Create stdio pipes. */ |
|
|
/* Create stdio pipes. */ |
|
@ -771,39 +822,45 @@ int uv_spawn(uv_process_t* process, uv_process_options_t options) { |
|
|
startup.hStdOutput = process->stdio_pipes[1].child_pipe; |
|
|
startup.hStdOutput = process->stdio_pipes[1].child_pipe; |
|
|
startup.hStdError = process->stdio_pipes[2].child_pipe; |
|
|
startup.hStdError = process->stdio_pipes[2].child_pipe; |
|
|
|
|
|
|
|
|
if (!CreateProcessW(application_path, |
|
|
if (CreateProcessW(application_path, |
|
|
arguments, |
|
|
arguments, |
|
|
NULL, |
|
|
NULL, |
|
|
NULL, |
|
|
NULL, |
|
|
1, |
|
|
1, |
|
|
CREATE_UNICODE_ENVIRONMENT, |
|
|
CREATE_UNICODE_ENVIRONMENT, |
|
|
env, |
|
|
env, |
|
|
cwd, |
|
|
cwd, |
|
|
&startup, |
|
|
&startup, |
|
|
&info)) { |
|
|
&info)) { |
|
|
uv_set_sys_error(GetLastError()); |
|
|
/* Spawn succeeded */ |
|
|
err = -1; |
|
|
process->process_handle = info.hProcess; |
|
|
goto done; |
|
|
process->pid = info.dwProcessId; |
|
|
} |
|
|
|
|
|
|
|
|
/* Setup notifications for when the child process exits. */ |
|
|
|
|
|
if (!RegisterWaitForSingleObject(&process->wait_handle, process->process_handle, |
|
|
|
|
|
exit_wait_callback, (void*)process, INFINITE, |
|
|
|
|
|
WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) { |
|
|
|
|
|
uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
process->process_handle = info.hProcess; |
|
|
CloseHandle(info.hThread); |
|
|
process->pid = info.dwProcessId; |
|
|
|
|
|
|
|
|
|
|
|
/* Setup notifications for when the child process exits. */ |
|
|
|
|
|
if (!RegisterWaitForSingleObject(&process->wait_handle, process->process_handle, |
|
|
|
|
|
exit_wait_callback, (void*)process, INFINITE, |
|
|
|
|
|
WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) { |
|
|
|
|
|
uv_set_sys_error(GetLastError()); |
|
|
|
|
|
err = -1; |
|
|
|
|
|
goto done; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
CloseHandle(info.hThread); |
|
|
} else { |
|
|
err = 0; |
|
|
/* CreateProcessW failed, but this failure should be delivered */ |
|
|
|
|
|
/* asynchronously to retain unix compatibility. So pretent spawn */ |
|
|
|
|
|
/* succeeded, and start a thread instead that prints an error */ |
|
|
|
|
|
/* to the child's intended stderr. */ |
|
|
|
|
|
process->spawn_errno = GetLastError(); |
|
|
|
|
|
if (!QueueUserWorkItem(spawn_failure, process, WT_EXECUTEDEFAULT)) { |
|
|
|
|
|
uv_fatal_error(GetLastError(), "QueueUserWorkItem"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
done: |
|
|
done: |
|
|
free(application_path); |
|
|
|
|
|
free(application); |
|
|
free(application); |
|
|
|
|
|
if (application_path != application) { |
|
|
|
|
|
free(application_path); |
|
|
|
|
|
} |
|
|
free(arguments); |
|
|
free(arguments); |
|
|
free(cwd); |
|
|
free(cwd); |
|
|
free(env); |
|
|
free(env); |
|
|