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_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_process_t* process, uv_process_options_t options) { int err = 0, i; wchar_t* path; int size; wchar_t* application_path, *application, *arguments, *env, *cwd; STARTUPINFOW startup; PROCESS_INFORMATION info; uv_process_init(process); process->exit_cb = options.exit_cb; UTF8_TO_UTF16(options.file, application); arguments = options.args ? make_program_args(options.args, options.windows_verbatim_arguments) : NULL; env = options.env ? make_program_env(options.env) : NULL; if (options.cwd) { UTF8_TO_UTF16(options.cwd, cwd); } else { size = GetCurrentDirectoryW(0, NULL) * sizeof(wchar_t); if (size) { cwd = (wchar_t*)malloc(size); if (!cwd) { uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); } GetCurrentDirectoryW(size, cwd); } else { uv_set_sys_error(GetLastError()); err = -1; goto done; } } /* Get PATH env. variable. */ size = GetEnvironmentVariableW(L"PATH", NULL, 0) + 1; path = (wchar_t*)malloc(size * sizeof(wchar_t)); if (!path) { uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); } GetEnvironmentVariableW(L"PATH", path, size * sizeof(wchar_t)); path[size - 1] = L'\0'; application_path = search_path(application, cwd, path, DEFAULT_PATH_EXT); if (!application_path) { /* CreateProcess will fail, but this allows us to pass this error to */ /* the user asynchronously. */ application_path = application; } /* Create stdio pipes. */ if (options.stdin_stream) { err = uv_create_stdio_pipe_pair(options.stdin_stream, &process->stdio_pipes[0].child_pipe, PIPE_ACCESS_OUTBOUND, GENERIC_READ | FILE_WRITE_ATTRIBUTES); if (err) { goto done; } process->stdio_pipes[0].server_pipe = options.stdin_stream; } if (options.stdout_stream) { err = uv_create_stdio_pipe_pair(options.stdout_stream, &process->stdio_pipes[1].child_pipe, PIPE_ACCESS_INBOUND, GENERIC_WRITE); if (err) { goto done; } process->stdio_pipes[1].server_pipe = options.stdout_stream; } if (options.stderr_stream) { err = uv_create_stdio_pipe_pair(options.stderr_stream, &process->stdio_pipes[2].child_pipe, PIPE_ACCESS_INBOUND, GENERIC_WRITE); if (err) { goto done; } process->stdio_pipes[2].server_pipe = options.stderr_stream; } startup.cb = sizeof(startup); startup.lpReserved = NULL; startup.lpDesktop = NULL; startup.lpTitle = NULL; startup.dwFlags = STARTF_USESTDHANDLES; startup.cbReserved2 = 0; startup.lpReserved2 = NULL; startup.hStdInput = process->stdio_pipes[0].child_pipe; startup.hStdOutput = process->stdio_pipes[1].child_pipe; startup.hStdError = process->stdio_pipes[2].child_pipe; if (CreateProcessW(application_path, arguments, NULL, NULL, 1, CREATE_UNICODE_ENVIRONMENT, env, cwd, &startup, &info)) { /* Spawn succeeded */ process->process_handle = info.hProcess; 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"); } CloseHandle(info.hThread); } else { /* 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: free(application); if (application_path != application) { free(application_path); } free(arguments); free(cwd); free(env); free(path); if (err) { for (i = 0; i < COUNTOF(process->stdio_pipes); i++) { if (process->stdio_pipes[i].child_pipe != INVALID_HANDLE_VALUE) { CloseHandle(process->stdio_pipes[i].child_pipe); process->stdio_pipes[i].child_pipe = INVALID_HANDLE_VALUE; } } if (process->wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(process->wait_handle); process->wait_handle = INVALID_HANDLE_VALUE; } if (process->process_handle != INVALID_HANDLE_VALUE) { CloseHandle(process->process_handle); process->process_handle = INVALID_HANDLE_VALUE; } } return err; }
int uv_spawn(uv_loop_t* loop, uv_process_t* process, uv_process_options_t options) { int err = 0, keep_child_stdio_open = 0; wchar_t* path = NULL; int size; BOOL result; wchar_t* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; HANDLE* child_stdio = process->child_stdio; STARTUPINFOW startup; PROCESS_INFORMATION info; if (!options.file) { uv__set_artificial_error(loop, UV_EINVAL); return -1; } uv_process_init(loop, process); process->exit_cb = options.exit_cb; UTF8_TO_UTF16(options.file, application); arguments = options.args ? make_program_args(options.args, options.windows_verbatim_arguments) : NULL; env = options.env ? make_program_env(options.env) : NULL; if (options.cwd) { UTF8_TO_UTF16(options.cwd, cwd); } else { size = GetCurrentDirectoryW(0, NULL) * sizeof(wchar_t); if (size) { cwd = (wchar_t*)malloc(size); if (!cwd) { uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); } GetCurrentDirectoryW(size, cwd); } else { uv__set_sys_error(loop, GetLastError()); err = -1; goto done; } } /* Get PATH env. variable. */ size = GetEnvironmentVariableW(L"PATH", NULL, 0) + 1; path = (wchar_t*)malloc(size * sizeof(wchar_t)); if (!path) { uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); } GetEnvironmentVariableW(L"PATH", path, size * sizeof(wchar_t)); path[size - 1] = L'\0'; application_path = search_path(application, cwd, path); if (!application_path) { /* CreateProcess will fail, but this allows us to pass this error to */ /* the user asynchronously. */ application_path = application; } /* Create stdio pipes. */ if (options.stdin_stream) { if (options.stdin_stream->ipc) { err = uv_create_stdio_pipe_pair( loop, options.stdin_stream, &child_stdio[0], PIPE_ACCESS_DUPLEX, GENERIC_READ | FILE_WRITE_ATTRIBUTES | GENERIC_WRITE, 1); } else { err = uv_create_stdio_pipe_pair( loop, options.stdin_stream, &child_stdio[0], PIPE_ACCESS_OUTBOUND, GENERIC_READ | FILE_WRITE_ATTRIBUTES, 0); } } else { err = duplicate_std_handle(loop, STD_INPUT_HANDLE, &child_stdio[0]); } if (err) { goto done; } if (options.stdout_stream) { err = uv_create_stdio_pipe_pair( loop, options.stdout_stream, &child_stdio[1], PIPE_ACCESS_INBOUND, GENERIC_WRITE, 0); } else { err = duplicate_std_handle(loop, STD_OUTPUT_HANDLE, &child_stdio[1]); } if (err) { goto done; } if (options.stderr_stream) { err = uv_create_stdio_pipe_pair( loop, options.stderr_stream, &child_stdio[2], PIPE_ACCESS_INBOUND, GENERIC_WRITE, 0); } else { err = duplicate_std_handle(loop, STD_ERROR_HANDLE, &child_stdio[2]); } if (err) { goto done; } startup.cb = sizeof(startup); startup.lpReserved = NULL; startup.lpDesktop = NULL; startup.lpTitle = NULL; startup.dwFlags = STARTF_USESTDHANDLES; startup.cbReserved2 = 0; startup.lpReserved2 = NULL; startup.hStdInput = child_stdio[0]; startup.hStdOutput = child_stdio[1]; startup.hStdError = child_stdio[2]; if (CreateProcessW(application_path, arguments, NULL, NULL, 1, CREATE_UNICODE_ENVIRONMENT, env, cwd, &startup, &info)) { /* Spawn succeeded */ process->process_handle = info.hProcess; process->pid = info.dwProcessId; if (options.stdin_stream && options.stdin_stream->ipc) { options.stdin_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, 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(); keep_child_stdio_open = 1; if (!QueueUserWorkItem(spawn_failure, process, WT_EXECUTEDEFAULT)) { uv_fatal_error(GetLastError(), "QueueUserWorkItem"); } } done: free(application); if (application_path != application) { free(application_path); } free(arguments); free(cwd); free(env); free(path); /* Under normal circumstances we should close the stdio handles now - */ /* the child now has its own duplicates, or something went horribly wrong. */ /* The only exception is when CreateProcess has failed, then we actually */ /* need to keep the stdio handles to report the error asynchronously. */ if (!keep_child_stdio_open) { close_child_stdio(process); } else { /* We're keeping the handles open, the thread pool is going to have */ /* it's way with them. But at least make them noninheritable. */ int i; for (i = 0; i < COUNTOF(process->child_stdio); i++) { SetHandleInformation(child_stdio[i], HANDLE_FLAG_INHERIT, 0); } } if (err) { if (process->wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(process->wait_handle); process->wait_handle = INVALID_HANDLE_VALUE; } if (process->process_handle != INVALID_HANDLE_VALUE) { CloseHandle(process->process_handle); process->process_handle = INVALID_HANDLE_VALUE; } } return err; }
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); }