int uv__stdio_create(uv_loop_t* loop, uv_process_options_t* options, BYTE** buffer_ptr) { BYTE* buffer; int count, i; count = options->stdio_count; if (count < 0 || count > 255) { /* Only support FDs 0-255 */ uv__set_artificial_error(loop, UV_ENOTSUP); return -1; } else if (count < 3) { /* There should always be at least 3 stdio handles. */ count = 3; } /* Allocate the child stdio buffer */ buffer = malloc(CHILD_STDIO_SIZE(count)); if (buffer == NULL) { uv__set_artificial_error(loop, UV_ENOMEM); return -1; } /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can */ /* clean up on failure. */ CHILD_STDIO_COUNT(buffer) = count; for (i = 0; i < count; i++) { CHILD_STDIO_CRT_FLAGS(buffer, i) = 0; CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE; } for (i = 0; i < count; i++) { uv_stdio_container_t fdopt; if (i < options->stdio_count) { fdopt = options->stdio[i]; } else { fdopt.flags = UV_IGNORE; } switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM)) { case UV_IGNORE: /* Starting a process with no stdin/stout/stderr can confuse it. */ /* So no matter what the user specified, we make sure the first */ /* three FDs are always open in their typical modes, e.g. stdin */ /* be readable and stdout/err should be writable. For FDs > 2, don't */ /* do anything - all handles in the stdio buffer are initialized with */ /* INVALID_HANDLE_VALUE, which should be okay. */ if (i <= 2) { DWORD access = (i == 0) ? FILE_GENERIC_READ : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES; if (uv__create_nul_handle(loop, &CHILD_STDIO_HANDLE(buffer, i), access) < 0) { goto error; } CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; } break; case UV_CREATE_PIPE: { /* Create a pair of two connected pipe ends; one end is turned into */ /* an uv_pipe_t for use by the parent. The other one is given to */ /* the child. */ uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream; HANDLE child_pipe; /* Create a new, connected pipe pair. stdio[i].stream should point */ /* to an uninitialized, but not connected pipe handle. */ assert(fdopt.data.stream->type == UV_NAMED_PIPE); assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION)); assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER)); if (uv__create_stdio_pipe_pair(loop, parent_pipe, &child_pipe, fdopt.flags) < 0) { goto error; } CHILD_STDIO_HANDLE(buffer, i) = child_pipe; CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; break; } case UV_INHERIT_FD: { /* Inherit a raw FD. */ HANDLE child_handle; /* Make an inheritable duplicate of the handle. */ if (uv__duplicate_fd(loop, fdopt.data.fd, &child_handle) < 0) { goto error; } /* Figure out what the type is. */ switch (GetFileType(child_handle)) { case FILE_TYPE_DISK: CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN; break; case FILE_TYPE_PIPE: CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; case FILE_TYPE_CHAR: case FILE_TYPE_REMOTE: CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; break; case FILE_TYPE_UNKNOWN: if (GetLastError != 0) { uv__set_sys_error(loop, GetLastError()); CloseHandle(child_handle); goto error; } CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV; break; default: assert(0); } CHILD_STDIO_HANDLE(buffer, i) = child_handle; break; } case UV_INHERIT_STREAM: { /* Use an existing stream as the stdio handle for the child. */ HANDLE stream_handle, child_handle; unsigned char crt_flags; uv_stream_t* stream = fdopt.data.stream; /* Leech the handle out of the stream. */ if (stream->type == UV_TTY) { stream_handle = ((uv_tty_t*) stream)->handle; crt_flags = FOPEN | FDEV; } else if (stream->type == UV_NAMED_PIPE && stream->flags & UV_HANDLE_CONNECTED) { stream_handle = ((uv_pipe_t*) stream)->handle; crt_flags = FOPEN | FPIPE; } else { stream_handle = INVALID_HANDLE_VALUE; crt_flags = 0; } if (stream_handle == NULL || stream_handle == INVALID_HANDLE_VALUE) { /* The handle is already closed, or not yet created, or the */ /* stream type is not supported. */ uv__set_artificial_error(loop, UV_ENOTSUP); goto error; } /* Make an inheritable copy of the handle. */ if (uv__duplicate_handle(loop, stream_handle, &child_handle) < 0) { goto error; } CHILD_STDIO_HANDLE(buffer, i) = child_handle; CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags; break; } default: assert(0); } } *buffer_ptr = buffer; return 0; error: uv__stdio_destroy(buffer); return -1; }
int uv_spawn_jx(uv_loop_t* loop, uv_process_t* process, uv_process_options_t* options) { #ifdef WINONECORE error_console("Error: WindowsOneCore does not support spawning a process.\n"); return -1; // not supported #else int i; uv_err_t err = uv_ok_; WCHAR* path = NULL; BOOL result; WCHAR* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; STARTUPINFOW startup; PROCESS_INFORMATION info; DWORD process_flags; if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { uv__set_artificial_error(loop, UV_ENOTSUP); return -1; } if (options->file == NULL || options->args == NULL) { uv__set_artificial_error(loop, UV_EINVAL); return -1; } assert(options->file != NULL); assert(!(options->flags & ~(UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID | UV_PROCESS_WINDOWS_HIDE | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); uv_process_init(loop, process); process->exit_cb = options->exit_cb; err = uv_utf8_to_utf16_alloc(options->file, &application); if (err.code != UV_OK) goto done; err = make_program_args( options->args, options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, &arguments); if (err.code != UV_OK) goto done; if (options->env) { err = make_program_env(options->env, &env); if (err.code != UV_OK) goto done; } if (options->cwd) { /* Explicit cwd */ err = uv_utf8_to_utf16_alloc(options->cwd, &cwd); if (err.code != UV_OK) goto done; } else { /* Inherit cwd */ DWORD cwd_len, r; cwd_len = GetCurrentDirectoryW(0, NULL); if (!cwd_len) { err = uv__new_sys_error(GetLastError()); goto done; } cwd = (WCHAR*)malloc(cwd_len * sizeof(WCHAR)); if (cwd == NULL) { err = uv__new_artificial_error(UV_ENOMEM); goto done; } r = GetCurrentDirectoryW(cwd_len, cwd); if (r == 0 || r >= cwd_len) { err = uv__new_sys_error(GetLastError()); goto done; } } /* Get PATH environment variable. */ { DWORD path_len, r; path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); if (path_len == 0) { err = uv__new_sys_error(GetLastError()); goto done; } path = (WCHAR*)malloc(path_len * sizeof(WCHAR)); if (path == NULL) { err = uv__new_artificial_error(UV_ENOMEM); goto done; } r = GetEnvironmentVariableW(L"PATH", path, path_len); if (r == 0 || r >= path_len) { err = uv__new_sys_error(GetLastError()); goto done; } } application_path = search_path(application, cwd, path); if (application_path == NULL) { /* Not found. */ err = uv__new_artificial_error(UV_ENOENT); goto done; } err = uv__stdio_create(loop, options, &process->child_stdio_buffer); if (err.code != UV_OK) goto done; startup.cb = sizeof(startup); startup.lpReserved = NULL; startup.lpDesktop = NULL; startup.lpTitle = NULL; startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer); startup.lpReserved2 = (BYTE*)process->child_stdio_buffer; startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0); startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); if (options->flags & UV_PROCESS_WINDOWS_HIDE) { /* Use SW_HIDE to avoid any potential process window. */ startup.wShowWindow = SW_HIDE; } else { startup.wShowWindow = SW_SHOWDEFAULT; } process_flags = CREATE_UNICODE_ENVIRONMENT; if (options->flags & UV_PROCESS_DETACHED) { /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That * means that libuv might not let you create a fully deamonized process * when run under job control. However the type of job control that libuv * itself creates doesn't trickle down to subprocesses so they can still * daemonize. * * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the * CreateProcess call fail if we're under job control that doesn't allow * breakaway. */ process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; } if (CreateProcessW(application_path, arguments, NULL, NULL, 1, process_flags, env, cwd, &startup, &info)) { /* Spawn succeeded */ process->process_handle = info.hProcess; process->pid = info.dwProcessId; /* If the process isn't spawned as detached, assign to the global job */ /* object so windows will kill it when the parent process dies. */ if (!(options->flags & UV_PROCESS_DETACHED)) { uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle); if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) { /* AssignProcessToJobObject might fail if this process is under job * control and the job doesn't have the * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version * that doesn't support nested jobs. * * When that happens we just swallow the error and continue without * establishing a kill-child-on-parent-exit relationship, otherwise * there would be no way for libuv applications run under job control * to spawn processes at all. */ DWORD err = GetLastError(); if (err != ERROR_ACCESS_DENIED) uv_fatal_error(err, "AssignProcessToJobObject"); } } /* Set IPC pid to all IPC pipes. */ for (i = 0; i < options->stdio_count; i++) { const uv_stdio_container_t* fdopt = &options->stdio[i]; if (fdopt->flags & UV_CREATE_PIPE && fdopt->data.stream->type == UV_NAMED_PIPE && ((uv_pipe_t*)fdopt->data.stream)->ipc) { ((uv_pipe_t*)fdopt->data.stream)->ipc_pid = info.dwProcessId; } } /* Setup notifications for when the child process exits. */ result = RegisterWaitForSingleObject( &process->wait_handle, process->process_handle, exit_wait_callback, (void*)process, INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); if (!result) { uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); } CloseHandle(info.hThread); } else { /* CreateProcessW failed. */ err = uv__new_sys_error(GetLastError()); } done: JX_FREE(process, application); JX_FREE(process, application_path); JX_FREE(process, arguments); JX_FREE(process, cwd); JX_FREE(process, env); JX_FREE(process, path); process->spawn_error = err; if (process->child_stdio_buffer != NULL) { /* Clean up child stdio handles. */ uv__stdio_destroy(process->child_stdio_buffer); process->child_stdio_buffer = NULL; } /* Make the handle active. It will remain active until the exit callback */ /* is made or the handle is closed, whichever happens first. */ uv__handle_start(process); /* If an error happened, queue the exit req. */ if (err.code != UV_OK) { process->exit_cb_pending = 1; uv_insert_pending_req(loop, (uv_req_t*)&process->exit_req); } return 0; #endif }
int uv_spawn(uv_loop_t* loop, uv_process_t* process, uv_process_options_t options) { int i; uv_err_t err = uv_ok_; WCHAR* path = NULL; BOOL result; WCHAR* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; STARTUPINFOW startup; PROCESS_INFORMATION info; DWORD process_flags; if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { uv__set_artificial_error(loop, UV_ENOTSUP); return -1; } if (options.file == NULL || options.args == NULL) { uv__set_artificial_error(loop, UV_EINVAL); return -1; } assert(options.file != NULL); assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID))); uv_process_init(loop, process); process->exit_cb = options.exit_cb; err = uv_utf8_to_utf16_alloc(options.file, &application); if (err.code != UV_OK) goto done; err = make_program_args(options.args, options.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, &arguments); if (err.code != UV_OK) goto done; if (options.cwd) { /* Explicit cwd */ err = uv_utf8_to_utf16_alloc(options.cwd, &cwd); if (err.code != UV_OK) goto done; } else { /* Inherit cwd */ DWORD cwd_len, r; cwd_len = GetCurrentDirectoryW(0, NULL); if (!cwd_len) { err = uv__new_sys_error(GetLastError()); goto done; } cwd = (WCHAR*) malloc(cwd_len * sizeof(WCHAR)); if (cwd == NULL) { err = uv__new_artificial_error(UV_ENOMEM); goto done; } r = GetCurrentDirectoryW(cwd_len, cwd); if (r == 0 || r >= cwd_len) { err = uv__new_sys_error(GetLastError()); goto done; } } /* Get PATH environment variable. */ { DWORD path_len, r; path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); if (path_len == 0) { err = uv__new_sys_error(GetLastError()); goto done; } path = (WCHAR*) malloc(path_len * sizeof(WCHAR)); if (path == NULL) { err = uv__new_artificial_error(UV_ENOMEM); goto done; } r = GetEnvironmentVariableW(L"PATH", path, path_len); if (r == 0 || r >= path_len) { err = uv__new_sys_error(GetLastError()); goto done; } } application_path = search_path(application, cwd, path); if (application_path == NULL) { /* Not found. */ err = uv__new_artificial_error(UV_ENOENT); goto done; } err = uv__stdio_create(loop, &options, &process->child_stdio_buffer); if (err.code != UV_OK) goto done; startup.cb = sizeof(startup); startup.lpReserved = NULL; startup.lpDesktop = NULL; startup.lpTitle = NULL; startup.dwFlags = STARTF_USESTDHANDLES; startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer); startup.lpReserved2 = (BYTE*) process->child_stdio_buffer; startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0); startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); process_flags = CREATE_UNICODE_ENVIRONMENT; if (options.flags & UV_PROCESS_DETACHED) { process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; } if (CreateProcessW(application_path, arguments, NULL, NULL, 1, process_flags, env, cwd, &startup, &info)) { /* Spawn succeeded */ process->process_handle = info.hProcess; process->pid = info.dwProcessId; /* Set IPC pid to all IPC pipes. */ for (i = 0; i < options.stdio_count; i++) { const uv_stdio_container_t* fdopt = &options.stdio[i]; if (fdopt->flags & UV_CREATE_PIPE && fdopt->data.stream->type == UV_NAMED_PIPE && ((uv_pipe_t*) fdopt->data.stream)->ipc) { ((uv_pipe_t*) fdopt->data.stream)->ipc_pid = info.dwProcessId; } } /* Setup notifications for when the child process exits. */ result = RegisterWaitForSingleObject(&process->wait_handle, process->process_handle, exit_wait_callback, (void*)process, INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); if (!result) { uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); } CloseHandle(info.hThread); } else { /* CreateProcessW failed. */ err = uv__new_sys_error(GetLastError()); } done: free(application); free(application_path); free(arguments); free(cwd); free(env); free(path); process->spawn_error = err; if (process->child_stdio_buffer != NULL) { /* Clean up child stdio handles. */ uv__stdio_destroy(process->child_stdio_buffer); process->child_stdio_buffer = NULL; } /* Make the handle active. It will remain active until the exit callback */ /* is made or the handle is closed, whichever happens first. */ uv__handle_start(process); /* If an error happened, queue the exit req. */ if (err.code != UV_OK) { process->exit_cb_pending = 1; uv_insert_pending_req(loop, (uv_req_t*) &process->exit_req); } return 0; }
int uv_spawn(uv_loop_t* loop, uv_process_t* process, const uv_process_options_t* options) { int i; int err = 0; WCHAR* path = NULL, *alloc_path = NULL; BOOL result; WCHAR* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; STARTUPINFOW startup; PROCESS_INFORMATION info; DWORD process_flags; uv_process_init(loop, process); process->exit_cb = options->exit_cb; if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { return UV_ENOTSUP; } if (options->file == NULL || options->args == NULL) { return UV_EINVAL; } if (options->cpumask != NULL) { if (options->cpumask_size < (size_t)uv_cpumask_size()) { return UV_EINVAL; } } assert(options->file != NULL); assert(!(options->flags & ~(UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID | UV_PROCESS_WINDOWS_HIDE | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); err = uv_utf8_to_utf16_alloc(options->file, &application); if (err) goto done; err = make_program_args( options->args, options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, &arguments); if (err) goto done; if (options->env) { err = make_program_env(options->env, &env); if (err) goto done; } if (options->cwd) { /* Explicit cwd */ err = uv_utf8_to_utf16_alloc(options->cwd, &cwd); if (err) goto done; } else { /* Inherit cwd */ DWORD cwd_len, r; cwd_len = GetCurrentDirectoryW(0, NULL); if (!cwd_len) { err = GetLastError(); goto done; } cwd = (WCHAR*) uv__malloc(cwd_len * sizeof(WCHAR)); if (cwd == NULL) { err = ERROR_OUTOFMEMORY; goto done; } r = GetCurrentDirectoryW(cwd_len, cwd); if (r == 0 || r >= cwd_len) { err = GetLastError(); goto done; } } /* Get PATH environment variable. */ path = find_path(env); if (path == NULL) { DWORD path_len, r; path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); if (path_len == 0) { err = GetLastError(); goto done; } alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR)); if (alloc_path == NULL) { err = ERROR_OUTOFMEMORY; goto done; } path = alloc_path; r = GetEnvironmentVariableW(L"PATH", path, path_len); if (r == 0 || r >= path_len) { err = GetLastError(); goto done; } } err = uv__stdio_create(loop, options, &process->child_stdio_buffer); if (err) goto done; application_path = search_path(application, cwd, path); if (application_path == NULL) { /* Not found. */ err = ERROR_FILE_NOT_FOUND; goto done; } startup.cb = sizeof(startup); startup.lpReserved = NULL; startup.lpDesktop = NULL; startup.lpTitle = NULL; startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer); startup.lpReserved2 = (BYTE*) process->child_stdio_buffer; startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0); startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); process_flags = CREATE_UNICODE_ENVIRONMENT; if (options->flags & UV_PROCESS_WINDOWS_HIDE) { /* Avoid creating console window if stdio is not inherited. */ for (i = 0; i < options->stdio_count; i++) { if (options->stdio[i].flags & UV_INHERIT_FD) break; if (i == options->stdio_count - 1) process_flags |= CREATE_NO_WINDOW; } /* Use SW_HIDE to avoid any potential process window. */ startup.wShowWindow = SW_HIDE; } else { startup.wShowWindow = SW_SHOWDEFAULT; } if (options->flags & UV_PROCESS_DETACHED) { /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That * means that libuv might not let you create a fully daemonized process * when run under job control. However the type of job control that libuv * itself creates doesn't trickle down to subprocesses so they can still * daemonize. * * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the * CreateProcess call fail if we're under job control that doesn't allow * breakaway. */ process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; } if (options->cpumask != NULL) { /* Create the child in a suspended state so we have a chance to set its process affinity before it runs. */ process_flags |= CREATE_SUSPENDED; } if (!CreateProcessW(application_path, arguments, NULL, NULL, 1, process_flags, env, cwd, &startup, &info)) { /* CreateProcessW failed. */ err = GetLastError(); goto done; } if (options->cpumask != NULL) { /* The child is currently suspended. Set its process affinity or terminate it if we can't. */ int i; int cpumasksize; DWORD_PTR sysmask; DWORD_PTR oldmask; DWORD_PTR newmask; cpumasksize = uv_cpumask_size(); if (!GetProcessAffinityMask(info.hProcess, &oldmask, &sysmask)) { err = GetLastError(); TerminateProcess(info.hProcess, 1); goto done; } newmask = 0; for (i = 0; i < cpumasksize; i++) { if (options->cpumask[i]) { if (oldmask & (((DWORD_PTR)1) << i)) { newmask |= ((DWORD_PTR)1) << i; } else { err = UV_EINVAL; TerminateProcess(info.hProcess, 1); goto done; } } } if (!SetProcessAffinityMask(info.hProcess, newmask)) { err = GetLastError(); TerminateProcess(info.hProcess, 1); goto done; } /* The process affinity of the child is set. Let it run. */ if (ResumeThread(info.hThread) == ((DWORD)-1)) { err = GetLastError(); TerminateProcess(info.hProcess, 1); goto done; } } /* Spawn succeeded */ /* Beyond this point, failure is reported asynchronously. */ process->process_handle = info.hProcess; process->pid = info.dwProcessId; /* If the process isn't spawned as detached, assign to the global job */ /* object so windows will kill it when the parent process dies. */ if (!(options->flags & UV_PROCESS_DETACHED)) { uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle); if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) { /* AssignProcessToJobObject might fail if this process is under job * control and the job doesn't have the * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version * that doesn't support nested jobs. * * When that happens we just swallow the error and continue without * establishing a kill-child-on-parent-exit relationship, otherwise * there would be no way for libuv applications run under job control * to spawn processes at all. */ DWORD err = GetLastError(); if (err != ERROR_ACCESS_DENIED) uv_fatal_error(err, "AssignProcessToJobObject"); } } /* Set IPC pid to all IPC pipes. */ for (i = 0; i < options->stdio_count; i++) { const uv_stdio_container_t* fdopt = &options->stdio[i]; if (fdopt->flags & UV_CREATE_PIPE && fdopt->data.stream->type == UV_NAMED_PIPE && ((uv_pipe_t*) fdopt->data.stream)->ipc) { ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_pid = info.dwProcessId; } } /* Setup notifications for when the child process exits. */ result = RegisterWaitForSingleObject(&process->wait_handle, process->process_handle, exit_wait_callback, (void*)process, INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); if (!result) { uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); } CloseHandle(info.hThread); assert(!err); /* Make the handle active. It will remain active until the exit callback */ /* is made or the handle is closed, whichever happens first. */ uv__handle_start(process); /* Cleanup, whether we succeeded or failed. */ done: uv__free(application); uv__free(application_path); uv__free(arguments); uv__free(cwd); uv__free(env); uv__free(alloc_path); if (process->child_stdio_buffer != NULL) { /* Clean up child stdio handles. */ uv__stdio_destroy(process->child_stdio_buffer); process->child_stdio_buffer = NULL; } return uv_translate_sys_error(err); }