11cb0ef41Sopenharmony_ci/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 21cb0ef41Sopenharmony_ci * 31cb0ef41Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy 41cb0ef41Sopenharmony_ci * of this software and associated documentation files (the "Software"), to 51cb0ef41Sopenharmony_ci * deal in the Software without restriction, including without limitation the 61cb0ef41Sopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 71cb0ef41Sopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is 81cb0ef41Sopenharmony_ci * furnished to do so, subject to the following conditions: 91cb0ef41Sopenharmony_ci * 101cb0ef41Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 111cb0ef41Sopenharmony_ci * all copies or substantial portions of the Software. 121cb0ef41Sopenharmony_ci * 131cb0ef41Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 141cb0ef41Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 151cb0ef41Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 161cb0ef41Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 171cb0ef41Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 181cb0ef41Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 191cb0ef41Sopenharmony_ci * IN THE SOFTWARE. 201cb0ef41Sopenharmony_ci */ 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ci#include <assert.h> 231cb0ef41Sopenharmony_ci#include <io.h> 241cb0ef41Sopenharmony_ci#include <stdio.h> 251cb0ef41Sopenharmony_ci#include <stdlib.h> 261cb0ef41Sopenharmony_ci#include <signal.h> 271cb0ef41Sopenharmony_ci#include <limits.h> 281cb0ef41Sopenharmony_ci#include <wchar.h> 291cb0ef41Sopenharmony_ci#include <malloc.h> /* alloca */ 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_ci#include "uv.h" 321cb0ef41Sopenharmony_ci#include "internal.h" 331cb0ef41Sopenharmony_ci#include "handle-inl.h" 341cb0ef41Sopenharmony_ci#include "req-inl.h" 351cb0ef41Sopenharmony_ci 361cb0ef41Sopenharmony_ci 371cb0ef41Sopenharmony_ci#define SIGKILL 9 381cb0ef41Sopenharmony_ci 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_citypedef struct env_var { 411cb0ef41Sopenharmony_ci const WCHAR* const wide; 421cb0ef41Sopenharmony_ci const WCHAR* const wide_eq; 431cb0ef41Sopenharmony_ci const size_t len; /* including null or '=' */ 441cb0ef41Sopenharmony_ci} env_var_t; 451cb0ef41Sopenharmony_ci 461cb0ef41Sopenharmony_ci#define E_V(str) { L##str, L##str L"=", sizeof(str) } 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_cistatic const env_var_t required_vars[] = { /* keep me sorted */ 491cb0ef41Sopenharmony_ci E_V("HOMEDRIVE"), 501cb0ef41Sopenharmony_ci E_V("HOMEPATH"), 511cb0ef41Sopenharmony_ci E_V("LOGONSERVER"), 521cb0ef41Sopenharmony_ci E_V("PATH"), 531cb0ef41Sopenharmony_ci E_V("SYSTEMDRIVE"), 541cb0ef41Sopenharmony_ci E_V("SYSTEMROOT"), 551cb0ef41Sopenharmony_ci E_V("TEMP"), 561cb0ef41Sopenharmony_ci E_V("USERDOMAIN"), 571cb0ef41Sopenharmony_ci E_V("USERNAME"), 581cb0ef41Sopenharmony_ci E_V("USERPROFILE"), 591cb0ef41Sopenharmony_ci E_V("WINDIR"), 601cb0ef41Sopenharmony_ci}; 611cb0ef41Sopenharmony_ci 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_cistatic HANDLE uv_global_job_handle_; 641cb0ef41Sopenharmony_cistatic uv_once_t uv_global_job_handle_init_guard_ = UV_ONCE_INIT; 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_cistatic void uv__init_global_job_handle(void) { 681cb0ef41Sopenharmony_ci /* Create a job object and set it up to kill all contained processes when 691cb0ef41Sopenharmony_ci * it's closed. Since this handle is made non-inheritable and we're not 701cb0ef41Sopenharmony_ci * giving it to anyone, we're the only process holding a reference to it. 711cb0ef41Sopenharmony_ci * That means that if this process exits it is closed and all the processes 721cb0ef41Sopenharmony_ci * it contains are killed. All processes created with uv_spawn that are not 731cb0ef41Sopenharmony_ci * spawned with the UV_PROCESS_DETACHED flag are assigned to this job. 741cb0ef41Sopenharmony_ci * 751cb0ef41Sopenharmony_ci * We're setting the JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag so only the 761cb0ef41Sopenharmony_ci * processes that we explicitly add are affected, and *their* subprocesses 771cb0ef41Sopenharmony_ci * are not. This ensures that our child processes are not limited in their 781cb0ef41Sopenharmony_ci * ability to use job control on Windows versions that don't deal with 791cb0ef41Sopenharmony_ci * nested jobs (prior to Windows 8 / Server 2012). It also lets our child 801cb0ef41Sopenharmony_ci * processes created detached processes without explicitly breaking away 811cb0ef41Sopenharmony_ci * from job control (which uv_spawn doesn't, either). 821cb0ef41Sopenharmony_ci */ 831cb0ef41Sopenharmony_ci SECURITY_ATTRIBUTES attr; 841cb0ef41Sopenharmony_ci JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci memset(&attr, 0, sizeof attr); 871cb0ef41Sopenharmony_ci attr.bInheritHandle = FALSE; 881cb0ef41Sopenharmony_ci 891cb0ef41Sopenharmony_ci memset(&info, 0, sizeof info); 901cb0ef41Sopenharmony_ci info.BasicLimitInformation.LimitFlags = 911cb0ef41Sopenharmony_ci JOB_OBJECT_LIMIT_BREAKAWAY_OK | 921cb0ef41Sopenharmony_ci JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | 931cb0ef41Sopenharmony_ci JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | 941cb0ef41Sopenharmony_ci JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci uv_global_job_handle_ = CreateJobObjectW(&attr, NULL); 971cb0ef41Sopenharmony_ci if (uv_global_job_handle_ == NULL) 981cb0ef41Sopenharmony_ci uv_fatal_error(GetLastError(), "CreateJobObjectW"); 991cb0ef41Sopenharmony_ci 1001cb0ef41Sopenharmony_ci if (!SetInformationJobObject(uv_global_job_handle_, 1011cb0ef41Sopenharmony_ci JobObjectExtendedLimitInformation, 1021cb0ef41Sopenharmony_ci &info, 1031cb0ef41Sopenharmony_ci sizeof info)) 1041cb0ef41Sopenharmony_ci uv_fatal_error(GetLastError(), "SetInformationJobObject"); 1051cb0ef41Sopenharmony_ci} 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_ci 1081cb0ef41Sopenharmony_cistatic int uv__utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) { 1091cb0ef41Sopenharmony_ci int ws_len, r; 1101cb0ef41Sopenharmony_ci WCHAR* ws; 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_ci ws_len = MultiByteToWideChar(CP_UTF8, 1131cb0ef41Sopenharmony_ci 0, 1141cb0ef41Sopenharmony_ci s, 1151cb0ef41Sopenharmony_ci -1, 1161cb0ef41Sopenharmony_ci NULL, 1171cb0ef41Sopenharmony_ci 0); 1181cb0ef41Sopenharmony_ci if (ws_len <= 0) { 1191cb0ef41Sopenharmony_ci return GetLastError(); 1201cb0ef41Sopenharmony_ci } 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci ws = (WCHAR*) uv__malloc(ws_len * sizeof(WCHAR)); 1231cb0ef41Sopenharmony_ci if (ws == NULL) { 1241cb0ef41Sopenharmony_ci return ERROR_OUTOFMEMORY; 1251cb0ef41Sopenharmony_ci } 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_ci r = MultiByteToWideChar(CP_UTF8, 1281cb0ef41Sopenharmony_ci 0, 1291cb0ef41Sopenharmony_ci s, 1301cb0ef41Sopenharmony_ci -1, 1311cb0ef41Sopenharmony_ci ws, 1321cb0ef41Sopenharmony_ci ws_len); 1331cb0ef41Sopenharmony_ci assert(r == ws_len); 1341cb0ef41Sopenharmony_ci 1351cb0ef41Sopenharmony_ci *ws_ptr = ws; 1361cb0ef41Sopenharmony_ci return 0; 1371cb0ef41Sopenharmony_ci} 1381cb0ef41Sopenharmony_ci 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_cistatic void uv__process_init(uv_loop_t* loop, uv_process_t* handle) { 1411cb0ef41Sopenharmony_ci uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS); 1421cb0ef41Sopenharmony_ci handle->exit_cb = NULL; 1431cb0ef41Sopenharmony_ci handle->pid = 0; 1441cb0ef41Sopenharmony_ci handle->exit_signal = 0; 1451cb0ef41Sopenharmony_ci handle->wait_handle = INVALID_HANDLE_VALUE; 1461cb0ef41Sopenharmony_ci handle->process_handle = INVALID_HANDLE_VALUE; 1471cb0ef41Sopenharmony_ci handle->child_stdio_buffer = NULL; 1481cb0ef41Sopenharmony_ci handle->exit_cb_pending = 0; 1491cb0ef41Sopenharmony_ci 1501cb0ef41Sopenharmony_ci UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT); 1511cb0ef41Sopenharmony_ci handle->exit_req.data = handle; 1521cb0ef41Sopenharmony_ci} 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci/* 1561cb0ef41Sopenharmony_ci * Path search functions 1571cb0ef41Sopenharmony_ci */ 1581cb0ef41Sopenharmony_ci 1591cb0ef41Sopenharmony_ci/* 1601cb0ef41Sopenharmony_ci * Helper function for search_path 1611cb0ef41Sopenharmony_ci */ 1621cb0ef41Sopenharmony_cistatic WCHAR* search_path_join_test(const WCHAR* dir, 1631cb0ef41Sopenharmony_ci size_t dir_len, 1641cb0ef41Sopenharmony_ci const WCHAR* name, 1651cb0ef41Sopenharmony_ci size_t name_len, 1661cb0ef41Sopenharmony_ci const WCHAR* ext, 1671cb0ef41Sopenharmony_ci size_t ext_len, 1681cb0ef41Sopenharmony_ci const WCHAR* cwd, 1691cb0ef41Sopenharmony_ci size_t cwd_len) { 1701cb0ef41Sopenharmony_ci WCHAR *result, *result_pos; 1711cb0ef41Sopenharmony_ci DWORD attrs; 1721cb0ef41Sopenharmony_ci if (dir_len > 2 && 1731cb0ef41Sopenharmony_ci ((dir[0] == L'\\' || dir[0] == L'/') && 1741cb0ef41Sopenharmony_ci (dir[1] == L'\\' || dir[1] == L'/'))) { 1751cb0ef41Sopenharmony_ci /* It's a UNC path so ignore cwd */ 1761cb0ef41Sopenharmony_ci cwd_len = 0; 1771cb0ef41Sopenharmony_ci } else if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) { 1781cb0ef41Sopenharmony_ci /* It's a full path without drive letter, use cwd's drive letter only */ 1791cb0ef41Sopenharmony_ci cwd_len = 2; 1801cb0ef41Sopenharmony_ci } else if (dir_len >= 2 && dir[1] == L':' && 1811cb0ef41Sopenharmony_ci (dir_len < 3 || (dir[2] != L'/' && dir[2] != L'\\'))) { 1821cb0ef41Sopenharmony_ci /* It's a relative path with drive letter (ext.g. D:../some/file) 1831cb0ef41Sopenharmony_ci * Replace drive letter in dir by full cwd if it points to the same drive, 1841cb0ef41Sopenharmony_ci * otherwise use the dir only. 1851cb0ef41Sopenharmony_ci */ 1861cb0ef41Sopenharmony_ci if (cwd_len < 2 || _wcsnicmp(cwd, dir, 2) != 0) { 1871cb0ef41Sopenharmony_ci cwd_len = 0; 1881cb0ef41Sopenharmony_ci } else { 1891cb0ef41Sopenharmony_ci dir += 2; 1901cb0ef41Sopenharmony_ci dir_len -= 2; 1911cb0ef41Sopenharmony_ci } 1921cb0ef41Sopenharmony_ci } else if (dir_len > 2 && dir[1] == L':') { 1931cb0ef41Sopenharmony_ci /* It's an absolute path with drive letter 1941cb0ef41Sopenharmony_ci * Don't use the cwd at all 1951cb0ef41Sopenharmony_ci */ 1961cb0ef41Sopenharmony_ci cwd_len = 0; 1971cb0ef41Sopenharmony_ci } 1981cb0ef41Sopenharmony_ci 1991cb0ef41Sopenharmony_ci /* Allocate buffer for output */ 2001cb0ef41Sopenharmony_ci result = result_pos = (WCHAR*)uv__malloc(sizeof(WCHAR) * 2011cb0ef41Sopenharmony_ci (cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1)); 2021cb0ef41Sopenharmony_ci 2031cb0ef41Sopenharmony_ci /* Copy cwd */ 2041cb0ef41Sopenharmony_ci wcsncpy(result_pos, cwd, cwd_len); 2051cb0ef41Sopenharmony_ci result_pos += cwd_len; 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_ci /* Add a path separator if cwd didn't end with one */ 2081cb0ef41Sopenharmony_ci if (cwd_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) { 2091cb0ef41Sopenharmony_ci result_pos[0] = L'\\'; 2101cb0ef41Sopenharmony_ci result_pos++; 2111cb0ef41Sopenharmony_ci } 2121cb0ef41Sopenharmony_ci 2131cb0ef41Sopenharmony_ci /* Copy dir */ 2141cb0ef41Sopenharmony_ci wcsncpy(result_pos, dir, dir_len); 2151cb0ef41Sopenharmony_ci result_pos += dir_len; 2161cb0ef41Sopenharmony_ci 2171cb0ef41Sopenharmony_ci /* Add a separator if the dir didn't end with one */ 2181cb0ef41Sopenharmony_ci if (dir_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) { 2191cb0ef41Sopenharmony_ci result_pos[0] = L'\\'; 2201cb0ef41Sopenharmony_ci result_pos++; 2211cb0ef41Sopenharmony_ci } 2221cb0ef41Sopenharmony_ci 2231cb0ef41Sopenharmony_ci /* Copy filename */ 2241cb0ef41Sopenharmony_ci wcsncpy(result_pos, name, name_len); 2251cb0ef41Sopenharmony_ci result_pos += name_len; 2261cb0ef41Sopenharmony_ci 2271cb0ef41Sopenharmony_ci if (ext_len) { 2281cb0ef41Sopenharmony_ci /* Add a dot if the filename didn't end with one */ 2291cb0ef41Sopenharmony_ci if (name_len && result_pos[-1] != '.') { 2301cb0ef41Sopenharmony_ci result_pos[0] = L'.'; 2311cb0ef41Sopenharmony_ci result_pos++; 2321cb0ef41Sopenharmony_ci } 2331cb0ef41Sopenharmony_ci 2341cb0ef41Sopenharmony_ci /* Copy extension */ 2351cb0ef41Sopenharmony_ci wcsncpy(result_pos, ext, ext_len); 2361cb0ef41Sopenharmony_ci result_pos += ext_len; 2371cb0ef41Sopenharmony_ci } 2381cb0ef41Sopenharmony_ci 2391cb0ef41Sopenharmony_ci /* Null terminator */ 2401cb0ef41Sopenharmony_ci result_pos[0] = L'\0'; 2411cb0ef41Sopenharmony_ci 2421cb0ef41Sopenharmony_ci attrs = GetFileAttributesW(result); 2431cb0ef41Sopenharmony_ci 2441cb0ef41Sopenharmony_ci if (attrs != INVALID_FILE_ATTRIBUTES && 2451cb0ef41Sopenharmony_ci !(attrs & FILE_ATTRIBUTE_DIRECTORY)) { 2461cb0ef41Sopenharmony_ci return result; 2471cb0ef41Sopenharmony_ci } 2481cb0ef41Sopenharmony_ci 2491cb0ef41Sopenharmony_ci uv__free(result); 2501cb0ef41Sopenharmony_ci return NULL; 2511cb0ef41Sopenharmony_ci} 2521cb0ef41Sopenharmony_ci 2531cb0ef41Sopenharmony_ci 2541cb0ef41Sopenharmony_ci/* 2551cb0ef41Sopenharmony_ci * Helper function for search_path 2561cb0ef41Sopenharmony_ci */ 2571cb0ef41Sopenharmony_cistatic WCHAR* path_search_walk_ext(const WCHAR *dir, 2581cb0ef41Sopenharmony_ci size_t dir_len, 2591cb0ef41Sopenharmony_ci const WCHAR *name, 2601cb0ef41Sopenharmony_ci size_t name_len, 2611cb0ef41Sopenharmony_ci WCHAR *cwd, 2621cb0ef41Sopenharmony_ci size_t cwd_len, 2631cb0ef41Sopenharmony_ci int name_has_ext) { 2641cb0ef41Sopenharmony_ci WCHAR* result; 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_ci /* If the name itself has a nonempty extension, try this extension first */ 2671cb0ef41Sopenharmony_ci if (name_has_ext) { 2681cb0ef41Sopenharmony_ci result = search_path_join_test(dir, dir_len, 2691cb0ef41Sopenharmony_ci name, name_len, 2701cb0ef41Sopenharmony_ci L"", 0, 2711cb0ef41Sopenharmony_ci cwd, cwd_len); 2721cb0ef41Sopenharmony_ci if (result != NULL) { 2731cb0ef41Sopenharmony_ci return result; 2741cb0ef41Sopenharmony_ci } 2751cb0ef41Sopenharmony_ci } 2761cb0ef41Sopenharmony_ci 2771cb0ef41Sopenharmony_ci /* Try .com extension */ 2781cb0ef41Sopenharmony_ci result = search_path_join_test(dir, dir_len, 2791cb0ef41Sopenharmony_ci name, name_len, 2801cb0ef41Sopenharmony_ci L"com", 3, 2811cb0ef41Sopenharmony_ci cwd, cwd_len); 2821cb0ef41Sopenharmony_ci if (result != NULL) { 2831cb0ef41Sopenharmony_ci return result; 2841cb0ef41Sopenharmony_ci } 2851cb0ef41Sopenharmony_ci 2861cb0ef41Sopenharmony_ci /* Try .exe extension */ 2871cb0ef41Sopenharmony_ci result = search_path_join_test(dir, dir_len, 2881cb0ef41Sopenharmony_ci name, name_len, 2891cb0ef41Sopenharmony_ci L"exe", 3, 2901cb0ef41Sopenharmony_ci cwd, cwd_len); 2911cb0ef41Sopenharmony_ci if (result != NULL) { 2921cb0ef41Sopenharmony_ci return result; 2931cb0ef41Sopenharmony_ci } 2941cb0ef41Sopenharmony_ci 2951cb0ef41Sopenharmony_ci return NULL; 2961cb0ef41Sopenharmony_ci} 2971cb0ef41Sopenharmony_ci 2981cb0ef41Sopenharmony_ci 2991cb0ef41Sopenharmony_ci/* 3001cb0ef41Sopenharmony_ci * search_path searches the system path for an executable filename - 3011cb0ef41Sopenharmony_ci * the windows API doesn't provide this as a standalone function nor as an 3021cb0ef41Sopenharmony_ci * option to CreateProcess. 3031cb0ef41Sopenharmony_ci * 3041cb0ef41Sopenharmony_ci * It tries to return an absolute filename. 3051cb0ef41Sopenharmony_ci * 3061cb0ef41Sopenharmony_ci * Furthermore, it tries to follow the semantics that cmd.exe, with this 3071cb0ef41Sopenharmony_ci * exception that PATHEXT environment variable isn't used. Since CreateProcess 3081cb0ef41Sopenharmony_ci * can start only .com and .exe files, only those extensions are tried. This 3091cb0ef41Sopenharmony_ci * behavior equals that of msvcrt's spawn functions. 3101cb0ef41Sopenharmony_ci * 3111cb0ef41Sopenharmony_ci * - Do not search the path if the filename already contains a path (either 3121cb0ef41Sopenharmony_ci * relative or absolute). 3131cb0ef41Sopenharmony_ci * 3141cb0ef41Sopenharmony_ci * - If there's really only a filename, check the current directory for file, 3151cb0ef41Sopenharmony_ci * then search all path directories. 3161cb0ef41Sopenharmony_ci * 3171cb0ef41Sopenharmony_ci * - If filename specified has *any* extension, search for the file with the 3181cb0ef41Sopenharmony_ci * specified extension first. 3191cb0ef41Sopenharmony_ci * 3201cb0ef41Sopenharmony_ci * - If the literal filename is not found in a directory, try *appending* 3211cb0ef41Sopenharmony_ci * (not replacing) .com first and then .exe. 3221cb0ef41Sopenharmony_ci * 3231cb0ef41Sopenharmony_ci * - The path variable may contain relative paths; relative paths are relative 3241cb0ef41Sopenharmony_ci * to the cwd. 3251cb0ef41Sopenharmony_ci * 3261cb0ef41Sopenharmony_ci * - Directories in path may or may not end with a trailing backslash. 3271cb0ef41Sopenharmony_ci * 3281cb0ef41Sopenharmony_ci * - CMD does not trim leading/trailing whitespace from path/pathex entries 3291cb0ef41Sopenharmony_ci * nor from the environment variables as a whole. 3301cb0ef41Sopenharmony_ci * 3311cb0ef41Sopenharmony_ci * - When cmd.exe cannot read a directory, it will just skip it and go on 3321cb0ef41Sopenharmony_ci * searching. However, unlike posix-y systems, it will happily try to run a 3331cb0ef41Sopenharmony_ci * file that is not readable/executable; if the spawn fails it will not 3341cb0ef41Sopenharmony_ci * continue searching. 3351cb0ef41Sopenharmony_ci * 3361cb0ef41Sopenharmony_ci * UNC path support: we are dealing with UNC paths in both the path and the 3371cb0ef41Sopenharmony_ci * filename. This is a deviation from what cmd.exe does (it does not let you 3381cb0ef41Sopenharmony_ci * start a program by specifying an UNC path on the command line) but this is 3391cb0ef41Sopenharmony_ci * really a pointless restriction. 3401cb0ef41Sopenharmony_ci * 3411cb0ef41Sopenharmony_ci */ 3421cb0ef41Sopenharmony_cistatic WCHAR* search_path(const WCHAR *file, 3431cb0ef41Sopenharmony_ci WCHAR *cwd, 3441cb0ef41Sopenharmony_ci const WCHAR *path) { 3451cb0ef41Sopenharmony_ci int file_has_dir; 3461cb0ef41Sopenharmony_ci WCHAR* result = NULL; 3471cb0ef41Sopenharmony_ci WCHAR *file_name_start; 3481cb0ef41Sopenharmony_ci WCHAR *dot; 3491cb0ef41Sopenharmony_ci const WCHAR *dir_start, *dir_end, *dir_path; 3501cb0ef41Sopenharmony_ci size_t dir_len; 3511cb0ef41Sopenharmony_ci int name_has_ext; 3521cb0ef41Sopenharmony_ci 3531cb0ef41Sopenharmony_ci size_t file_len = wcslen(file); 3541cb0ef41Sopenharmony_ci size_t cwd_len = wcslen(cwd); 3551cb0ef41Sopenharmony_ci 3561cb0ef41Sopenharmony_ci /* If the caller supplies an empty filename, 3571cb0ef41Sopenharmony_ci * we're not gonna return c:\windows\.exe -- GFY! 3581cb0ef41Sopenharmony_ci */ 3591cb0ef41Sopenharmony_ci if (file_len == 0 3601cb0ef41Sopenharmony_ci || (file_len == 1 && file[0] == L'.')) { 3611cb0ef41Sopenharmony_ci return NULL; 3621cb0ef41Sopenharmony_ci } 3631cb0ef41Sopenharmony_ci 3641cb0ef41Sopenharmony_ci /* Find the start of the filename so we can split the directory from the 3651cb0ef41Sopenharmony_ci * name. */ 3661cb0ef41Sopenharmony_ci for (file_name_start = (WCHAR*)file + file_len; 3671cb0ef41Sopenharmony_ci file_name_start > file 3681cb0ef41Sopenharmony_ci && file_name_start[-1] != L'\\' 3691cb0ef41Sopenharmony_ci && file_name_start[-1] != L'/' 3701cb0ef41Sopenharmony_ci && file_name_start[-1] != L':'; 3711cb0ef41Sopenharmony_ci file_name_start--); 3721cb0ef41Sopenharmony_ci 3731cb0ef41Sopenharmony_ci file_has_dir = file_name_start != file; 3741cb0ef41Sopenharmony_ci 3751cb0ef41Sopenharmony_ci /* Check if the filename includes an extension */ 3761cb0ef41Sopenharmony_ci dot = wcschr(file_name_start, L'.'); 3771cb0ef41Sopenharmony_ci name_has_ext = (dot != NULL && dot[1] != L'\0'); 3781cb0ef41Sopenharmony_ci 3791cb0ef41Sopenharmony_ci if (file_has_dir) { 3801cb0ef41Sopenharmony_ci /* The file has a path inside, don't use path */ 3811cb0ef41Sopenharmony_ci result = path_search_walk_ext( 3821cb0ef41Sopenharmony_ci file, file_name_start - file, 3831cb0ef41Sopenharmony_ci file_name_start, file_len - (file_name_start - file), 3841cb0ef41Sopenharmony_ci cwd, cwd_len, 3851cb0ef41Sopenharmony_ci name_has_ext); 3861cb0ef41Sopenharmony_ci 3871cb0ef41Sopenharmony_ci } else { 3881cb0ef41Sopenharmony_ci dir_end = path; 3891cb0ef41Sopenharmony_ci 3901cb0ef41Sopenharmony_ci /* The file is really only a name; look in cwd first, then scan path */ 3911cb0ef41Sopenharmony_ci result = path_search_walk_ext(L"", 0, 3921cb0ef41Sopenharmony_ci file, file_len, 3931cb0ef41Sopenharmony_ci cwd, cwd_len, 3941cb0ef41Sopenharmony_ci name_has_ext); 3951cb0ef41Sopenharmony_ci 3961cb0ef41Sopenharmony_ci while (result == NULL) { 3971cb0ef41Sopenharmony_ci if (*dir_end == L'\0') { 3981cb0ef41Sopenharmony_ci break; 3991cb0ef41Sopenharmony_ci } 4001cb0ef41Sopenharmony_ci 4011cb0ef41Sopenharmony_ci /* Skip the separator that dir_end now points to */ 4021cb0ef41Sopenharmony_ci if (dir_end != path || *path == L';') { 4031cb0ef41Sopenharmony_ci dir_end++; 4041cb0ef41Sopenharmony_ci } 4051cb0ef41Sopenharmony_ci 4061cb0ef41Sopenharmony_ci /* Next slice starts just after where the previous one ended */ 4071cb0ef41Sopenharmony_ci dir_start = dir_end; 4081cb0ef41Sopenharmony_ci 4091cb0ef41Sopenharmony_ci /* If path is quoted, find quote end */ 4101cb0ef41Sopenharmony_ci if (*dir_start == L'"' || *dir_start == L'\'') { 4111cb0ef41Sopenharmony_ci dir_end = wcschr(dir_start + 1, *dir_start); 4121cb0ef41Sopenharmony_ci if (dir_end == NULL) { 4131cb0ef41Sopenharmony_ci dir_end = wcschr(dir_start, L'\0'); 4141cb0ef41Sopenharmony_ci } 4151cb0ef41Sopenharmony_ci } 4161cb0ef41Sopenharmony_ci /* Slice until the next ; or \0 is found */ 4171cb0ef41Sopenharmony_ci dir_end = wcschr(dir_end, L';'); 4181cb0ef41Sopenharmony_ci if (dir_end == NULL) { 4191cb0ef41Sopenharmony_ci dir_end = wcschr(dir_start, L'\0'); 4201cb0ef41Sopenharmony_ci } 4211cb0ef41Sopenharmony_ci 4221cb0ef41Sopenharmony_ci /* If the slice is zero-length, don't bother */ 4231cb0ef41Sopenharmony_ci if (dir_end - dir_start == 0) { 4241cb0ef41Sopenharmony_ci continue; 4251cb0ef41Sopenharmony_ci } 4261cb0ef41Sopenharmony_ci 4271cb0ef41Sopenharmony_ci dir_path = dir_start; 4281cb0ef41Sopenharmony_ci dir_len = dir_end - dir_start; 4291cb0ef41Sopenharmony_ci 4301cb0ef41Sopenharmony_ci /* Adjust if the path is quoted. */ 4311cb0ef41Sopenharmony_ci if (dir_path[0] == '"' || dir_path[0] == '\'') { 4321cb0ef41Sopenharmony_ci ++dir_path; 4331cb0ef41Sopenharmony_ci --dir_len; 4341cb0ef41Sopenharmony_ci } 4351cb0ef41Sopenharmony_ci 4361cb0ef41Sopenharmony_ci if (dir_path[dir_len - 1] == '"' || dir_path[dir_len - 1] == '\'') { 4371cb0ef41Sopenharmony_ci --dir_len; 4381cb0ef41Sopenharmony_ci } 4391cb0ef41Sopenharmony_ci 4401cb0ef41Sopenharmony_ci result = path_search_walk_ext(dir_path, dir_len, 4411cb0ef41Sopenharmony_ci file, file_len, 4421cb0ef41Sopenharmony_ci cwd, cwd_len, 4431cb0ef41Sopenharmony_ci name_has_ext); 4441cb0ef41Sopenharmony_ci } 4451cb0ef41Sopenharmony_ci } 4461cb0ef41Sopenharmony_ci 4471cb0ef41Sopenharmony_ci return result; 4481cb0ef41Sopenharmony_ci} 4491cb0ef41Sopenharmony_ci 4501cb0ef41Sopenharmony_ci 4511cb0ef41Sopenharmony_ci/* 4521cb0ef41Sopenharmony_ci * Quotes command line arguments 4531cb0ef41Sopenharmony_ci * Returns a pointer to the end (next char to be written) of the buffer 4541cb0ef41Sopenharmony_ci */ 4551cb0ef41Sopenharmony_ciWCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) { 4561cb0ef41Sopenharmony_ci size_t len = wcslen(source); 4571cb0ef41Sopenharmony_ci size_t i; 4581cb0ef41Sopenharmony_ci int quote_hit; 4591cb0ef41Sopenharmony_ci WCHAR* start; 4601cb0ef41Sopenharmony_ci 4611cb0ef41Sopenharmony_ci if (len == 0) { 4621cb0ef41Sopenharmony_ci /* Need double quotation for empty argument */ 4631cb0ef41Sopenharmony_ci *(target++) = L'"'; 4641cb0ef41Sopenharmony_ci *(target++) = L'"'; 4651cb0ef41Sopenharmony_ci return target; 4661cb0ef41Sopenharmony_ci } 4671cb0ef41Sopenharmony_ci 4681cb0ef41Sopenharmony_ci if (NULL == wcspbrk(source, L" \t\"")) { 4691cb0ef41Sopenharmony_ci /* No quotation needed */ 4701cb0ef41Sopenharmony_ci wcsncpy(target, source, len); 4711cb0ef41Sopenharmony_ci target += len; 4721cb0ef41Sopenharmony_ci return target; 4731cb0ef41Sopenharmony_ci } 4741cb0ef41Sopenharmony_ci 4751cb0ef41Sopenharmony_ci if (NULL == wcspbrk(source, L"\"\\")) { 4761cb0ef41Sopenharmony_ci /* 4771cb0ef41Sopenharmony_ci * No embedded double quotes or backlashes, so I can just wrap 4781cb0ef41Sopenharmony_ci * quote marks around the whole thing. 4791cb0ef41Sopenharmony_ci */ 4801cb0ef41Sopenharmony_ci *(target++) = L'"'; 4811cb0ef41Sopenharmony_ci wcsncpy(target, source, len); 4821cb0ef41Sopenharmony_ci target += len; 4831cb0ef41Sopenharmony_ci *(target++) = L'"'; 4841cb0ef41Sopenharmony_ci return target; 4851cb0ef41Sopenharmony_ci } 4861cb0ef41Sopenharmony_ci 4871cb0ef41Sopenharmony_ci /* 4881cb0ef41Sopenharmony_ci * Expected input/output: 4891cb0ef41Sopenharmony_ci * input : hello"world 4901cb0ef41Sopenharmony_ci * output: "hello\"world" 4911cb0ef41Sopenharmony_ci * input : hello""world 4921cb0ef41Sopenharmony_ci * output: "hello\"\"world" 4931cb0ef41Sopenharmony_ci * input : hello\world 4941cb0ef41Sopenharmony_ci * output: hello\world 4951cb0ef41Sopenharmony_ci * input : hello\\world 4961cb0ef41Sopenharmony_ci * output: hello\\world 4971cb0ef41Sopenharmony_ci * input : hello\"world 4981cb0ef41Sopenharmony_ci * output: "hello\\\"world" 4991cb0ef41Sopenharmony_ci * input : hello\\"world 5001cb0ef41Sopenharmony_ci * output: "hello\\\\\"world" 5011cb0ef41Sopenharmony_ci * input : hello world\ 5021cb0ef41Sopenharmony_ci * output: "hello world\\" 5031cb0ef41Sopenharmony_ci */ 5041cb0ef41Sopenharmony_ci 5051cb0ef41Sopenharmony_ci *(target++) = L'"'; 5061cb0ef41Sopenharmony_ci start = target; 5071cb0ef41Sopenharmony_ci quote_hit = 1; 5081cb0ef41Sopenharmony_ci 5091cb0ef41Sopenharmony_ci for (i = len; i > 0; --i) { 5101cb0ef41Sopenharmony_ci *(target++) = source[i - 1]; 5111cb0ef41Sopenharmony_ci 5121cb0ef41Sopenharmony_ci if (quote_hit && source[i - 1] == L'\\') { 5131cb0ef41Sopenharmony_ci *(target++) = L'\\'; 5141cb0ef41Sopenharmony_ci } else if(source[i - 1] == L'"') { 5151cb0ef41Sopenharmony_ci quote_hit = 1; 5161cb0ef41Sopenharmony_ci *(target++) = L'\\'; 5171cb0ef41Sopenharmony_ci } else { 5181cb0ef41Sopenharmony_ci quote_hit = 0; 5191cb0ef41Sopenharmony_ci } 5201cb0ef41Sopenharmony_ci } 5211cb0ef41Sopenharmony_ci target[0] = L'\0'; 5221cb0ef41Sopenharmony_ci wcsrev(start); 5231cb0ef41Sopenharmony_ci *(target++) = L'"'; 5241cb0ef41Sopenharmony_ci return target; 5251cb0ef41Sopenharmony_ci} 5261cb0ef41Sopenharmony_ci 5271cb0ef41Sopenharmony_ci 5281cb0ef41Sopenharmony_ciint make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) { 5291cb0ef41Sopenharmony_ci char** arg; 5301cb0ef41Sopenharmony_ci WCHAR* dst = NULL; 5311cb0ef41Sopenharmony_ci WCHAR* temp_buffer = NULL; 5321cb0ef41Sopenharmony_ci size_t dst_len = 0; 5331cb0ef41Sopenharmony_ci size_t temp_buffer_len = 0; 5341cb0ef41Sopenharmony_ci WCHAR* pos; 5351cb0ef41Sopenharmony_ci int arg_count = 0; 5361cb0ef41Sopenharmony_ci int err = 0; 5371cb0ef41Sopenharmony_ci 5381cb0ef41Sopenharmony_ci /* Count the required size. */ 5391cb0ef41Sopenharmony_ci for (arg = args; *arg; arg++) { 5401cb0ef41Sopenharmony_ci DWORD arg_len; 5411cb0ef41Sopenharmony_ci 5421cb0ef41Sopenharmony_ci arg_len = MultiByteToWideChar(CP_UTF8, 5431cb0ef41Sopenharmony_ci 0, 5441cb0ef41Sopenharmony_ci *arg, 5451cb0ef41Sopenharmony_ci -1, 5461cb0ef41Sopenharmony_ci NULL, 5471cb0ef41Sopenharmony_ci 0); 5481cb0ef41Sopenharmony_ci if (arg_len == 0) { 5491cb0ef41Sopenharmony_ci return GetLastError(); 5501cb0ef41Sopenharmony_ci } 5511cb0ef41Sopenharmony_ci 5521cb0ef41Sopenharmony_ci dst_len += arg_len; 5531cb0ef41Sopenharmony_ci 5541cb0ef41Sopenharmony_ci if (arg_len > temp_buffer_len) 5551cb0ef41Sopenharmony_ci temp_buffer_len = arg_len; 5561cb0ef41Sopenharmony_ci 5571cb0ef41Sopenharmony_ci arg_count++; 5581cb0ef41Sopenharmony_ci } 5591cb0ef41Sopenharmony_ci 5601cb0ef41Sopenharmony_ci /* Adjust for potential quotes. Also assume the worst-case scenario that 5611cb0ef41Sopenharmony_ci * every character needs escaping, so we need twice as much space. */ 5621cb0ef41Sopenharmony_ci dst_len = dst_len * 2 + arg_count * 2; 5631cb0ef41Sopenharmony_ci 5641cb0ef41Sopenharmony_ci /* Allocate buffer for the final command line. */ 5651cb0ef41Sopenharmony_ci dst = (WCHAR*) uv__malloc(dst_len * sizeof(WCHAR)); 5661cb0ef41Sopenharmony_ci if (dst == NULL) { 5671cb0ef41Sopenharmony_ci err = ERROR_OUTOFMEMORY; 5681cb0ef41Sopenharmony_ci goto error; 5691cb0ef41Sopenharmony_ci } 5701cb0ef41Sopenharmony_ci 5711cb0ef41Sopenharmony_ci /* Allocate temporary working buffer. */ 5721cb0ef41Sopenharmony_ci temp_buffer = (WCHAR*) uv__malloc(temp_buffer_len * sizeof(WCHAR)); 5731cb0ef41Sopenharmony_ci if (temp_buffer == NULL) { 5741cb0ef41Sopenharmony_ci err = ERROR_OUTOFMEMORY; 5751cb0ef41Sopenharmony_ci goto error; 5761cb0ef41Sopenharmony_ci } 5771cb0ef41Sopenharmony_ci 5781cb0ef41Sopenharmony_ci pos = dst; 5791cb0ef41Sopenharmony_ci for (arg = args; *arg; arg++) { 5801cb0ef41Sopenharmony_ci DWORD arg_len; 5811cb0ef41Sopenharmony_ci 5821cb0ef41Sopenharmony_ci /* Convert argument to wide char. */ 5831cb0ef41Sopenharmony_ci arg_len = MultiByteToWideChar(CP_UTF8, 5841cb0ef41Sopenharmony_ci 0, 5851cb0ef41Sopenharmony_ci *arg, 5861cb0ef41Sopenharmony_ci -1, 5871cb0ef41Sopenharmony_ci temp_buffer, 5881cb0ef41Sopenharmony_ci (int) (dst + dst_len - pos)); 5891cb0ef41Sopenharmony_ci if (arg_len == 0) { 5901cb0ef41Sopenharmony_ci err = GetLastError(); 5911cb0ef41Sopenharmony_ci goto error; 5921cb0ef41Sopenharmony_ci } 5931cb0ef41Sopenharmony_ci 5941cb0ef41Sopenharmony_ci if (verbatim_arguments) { 5951cb0ef41Sopenharmony_ci /* Copy verbatim. */ 5961cb0ef41Sopenharmony_ci wcscpy(pos, temp_buffer); 5971cb0ef41Sopenharmony_ci pos += arg_len - 1; 5981cb0ef41Sopenharmony_ci } else { 5991cb0ef41Sopenharmony_ci /* Quote/escape, if needed. */ 6001cb0ef41Sopenharmony_ci pos = quote_cmd_arg(temp_buffer, pos); 6011cb0ef41Sopenharmony_ci } 6021cb0ef41Sopenharmony_ci 6031cb0ef41Sopenharmony_ci *pos++ = *(arg + 1) ? L' ' : L'\0'; 6041cb0ef41Sopenharmony_ci } 6051cb0ef41Sopenharmony_ci 6061cb0ef41Sopenharmony_ci uv__free(temp_buffer); 6071cb0ef41Sopenharmony_ci 6081cb0ef41Sopenharmony_ci *dst_ptr = dst; 6091cb0ef41Sopenharmony_ci return 0; 6101cb0ef41Sopenharmony_ci 6111cb0ef41Sopenharmony_cierror: 6121cb0ef41Sopenharmony_ci uv__free(dst); 6131cb0ef41Sopenharmony_ci uv__free(temp_buffer); 6141cb0ef41Sopenharmony_ci return err; 6151cb0ef41Sopenharmony_ci} 6161cb0ef41Sopenharmony_ci 6171cb0ef41Sopenharmony_ci 6181cb0ef41Sopenharmony_ciint env_strncmp(const wchar_t* a, int na, const wchar_t* b) { 6191cb0ef41Sopenharmony_ci wchar_t* a_eq; 6201cb0ef41Sopenharmony_ci wchar_t* b_eq; 6211cb0ef41Sopenharmony_ci wchar_t* A; 6221cb0ef41Sopenharmony_ci wchar_t* B; 6231cb0ef41Sopenharmony_ci int nb; 6241cb0ef41Sopenharmony_ci int r; 6251cb0ef41Sopenharmony_ci 6261cb0ef41Sopenharmony_ci if (na < 0) { 6271cb0ef41Sopenharmony_ci a_eq = wcschr(a, L'='); 6281cb0ef41Sopenharmony_ci assert(a_eq); 6291cb0ef41Sopenharmony_ci na = (int)(long)(a_eq - a); 6301cb0ef41Sopenharmony_ci } else { 6311cb0ef41Sopenharmony_ci na--; 6321cb0ef41Sopenharmony_ci } 6331cb0ef41Sopenharmony_ci b_eq = wcschr(b, L'='); 6341cb0ef41Sopenharmony_ci assert(b_eq); 6351cb0ef41Sopenharmony_ci nb = b_eq - b; 6361cb0ef41Sopenharmony_ci 6371cb0ef41Sopenharmony_ci A = alloca((na+1) * sizeof(wchar_t)); 6381cb0ef41Sopenharmony_ci B = alloca((nb+1) * sizeof(wchar_t)); 6391cb0ef41Sopenharmony_ci 6401cb0ef41Sopenharmony_ci r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na); 6411cb0ef41Sopenharmony_ci assert(r==na); 6421cb0ef41Sopenharmony_ci A[na] = L'\0'; 6431cb0ef41Sopenharmony_ci r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb); 6441cb0ef41Sopenharmony_ci assert(r==nb); 6451cb0ef41Sopenharmony_ci B[nb] = L'\0'; 6461cb0ef41Sopenharmony_ci 6471cb0ef41Sopenharmony_ci for (;;) { 6481cb0ef41Sopenharmony_ci wchar_t AA = *A++; 6491cb0ef41Sopenharmony_ci wchar_t BB = *B++; 6501cb0ef41Sopenharmony_ci if (AA < BB) { 6511cb0ef41Sopenharmony_ci return -1; 6521cb0ef41Sopenharmony_ci } else if (AA > BB) { 6531cb0ef41Sopenharmony_ci return 1; 6541cb0ef41Sopenharmony_ci } else if (!AA && !BB) { 6551cb0ef41Sopenharmony_ci return 0; 6561cb0ef41Sopenharmony_ci } 6571cb0ef41Sopenharmony_ci } 6581cb0ef41Sopenharmony_ci} 6591cb0ef41Sopenharmony_ci 6601cb0ef41Sopenharmony_ci 6611cb0ef41Sopenharmony_cistatic int qsort_wcscmp(const void *a, const void *b) { 6621cb0ef41Sopenharmony_ci wchar_t* astr = *(wchar_t* const*)a; 6631cb0ef41Sopenharmony_ci wchar_t* bstr = *(wchar_t* const*)b; 6641cb0ef41Sopenharmony_ci return env_strncmp(astr, -1, bstr); 6651cb0ef41Sopenharmony_ci} 6661cb0ef41Sopenharmony_ci 6671cb0ef41Sopenharmony_ci 6681cb0ef41Sopenharmony_ci/* 6691cb0ef41Sopenharmony_ci * The way windows takes environment variables is different than what C does; 6701cb0ef41Sopenharmony_ci * Windows wants a contiguous block of null-terminated strings, terminated 6711cb0ef41Sopenharmony_ci * with an additional null. 6721cb0ef41Sopenharmony_ci * 6731cb0ef41Sopenharmony_ci * Windows has a few "essential" environment variables. winsock will fail 6741cb0ef41Sopenharmony_ci * to initialize if SYSTEMROOT is not defined; some APIs make reference to 6751cb0ef41Sopenharmony_ci * TEMP. SYSTEMDRIVE is probably also important. We therefore ensure that 6761cb0ef41Sopenharmony_ci * these get defined if the input environment block does not contain any 6771cb0ef41Sopenharmony_ci * values for them. 6781cb0ef41Sopenharmony_ci * 6791cb0ef41Sopenharmony_ci * Also add variables known to Cygwin to be required for correct 6801cb0ef41Sopenharmony_ci * subprocess operation in many cases: 6811cb0ef41Sopenharmony_ci * https://github.com/Alexpux/Cygwin/blob/b266b04fbbd3a595f02ea149e4306d3ab9b1fe3d/winsup/cygwin/environ.cc#L955 6821cb0ef41Sopenharmony_ci * 6831cb0ef41Sopenharmony_ci */ 6841cb0ef41Sopenharmony_ciint make_program_env(char* env_block[], WCHAR** dst_ptr) { 6851cb0ef41Sopenharmony_ci WCHAR* dst; 6861cb0ef41Sopenharmony_ci WCHAR* ptr; 6871cb0ef41Sopenharmony_ci char** env; 6881cb0ef41Sopenharmony_ci size_t env_len = 0; 6891cb0ef41Sopenharmony_ci int len; 6901cb0ef41Sopenharmony_ci size_t i; 6911cb0ef41Sopenharmony_ci DWORD var_size; 6921cb0ef41Sopenharmony_ci size_t env_block_count = 1; /* 1 for null-terminator */ 6931cb0ef41Sopenharmony_ci WCHAR* dst_copy; 6941cb0ef41Sopenharmony_ci WCHAR** ptr_copy; 6951cb0ef41Sopenharmony_ci WCHAR** env_copy; 6961cb0ef41Sopenharmony_ci DWORD required_vars_value_len[ARRAY_SIZE(required_vars)]; 6971cb0ef41Sopenharmony_ci 6981cb0ef41Sopenharmony_ci /* first pass: determine size in UTF-16 */ 6991cb0ef41Sopenharmony_ci for (env = env_block; *env; env++) { 7001cb0ef41Sopenharmony_ci int len; 7011cb0ef41Sopenharmony_ci if (strchr(*env, '=')) { 7021cb0ef41Sopenharmony_ci len = MultiByteToWideChar(CP_UTF8, 7031cb0ef41Sopenharmony_ci 0, 7041cb0ef41Sopenharmony_ci *env, 7051cb0ef41Sopenharmony_ci -1, 7061cb0ef41Sopenharmony_ci NULL, 7071cb0ef41Sopenharmony_ci 0); 7081cb0ef41Sopenharmony_ci if (len <= 0) { 7091cb0ef41Sopenharmony_ci return GetLastError(); 7101cb0ef41Sopenharmony_ci } 7111cb0ef41Sopenharmony_ci env_len += len; 7121cb0ef41Sopenharmony_ci env_block_count++; 7131cb0ef41Sopenharmony_ci } 7141cb0ef41Sopenharmony_ci } 7151cb0ef41Sopenharmony_ci 7161cb0ef41Sopenharmony_ci /* second pass: copy to UTF-16 environment block */ 7171cb0ef41Sopenharmony_ci dst_copy = (WCHAR*)uv__malloc(env_len * sizeof(WCHAR)); 7181cb0ef41Sopenharmony_ci if (dst_copy == NULL && env_len > 0) { 7191cb0ef41Sopenharmony_ci return ERROR_OUTOFMEMORY; 7201cb0ef41Sopenharmony_ci } 7211cb0ef41Sopenharmony_ci env_copy = alloca(env_block_count * sizeof(WCHAR*)); 7221cb0ef41Sopenharmony_ci 7231cb0ef41Sopenharmony_ci ptr = dst_copy; 7241cb0ef41Sopenharmony_ci ptr_copy = env_copy; 7251cb0ef41Sopenharmony_ci for (env = env_block; *env; env++) { 7261cb0ef41Sopenharmony_ci if (strchr(*env, '=')) { 7271cb0ef41Sopenharmony_ci len = MultiByteToWideChar(CP_UTF8, 7281cb0ef41Sopenharmony_ci 0, 7291cb0ef41Sopenharmony_ci *env, 7301cb0ef41Sopenharmony_ci -1, 7311cb0ef41Sopenharmony_ci ptr, 7321cb0ef41Sopenharmony_ci (int) (env_len - (ptr - dst_copy))); 7331cb0ef41Sopenharmony_ci if (len <= 0) { 7341cb0ef41Sopenharmony_ci DWORD err = GetLastError(); 7351cb0ef41Sopenharmony_ci uv__free(dst_copy); 7361cb0ef41Sopenharmony_ci return err; 7371cb0ef41Sopenharmony_ci } 7381cb0ef41Sopenharmony_ci *ptr_copy++ = ptr; 7391cb0ef41Sopenharmony_ci ptr += len; 7401cb0ef41Sopenharmony_ci } 7411cb0ef41Sopenharmony_ci } 7421cb0ef41Sopenharmony_ci *ptr_copy = NULL; 7431cb0ef41Sopenharmony_ci assert(env_len == 0 || env_len == (size_t) (ptr - dst_copy)); 7441cb0ef41Sopenharmony_ci 7451cb0ef41Sopenharmony_ci /* sort our (UTF-16) copy */ 7461cb0ef41Sopenharmony_ci qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp); 7471cb0ef41Sopenharmony_ci 7481cb0ef41Sopenharmony_ci /* third pass: check for required variables */ 7491cb0ef41Sopenharmony_ci for (ptr_copy = env_copy, i = 0; i < ARRAY_SIZE(required_vars); ) { 7501cb0ef41Sopenharmony_ci int cmp; 7511cb0ef41Sopenharmony_ci if (!*ptr_copy) { 7521cb0ef41Sopenharmony_ci cmp = -1; 7531cb0ef41Sopenharmony_ci } else { 7541cb0ef41Sopenharmony_ci cmp = env_strncmp(required_vars[i].wide_eq, 7551cb0ef41Sopenharmony_ci required_vars[i].len, 7561cb0ef41Sopenharmony_ci *ptr_copy); 7571cb0ef41Sopenharmony_ci } 7581cb0ef41Sopenharmony_ci if (cmp < 0) { 7591cb0ef41Sopenharmony_ci /* missing required var */ 7601cb0ef41Sopenharmony_ci var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0); 7611cb0ef41Sopenharmony_ci required_vars_value_len[i] = var_size; 7621cb0ef41Sopenharmony_ci if (var_size != 0) { 7631cb0ef41Sopenharmony_ci env_len += required_vars[i].len; 7641cb0ef41Sopenharmony_ci env_len += var_size; 7651cb0ef41Sopenharmony_ci } 7661cb0ef41Sopenharmony_ci i++; 7671cb0ef41Sopenharmony_ci } else { 7681cb0ef41Sopenharmony_ci ptr_copy++; 7691cb0ef41Sopenharmony_ci if (cmp == 0) 7701cb0ef41Sopenharmony_ci i++; 7711cb0ef41Sopenharmony_ci } 7721cb0ef41Sopenharmony_ci } 7731cb0ef41Sopenharmony_ci 7741cb0ef41Sopenharmony_ci /* final pass: copy, in sort order, and inserting required variables */ 7751cb0ef41Sopenharmony_ci dst = uv__malloc((1+env_len) * sizeof(WCHAR)); 7761cb0ef41Sopenharmony_ci if (!dst) { 7771cb0ef41Sopenharmony_ci uv__free(dst_copy); 7781cb0ef41Sopenharmony_ci return ERROR_OUTOFMEMORY; 7791cb0ef41Sopenharmony_ci } 7801cb0ef41Sopenharmony_ci 7811cb0ef41Sopenharmony_ci for (ptr = dst, ptr_copy = env_copy, i = 0; 7821cb0ef41Sopenharmony_ci *ptr_copy || i < ARRAY_SIZE(required_vars); 7831cb0ef41Sopenharmony_ci ptr += len) { 7841cb0ef41Sopenharmony_ci int cmp; 7851cb0ef41Sopenharmony_ci if (i >= ARRAY_SIZE(required_vars)) { 7861cb0ef41Sopenharmony_ci cmp = 1; 7871cb0ef41Sopenharmony_ci } else if (!*ptr_copy) { 7881cb0ef41Sopenharmony_ci cmp = -1; 7891cb0ef41Sopenharmony_ci } else { 7901cb0ef41Sopenharmony_ci cmp = env_strncmp(required_vars[i].wide_eq, 7911cb0ef41Sopenharmony_ci required_vars[i].len, 7921cb0ef41Sopenharmony_ci *ptr_copy); 7931cb0ef41Sopenharmony_ci } 7941cb0ef41Sopenharmony_ci if (cmp < 0) { 7951cb0ef41Sopenharmony_ci /* missing required var */ 7961cb0ef41Sopenharmony_ci len = required_vars_value_len[i]; 7971cb0ef41Sopenharmony_ci if (len) { 7981cb0ef41Sopenharmony_ci wcscpy(ptr, required_vars[i].wide_eq); 7991cb0ef41Sopenharmony_ci ptr += required_vars[i].len; 8001cb0ef41Sopenharmony_ci var_size = GetEnvironmentVariableW(required_vars[i].wide, 8011cb0ef41Sopenharmony_ci ptr, 8021cb0ef41Sopenharmony_ci (int) (env_len - (ptr - dst))); 8031cb0ef41Sopenharmony_ci if (var_size != (DWORD) (len - 1)) { /* TODO: handle race condition? */ 8041cb0ef41Sopenharmony_ci uv_fatal_error(GetLastError(), "GetEnvironmentVariableW"); 8051cb0ef41Sopenharmony_ci } 8061cb0ef41Sopenharmony_ci } 8071cb0ef41Sopenharmony_ci i++; 8081cb0ef41Sopenharmony_ci } else { 8091cb0ef41Sopenharmony_ci /* copy var from env_block */ 8101cb0ef41Sopenharmony_ci len = wcslen(*ptr_copy) + 1; 8111cb0ef41Sopenharmony_ci wmemcpy(ptr, *ptr_copy, len); 8121cb0ef41Sopenharmony_ci ptr_copy++; 8131cb0ef41Sopenharmony_ci if (cmp == 0) 8141cb0ef41Sopenharmony_ci i++; 8151cb0ef41Sopenharmony_ci } 8161cb0ef41Sopenharmony_ci } 8171cb0ef41Sopenharmony_ci 8181cb0ef41Sopenharmony_ci /* Terminate with an extra NULL. */ 8191cb0ef41Sopenharmony_ci assert(env_len == (size_t) (ptr - dst)); 8201cb0ef41Sopenharmony_ci *ptr = L'\0'; 8211cb0ef41Sopenharmony_ci 8221cb0ef41Sopenharmony_ci uv__free(dst_copy); 8231cb0ef41Sopenharmony_ci *dst_ptr = dst; 8241cb0ef41Sopenharmony_ci return 0; 8251cb0ef41Sopenharmony_ci} 8261cb0ef41Sopenharmony_ci 8271cb0ef41Sopenharmony_ci/* 8281cb0ef41Sopenharmony_ci * Attempt to find the value of the PATH environment variable in the child's 8291cb0ef41Sopenharmony_ci * preprocessed environment. 8301cb0ef41Sopenharmony_ci * 8311cb0ef41Sopenharmony_ci * If found, a pointer into `env` is returned. If not found, NULL is returned. 8321cb0ef41Sopenharmony_ci */ 8331cb0ef41Sopenharmony_cistatic WCHAR* find_path(WCHAR *env) { 8341cb0ef41Sopenharmony_ci for (; env != NULL && *env != 0; env += wcslen(env) + 1) { 8351cb0ef41Sopenharmony_ci if ((env[0] == L'P' || env[0] == L'p') && 8361cb0ef41Sopenharmony_ci (env[1] == L'A' || env[1] == L'a') && 8371cb0ef41Sopenharmony_ci (env[2] == L'T' || env[2] == L't') && 8381cb0ef41Sopenharmony_ci (env[3] == L'H' || env[3] == L'h') && 8391cb0ef41Sopenharmony_ci (env[4] == L'=')) { 8401cb0ef41Sopenharmony_ci return &env[5]; 8411cb0ef41Sopenharmony_ci } 8421cb0ef41Sopenharmony_ci } 8431cb0ef41Sopenharmony_ci 8441cb0ef41Sopenharmony_ci return NULL; 8451cb0ef41Sopenharmony_ci} 8461cb0ef41Sopenharmony_ci 8471cb0ef41Sopenharmony_ci/* 8481cb0ef41Sopenharmony_ci * Called on Windows thread-pool thread to indicate that 8491cb0ef41Sopenharmony_ci * a child process has exited. 8501cb0ef41Sopenharmony_ci */ 8511cb0ef41Sopenharmony_cistatic void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { 8521cb0ef41Sopenharmony_ci uv_process_t* process = (uv_process_t*) data; 8531cb0ef41Sopenharmony_ci uv_loop_t* loop = process->loop; 8541cb0ef41Sopenharmony_ci 8551cb0ef41Sopenharmony_ci assert(didTimeout == FALSE); 8561cb0ef41Sopenharmony_ci assert(process); 8571cb0ef41Sopenharmony_ci assert(!process->exit_cb_pending); 8581cb0ef41Sopenharmony_ci 8591cb0ef41Sopenharmony_ci process->exit_cb_pending = 1; 8601cb0ef41Sopenharmony_ci 8611cb0ef41Sopenharmony_ci /* Post completed */ 8621cb0ef41Sopenharmony_ci POST_COMPLETION_FOR_REQ(loop, &process->exit_req); 8631cb0ef41Sopenharmony_ci} 8641cb0ef41Sopenharmony_ci 8651cb0ef41Sopenharmony_ci 8661cb0ef41Sopenharmony_ci/* Called on main thread after a child process has exited. */ 8671cb0ef41Sopenharmony_civoid uv__process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { 8681cb0ef41Sopenharmony_ci int64_t exit_code; 8691cb0ef41Sopenharmony_ci DWORD status; 8701cb0ef41Sopenharmony_ci 8711cb0ef41Sopenharmony_ci assert(handle->exit_cb_pending); 8721cb0ef41Sopenharmony_ci handle->exit_cb_pending = 0; 8731cb0ef41Sopenharmony_ci 8741cb0ef41Sopenharmony_ci /* If we're closing, don't call the exit callback. Just schedule a close 8751cb0ef41Sopenharmony_ci * callback now. */ 8761cb0ef41Sopenharmony_ci if (handle->flags & UV_HANDLE_CLOSING) { 8771cb0ef41Sopenharmony_ci uv__want_endgame(loop, (uv_handle_t*) handle); 8781cb0ef41Sopenharmony_ci return; 8791cb0ef41Sopenharmony_ci } 8801cb0ef41Sopenharmony_ci 8811cb0ef41Sopenharmony_ci /* Unregister from process notification. */ 8821cb0ef41Sopenharmony_ci if (handle->wait_handle != INVALID_HANDLE_VALUE) { 8831cb0ef41Sopenharmony_ci UnregisterWait(handle->wait_handle); 8841cb0ef41Sopenharmony_ci handle->wait_handle = INVALID_HANDLE_VALUE; 8851cb0ef41Sopenharmony_ci } 8861cb0ef41Sopenharmony_ci 8871cb0ef41Sopenharmony_ci /* Set the handle to inactive: no callbacks will be made after the exit 8881cb0ef41Sopenharmony_ci * callback. */ 8891cb0ef41Sopenharmony_ci uv__handle_stop(handle); 8901cb0ef41Sopenharmony_ci 8911cb0ef41Sopenharmony_ci if (GetExitCodeProcess(handle->process_handle, &status)) { 8921cb0ef41Sopenharmony_ci exit_code = status; 8931cb0ef41Sopenharmony_ci } else { 8941cb0ef41Sopenharmony_ci /* Unable to obtain the exit code. This should never happen. */ 8951cb0ef41Sopenharmony_ci exit_code = uv_translate_sys_error(GetLastError()); 8961cb0ef41Sopenharmony_ci } 8971cb0ef41Sopenharmony_ci 8981cb0ef41Sopenharmony_ci /* Fire the exit callback. */ 8991cb0ef41Sopenharmony_ci if (handle->exit_cb) { 9001cb0ef41Sopenharmony_ci handle->exit_cb(handle, exit_code, handle->exit_signal); 9011cb0ef41Sopenharmony_ci } 9021cb0ef41Sopenharmony_ci} 9031cb0ef41Sopenharmony_ci 9041cb0ef41Sopenharmony_ci 9051cb0ef41Sopenharmony_civoid uv__process_close(uv_loop_t* loop, uv_process_t* handle) { 9061cb0ef41Sopenharmony_ci uv__handle_closing(handle); 9071cb0ef41Sopenharmony_ci 9081cb0ef41Sopenharmony_ci if (handle->wait_handle != INVALID_HANDLE_VALUE) { 9091cb0ef41Sopenharmony_ci /* This blocks until either the wait was cancelled, or the callback has 9101cb0ef41Sopenharmony_ci * completed. */ 9111cb0ef41Sopenharmony_ci BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE); 9121cb0ef41Sopenharmony_ci if (!r) { 9131cb0ef41Sopenharmony_ci /* This should never happen, and if it happens, we can't recover... */ 9141cb0ef41Sopenharmony_ci uv_fatal_error(GetLastError(), "UnregisterWaitEx"); 9151cb0ef41Sopenharmony_ci } 9161cb0ef41Sopenharmony_ci 9171cb0ef41Sopenharmony_ci handle->wait_handle = INVALID_HANDLE_VALUE; 9181cb0ef41Sopenharmony_ci } 9191cb0ef41Sopenharmony_ci 9201cb0ef41Sopenharmony_ci if (!handle->exit_cb_pending) { 9211cb0ef41Sopenharmony_ci uv__want_endgame(loop, (uv_handle_t*)handle); 9221cb0ef41Sopenharmony_ci } 9231cb0ef41Sopenharmony_ci} 9241cb0ef41Sopenharmony_ci 9251cb0ef41Sopenharmony_ci 9261cb0ef41Sopenharmony_civoid uv__process_endgame(uv_loop_t* loop, uv_process_t* handle) { 9271cb0ef41Sopenharmony_ci assert(!handle->exit_cb_pending); 9281cb0ef41Sopenharmony_ci assert(handle->flags & UV_HANDLE_CLOSING); 9291cb0ef41Sopenharmony_ci assert(!(handle->flags & UV_HANDLE_CLOSED)); 9301cb0ef41Sopenharmony_ci 9311cb0ef41Sopenharmony_ci /* Clean-up the process handle. */ 9321cb0ef41Sopenharmony_ci CloseHandle(handle->process_handle); 9331cb0ef41Sopenharmony_ci 9341cb0ef41Sopenharmony_ci uv__handle_close(handle); 9351cb0ef41Sopenharmony_ci} 9361cb0ef41Sopenharmony_ci 9371cb0ef41Sopenharmony_ci 9381cb0ef41Sopenharmony_ciint uv_spawn(uv_loop_t* loop, 9391cb0ef41Sopenharmony_ci uv_process_t* process, 9401cb0ef41Sopenharmony_ci const uv_process_options_t* options) { 9411cb0ef41Sopenharmony_ci int i; 9421cb0ef41Sopenharmony_ci int err = 0; 9431cb0ef41Sopenharmony_ci WCHAR* path = NULL, *alloc_path = NULL; 9441cb0ef41Sopenharmony_ci BOOL result; 9451cb0ef41Sopenharmony_ci WCHAR* application_path = NULL, *application = NULL, *arguments = NULL, 9461cb0ef41Sopenharmony_ci *env = NULL, *cwd = NULL; 9471cb0ef41Sopenharmony_ci STARTUPINFOW startup; 9481cb0ef41Sopenharmony_ci PROCESS_INFORMATION info; 9491cb0ef41Sopenharmony_ci DWORD process_flags; 9501cb0ef41Sopenharmony_ci 9511cb0ef41Sopenharmony_ci uv__process_init(loop, process); 9521cb0ef41Sopenharmony_ci process->exit_cb = options->exit_cb; 9531cb0ef41Sopenharmony_ci 9541cb0ef41Sopenharmony_ci if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { 9551cb0ef41Sopenharmony_ci return UV_ENOTSUP; 9561cb0ef41Sopenharmony_ci } 9571cb0ef41Sopenharmony_ci 9581cb0ef41Sopenharmony_ci if (options->file == NULL || 9591cb0ef41Sopenharmony_ci options->args == NULL) { 9601cb0ef41Sopenharmony_ci return UV_EINVAL; 9611cb0ef41Sopenharmony_ci } 9621cb0ef41Sopenharmony_ci 9631cb0ef41Sopenharmony_ci assert(options->file != NULL); 9641cb0ef41Sopenharmony_ci assert(!(options->flags & ~(UV_PROCESS_DETACHED | 9651cb0ef41Sopenharmony_ci UV_PROCESS_SETGID | 9661cb0ef41Sopenharmony_ci UV_PROCESS_SETUID | 9671cb0ef41Sopenharmony_ci UV_PROCESS_WINDOWS_HIDE | 9681cb0ef41Sopenharmony_ci UV_PROCESS_WINDOWS_HIDE_CONSOLE | 9691cb0ef41Sopenharmony_ci UV_PROCESS_WINDOWS_HIDE_GUI | 9701cb0ef41Sopenharmony_ci UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); 9711cb0ef41Sopenharmony_ci 9721cb0ef41Sopenharmony_ci err = uv__utf8_to_utf16_alloc(options->file, &application); 9731cb0ef41Sopenharmony_ci if (err) 9741cb0ef41Sopenharmony_ci goto done; 9751cb0ef41Sopenharmony_ci 9761cb0ef41Sopenharmony_ci err = make_program_args( 9771cb0ef41Sopenharmony_ci options->args, 9781cb0ef41Sopenharmony_ci options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, 9791cb0ef41Sopenharmony_ci &arguments); 9801cb0ef41Sopenharmony_ci if (err) 9811cb0ef41Sopenharmony_ci goto done; 9821cb0ef41Sopenharmony_ci 9831cb0ef41Sopenharmony_ci if (options->env) { 9841cb0ef41Sopenharmony_ci err = make_program_env(options->env, &env); 9851cb0ef41Sopenharmony_ci if (err) 9861cb0ef41Sopenharmony_ci goto done; 9871cb0ef41Sopenharmony_ci } 9881cb0ef41Sopenharmony_ci 9891cb0ef41Sopenharmony_ci if (options->cwd) { 9901cb0ef41Sopenharmony_ci /* Explicit cwd */ 9911cb0ef41Sopenharmony_ci err = uv__utf8_to_utf16_alloc(options->cwd, &cwd); 9921cb0ef41Sopenharmony_ci if (err) 9931cb0ef41Sopenharmony_ci goto done; 9941cb0ef41Sopenharmony_ci 9951cb0ef41Sopenharmony_ci } else { 9961cb0ef41Sopenharmony_ci /* Inherit cwd */ 9971cb0ef41Sopenharmony_ci DWORD cwd_len, r; 9981cb0ef41Sopenharmony_ci 9991cb0ef41Sopenharmony_ci cwd_len = GetCurrentDirectoryW(0, NULL); 10001cb0ef41Sopenharmony_ci if (!cwd_len) { 10011cb0ef41Sopenharmony_ci err = GetLastError(); 10021cb0ef41Sopenharmony_ci goto done; 10031cb0ef41Sopenharmony_ci } 10041cb0ef41Sopenharmony_ci 10051cb0ef41Sopenharmony_ci cwd = (WCHAR*) uv__malloc(cwd_len * sizeof(WCHAR)); 10061cb0ef41Sopenharmony_ci if (cwd == NULL) { 10071cb0ef41Sopenharmony_ci err = ERROR_OUTOFMEMORY; 10081cb0ef41Sopenharmony_ci goto done; 10091cb0ef41Sopenharmony_ci } 10101cb0ef41Sopenharmony_ci 10111cb0ef41Sopenharmony_ci r = GetCurrentDirectoryW(cwd_len, cwd); 10121cb0ef41Sopenharmony_ci if (r == 0 || r >= cwd_len) { 10131cb0ef41Sopenharmony_ci err = GetLastError(); 10141cb0ef41Sopenharmony_ci goto done; 10151cb0ef41Sopenharmony_ci } 10161cb0ef41Sopenharmony_ci } 10171cb0ef41Sopenharmony_ci 10181cb0ef41Sopenharmony_ci /* Get PATH environment variable. */ 10191cb0ef41Sopenharmony_ci path = find_path(env); 10201cb0ef41Sopenharmony_ci if (path == NULL) { 10211cb0ef41Sopenharmony_ci DWORD path_len, r; 10221cb0ef41Sopenharmony_ci 10231cb0ef41Sopenharmony_ci path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); 10241cb0ef41Sopenharmony_ci if (path_len == 0) { 10251cb0ef41Sopenharmony_ci err = GetLastError(); 10261cb0ef41Sopenharmony_ci goto done; 10271cb0ef41Sopenharmony_ci } 10281cb0ef41Sopenharmony_ci 10291cb0ef41Sopenharmony_ci alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR)); 10301cb0ef41Sopenharmony_ci if (alloc_path == NULL) { 10311cb0ef41Sopenharmony_ci err = ERROR_OUTOFMEMORY; 10321cb0ef41Sopenharmony_ci goto done; 10331cb0ef41Sopenharmony_ci } 10341cb0ef41Sopenharmony_ci path = alloc_path; 10351cb0ef41Sopenharmony_ci 10361cb0ef41Sopenharmony_ci r = GetEnvironmentVariableW(L"PATH", path, path_len); 10371cb0ef41Sopenharmony_ci if (r == 0 || r >= path_len) { 10381cb0ef41Sopenharmony_ci err = GetLastError(); 10391cb0ef41Sopenharmony_ci goto done; 10401cb0ef41Sopenharmony_ci } 10411cb0ef41Sopenharmony_ci } 10421cb0ef41Sopenharmony_ci 10431cb0ef41Sopenharmony_ci err = uv__stdio_create(loop, options, &process->child_stdio_buffer); 10441cb0ef41Sopenharmony_ci if (err) 10451cb0ef41Sopenharmony_ci goto done; 10461cb0ef41Sopenharmony_ci 10471cb0ef41Sopenharmony_ci application_path = search_path(application, 10481cb0ef41Sopenharmony_ci cwd, 10491cb0ef41Sopenharmony_ci path); 10501cb0ef41Sopenharmony_ci if (application_path == NULL) { 10511cb0ef41Sopenharmony_ci /* Not found. */ 10521cb0ef41Sopenharmony_ci err = ERROR_FILE_NOT_FOUND; 10531cb0ef41Sopenharmony_ci goto done; 10541cb0ef41Sopenharmony_ci } 10551cb0ef41Sopenharmony_ci 10561cb0ef41Sopenharmony_ci startup.cb = sizeof(startup); 10571cb0ef41Sopenharmony_ci startup.lpReserved = NULL; 10581cb0ef41Sopenharmony_ci startup.lpDesktop = NULL; 10591cb0ef41Sopenharmony_ci startup.lpTitle = NULL; 10601cb0ef41Sopenharmony_ci startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; 10611cb0ef41Sopenharmony_ci 10621cb0ef41Sopenharmony_ci startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer); 10631cb0ef41Sopenharmony_ci startup.lpReserved2 = (BYTE*) process->child_stdio_buffer; 10641cb0ef41Sopenharmony_ci 10651cb0ef41Sopenharmony_ci startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0); 10661cb0ef41Sopenharmony_ci startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); 10671cb0ef41Sopenharmony_ci startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); 10681cb0ef41Sopenharmony_ci 10691cb0ef41Sopenharmony_ci process_flags = CREATE_UNICODE_ENVIRONMENT; 10701cb0ef41Sopenharmony_ci 10711cb0ef41Sopenharmony_ci if ((options->flags & UV_PROCESS_WINDOWS_HIDE_CONSOLE) || 10721cb0ef41Sopenharmony_ci (options->flags & UV_PROCESS_WINDOWS_HIDE)) { 10731cb0ef41Sopenharmony_ci /* Avoid creating console window if stdio is not inherited. */ 10741cb0ef41Sopenharmony_ci for (i = 0; i < options->stdio_count; i++) { 10751cb0ef41Sopenharmony_ci if (options->stdio[i].flags & UV_INHERIT_FD) 10761cb0ef41Sopenharmony_ci break; 10771cb0ef41Sopenharmony_ci if (i == options->stdio_count - 1) 10781cb0ef41Sopenharmony_ci process_flags |= CREATE_NO_WINDOW; 10791cb0ef41Sopenharmony_ci } 10801cb0ef41Sopenharmony_ci } 10811cb0ef41Sopenharmony_ci if ((options->flags & UV_PROCESS_WINDOWS_HIDE_GUI) || 10821cb0ef41Sopenharmony_ci (options->flags & UV_PROCESS_WINDOWS_HIDE)) { 10831cb0ef41Sopenharmony_ci /* Use SW_HIDE to avoid any potential process window. */ 10841cb0ef41Sopenharmony_ci startup.wShowWindow = SW_HIDE; 10851cb0ef41Sopenharmony_ci } else { 10861cb0ef41Sopenharmony_ci startup.wShowWindow = SW_SHOWDEFAULT; 10871cb0ef41Sopenharmony_ci } 10881cb0ef41Sopenharmony_ci 10891cb0ef41Sopenharmony_ci if (options->flags & UV_PROCESS_DETACHED) { 10901cb0ef41Sopenharmony_ci /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That 10911cb0ef41Sopenharmony_ci * means that libuv might not let you create a fully daemonized process 10921cb0ef41Sopenharmony_ci * when run under job control. However the type of job control that libuv 10931cb0ef41Sopenharmony_ci * itself creates doesn't trickle down to subprocesses so they can still 10941cb0ef41Sopenharmony_ci * daemonize. 10951cb0ef41Sopenharmony_ci * 10961cb0ef41Sopenharmony_ci * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the 10971cb0ef41Sopenharmony_ci * CreateProcess call fail if we're under job control that doesn't allow 10981cb0ef41Sopenharmony_ci * breakaway. 10991cb0ef41Sopenharmony_ci */ 11001cb0ef41Sopenharmony_ci process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; 11011cb0ef41Sopenharmony_ci } 11021cb0ef41Sopenharmony_ci 11031cb0ef41Sopenharmony_ci if (!CreateProcessW(application_path, 11041cb0ef41Sopenharmony_ci arguments, 11051cb0ef41Sopenharmony_ci NULL, 11061cb0ef41Sopenharmony_ci NULL, 11071cb0ef41Sopenharmony_ci 1, 11081cb0ef41Sopenharmony_ci process_flags, 11091cb0ef41Sopenharmony_ci env, 11101cb0ef41Sopenharmony_ci cwd, 11111cb0ef41Sopenharmony_ci &startup, 11121cb0ef41Sopenharmony_ci &info)) { 11131cb0ef41Sopenharmony_ci /* CreateProcessW failed. */ 11141cb0ef41Sopenharmony_ci err = GetLastError(); 11151cb0ef41Sopenharmony_ci goto done; 11161cb0ef41Sopenharmony_ci } 11171cb0ef41Sopenharmony_ci 11181cb0ef41Sopenharmony_ci /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */ 11191cb0ef41Sopenharmony_ci 11201cb0ef41Sopenharmony_ci process->process_handle = info.hProcess; 11211cb0ef41Sopenharmony_ci process->pid = info.dwProcessId; 11221cb0ef41Sopenharmony_ci 11231cb0ef41Sopenharmony_ci /* If the process isn't spawned as detached, assign to the global job object 11241cb0ef41Sopenharmony_ci * so windows will kill it when the parent process dies. */ 11251cb0ef41Sopenharmony_ci if (!(options->flags & UV_PROCESS_DETACHED)) { 11261cb0ef41Sopenharmony_ci uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle); 11271cb0ef41Sopenharmony_ci 11281cb0ef41Sopenharmony_ci if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) { 11291cb0ef41Sopenharmony_ci /* AssignProcessToJobObject might fail if this process is under job 11301cb0ef41Sopenharmony_ci * control and the job doesn't have the 11311cb0ef41Sopenharmony_ci * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version 11321cb0ef41Sopenharmony_ci * that doesn't support nested jobs. 11331cb0ef41Sopenharmony_ci * 11341cb0ef41Sopenharmony_ci * When that happens we just swallow the error and continue without 11351cb0ef41Sopenharmony_ci * establishing a kill-child-on-parent-exit relationship, otherwise 11361cb0ef41Sopenharmony_ci * there would be no way for libuv applications run under job control 11371cb0ef41Sopenharmony_ci * to spawn processes at all. 11381cb0ef41Sopenharmony_ci */ 11391cb0ef41Sopenharmony_ci DWORD err = GetLastError(); 11401cb0ef41Sopenharmony_ci if (err != ERROR_ACCESS_DENIED) 11411cb0ef41Sopenharmony_ci uv_fatal_error(err, "AssignProcessToJobObject"); 11421cb0ef41Sopenharmony_ci } 11431cb0ef41Sopenharmony_ci } 11441cb0ef41Sopenharmony_ci 11451cb0ef41Sopenharmony_ci /* Set IPC pid to all IPC pipes. */ 11461cb0ef41Sopenharmony_ci for (i = 0; i < options->stdio_count; i++) { 11471cb0ef41Sopenharmony_ci const uv_stdio_container_t* fdopt = &options->stdio[i]; 11481cb0ef41Sopenharmony_ci if (fdopt->flags & UV_CREATE_PIPE && 11491cb0ef41Sopenharmony_ci fdopt->data.stream->type == UV_NAMED_PIPE && 11501cb0ef41Sopenharmony_ci ((uv_pipe_t*) fdopt->data.stream)->ipc) { 11511cb0ef41Sopenharmony_ci ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_remote_pid = 11521cb0ef41Sopenharmony_ci info.dwProcessId; 11531cb0ef41Sopenharmony_ci } 11541cb0ef41Sopenharmony_ci } 11551cb0ef41Sopenharmony_ci 11561cb0ef41Sopenharmony_ci /* Setup notifications for when the child process exits. */ 11571cb0ef41Sopenharmony_ci result = RegisterWaitForSingleObject(&process->wait_handle, 11581cb0ef41Sopenharmony_ci process->process_handle, exit_wait_callback, (void*)process, INFINITE, 11591cb0ef41Sopenharmony_ci WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); 11601cb0ef41Sopenharmony_ci if (!result) { 11611cb0ef41Sopenharmony_ci uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); 11621cb0ef41Sopenharmony_ci } 11631cb0ef41Sopenharmony_ci 11641cb0ef41Sopenharmony_ci CloseHandle(info.hThread); 11651cb0ef41Sopenharmony_ci 11661cb0ef41Sopenharmony_ci assert(!err); 11671cb0ef41Sopenharmony_ci 11681cb0ef41Sopenharmony_ci /* Make the handle active. It will remain active until the exit callback is 11691cb0ef41Sopenharmony_ci * made or the handle is closed, whichever happens first. */ 11701cb0ef41Sopenharmony_ci uv__handle_start(process); 11711cb0ef41Sopenharmony_ci 11721cb0ef41Sopenharmony_ci /* Cleanup, whether we succeeded or failed. */ 11731cb0ef41Sopenharmony_ci done: 11741cb0ef41Sopenharmony_ci uv__free(application); 11751cb0ef41Sopenharmony_ci uv__free(application_path); 11761cb0ef41Sopenharmony_ci uv__free(arguments); 11771cb0ef41Sopenharmony_ci uv__free(cwd); 11781cb0ef41Sopenharmony_ci uv__free(env); 11791cb0ef41Sopenharmony_ci uv__free(alloc_path); 11801cb0ef41Sopenharmony_ci 11811cb0ef41Sopenharmony_ci if (process->child_stdio_buffer != NULL) { 11821cb0ef41Sopenharmony_ci /* Clean up child stdio handles. */ 11831cb0ef41Sopenharmony_ci uv__stdio_destroy(process->child_stdio_buffer); 11841cb0ef41Sopenharmony_ci process->child_stdio_buffer = NULL; 11851cb0ef41Sopenharmony_ci } 11861cb0ef41Sopenharmony_ci 11871cb0ef41Sopenharmony_ci return uv_translate_sys_error(err); 11881cb0ef41Sopenharmony_ci} 11891cb0ef41Sopenharmony_ci 11901cb0ef41Sopenharmony_ci 11911cb0ef41Sopenharmony_cistatic int uv__kill(HANDLE process_handle, int signum) { 11921cb0ef41Sopenharmony_ci if (signum < 0 || signum >= NSIG) { 11931cb0ef41Sopenharmony_ci return UV_EINVAL; 11941cb0ef41Sopenharmony_ci } 11951cb0ef41Sopenharmony_ci 11961cb0ef41Sopenharmony_ci switch (signum) { 11971cb0ef41Sopenharmony_ci case SIGTERM: 11981cb0ef41Sopenharmony_ci case SIGKILL: 11991cb0ef41Sopenharmony_ci case SIGINT: { 12001cb0ef41Sopenharmony_ci /* Unconditionally terminate the process. On Windows, killed processes 12011cb0ef41Sopenharmony_ci * normally return 1. */ 12021cb0ef41Sopenharmony_ci DWORD status; 12031cb0ef41Sopenharmony_ci int err; 12041cb0ef41Sopenharmony_ci 12051cb0ef41Sopenharmony_ci if (TerminateProcess(process_handle, 1)) 12061cb0ef41Sopenharmony_ci return 0; 12071cb0ef41Sopenharmony_ci 12081cb0ef41Sopenharmony_ci /* If the process already exited before TerminateProcess was called,. 12091cb0ef41Sopenharmony_ci * TerminateProcess will fail with ERROR_ACCESS_DENIED. */ 12101cb0ef41Sopenharmony_ci err = GetLastError(); 12111cb0ef41Sopenharmony_ci if (err == ERROR_ACCESS_DENIED && 12121cb0ef41Sopenharmony_ci GetExitCodeProcess(process_handle, &status) && 12131cb0ef41Sopenharmony_ci status != STILL_ACTIVE) { 12141cb0ef41Sopenharmony_ci return UV_ESRCH; 12151cb0ef41Sopenharmony_ci } 12161cb0ef41Sopenharmony_ci 12171cb0ef41Sopenharmony_ci return uv_translate_sys_error(err); 12181cb0ef41Sopenharmony_ci } 12191cb0ef41Sopenharmony_ci 12201cb0ef41Sopenharmony_ci case 0: { 12211cb0ef41Sopenharmony_ci /* Health check: is the process still alive? */ 12221cb0ef41Sopenharmony_ci DWORD status; 12231cb0ef41Sopenharmony_ci 12241cb0ef41Sopenharmony_ci if (!GetExitCodeProcess(process_handle, &status)) 12251cb0ef41Sopenharmony_ci return uv_translate_sys_error(GetLastError()); 12261cb0ef41Sopenharmony_ci 12271cb0ef41Sopenharmony_ci if (status != STILL_ACTIVE) 12281cb0ef41Sopenharmony_ci return UV_ESRCH; 12291cb0ef41Sopenharmony_ci 12301cb0ef41Sopenharmony_ci return 0; 12311cb0ef41Sopenharmony_ci } 12321cb0ef41Sopenharmony_ci 12331cb0ef41Sopenharmony_ci default: 12341cb0ef41Sopenharmony_ci /* Unsupported signal. */ 12351cb0ef41Sopenharmony_ci return UV_ENOSYS; 12361cb0ef41Sopenharmony_ci } 12371cb0ef41Sopenharmony_ci} 12381cb0ef41Sopenharmony_ci 12391cb0ef41Sopenharmony_ci 12401cb0ef41Sopenharmony_ciint uv_process_kill(uv_process_t* process, int signum) { 12411cb0ef41Sopenharmony_ci int err; 12421cb0ef41Sopenharmony_ci 12431cb0ef41Sopenharmony_ci if (process->process_handle == INVALID_HANDLE_VALUE) { 12441cb0ef41Sopenharmony_ci return UV_EINVAL; 12451cb0ef41Sopenharmony_ci } 12461cb0ef41Sopenharmony_ci 12471cb0ef41Sopenharmony_ci err = uv__kill(process->process_handle, signum); 12481cb0ef41Sopenharmony_ci if (err) { 12491cb0ef41Sopenharmony_ci return err; /* err is already translated. */ 12501cb0ef41Sopenharmony_ci } 12511cb0ef41Sopenharmony_ci 12521cb0ef41Sopenharmony_ci process->exit_signal = signum; 12531cb0ef41Sopenharmony_ci 12541cb0ef41Sopenharmony_ci return 0; 12551cb0ef41Sopenharmony_ci} 12561cb0ef41Sopenharmony_ci 12571cb0ef41Sopenharmony_ci 12581cb0ef41Sopenharmony_ciint uv_kill(int pid, int signum) { 12591cb0ef41Sopenharmony_ci int err; 12601cb0ef41Sopenharmony_ci HANDLE process_handle; 12611cb0ef41Sopenharmony_ci 12621cb0ef41Sopenharmony_ci if (pid == 0) { 12631cb0ef41Sopenharmony_ci process_handle = GetCurrentProcess(); 12641cb0ef41Sopenharmony_ci } else { 12651cb0ef41Sopenharmony_ci process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, 12661cb0ef41Sopenharmony_ci FALSE, 12671cb0ef41Sopenharmony_ci pid); 12681cb0ef41Sopenharmony_ci } 12691cb0ef41Sopenharmony_ci 12701cb0ef41Sopenharmony_ci if (process_handle == NULL) { 12711cb0ef41Sopenharmony_ci err = GetLastError(); 12721cb0ef41Sopenharmony_ci if (err == ERROR_INVALID_PARAMETER) { 12731cb0ef41Sopenharmony_ci return UV_ESRCH; 12741cb0ef41Sopenharmony_ci } else { 12751cb0ef41Sopenharmony_ci return uv_translate_sys_error(err); 12761cb0ef41Sopenharmony_ci } 12771cb0ef41Sopenharmony_ci } 12781cb0ef41Sopenharmony_ci 12791cb0ef41Sopenharmony_ci err = uv__kill(process_handle, signum); 12801cb0ef41Sopenharmony_ci CloseHandle(process_handle); 12811cb0ef41Sopenharmony_ci 12821cb0ef41Sopenharmony_ci return err; /* err is already translated. */ 12831cb0ef41Sopenharmony_ci} 1284