1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining a copy 4 * of this software and associated documentation files (the "Software"), to 5 * deal in the Software without restriction, including without limitation the 6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 * sell copies of the Software, and to permit persons to whom the Software is 8 * furnished to do so, subject to the following conditions: 9 * 10 * The above copyright notice and this permission notice shall be included in 11 * all copies or substantial portions of the Software. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 * IN THE SOFTWARE. 20 */ 21 22 #include <assert.h> 23 #include <io.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <signal.h> 27 #include <limits.h> 28 #include <wchar.h> 29 30 #include "uv.h" 31 #include "internal.h" 32 #include "handle-inl.h" 33 #include "req-inl.h" 34 #include <dbghelp.h> 35 #include <shlobj.h> 36 #include <psapi.h> /* GetModuleBaseNameW */ 37 38 39 #define SIGKILL 9 40 41 42 typedef struct env_var { 43 const WCHAR* const wide; 44 const WCHAR* const wide_eq; 45 const size_t len; /* including null or '=' */ 46 } env_var_t; 47 48 #define E_V(str) { L##str, L##str L"=", sizeof(str) } 49 50 static const env_var_t required_vars[] = { /* keep me sorted */ 51 E_V("HOMEDRIVE"), 52 E_V("HOMEPATH"), 53 E_V("LOGONSERVER"), 54 E_V("PATH"), 55 E_V("SYSTEMDRIVE"), 56 E_V("SYSTEMROOT"), 57 E_V("TEMP"), 58 E_V("USERDOMAIN"), 59 E_V("USERNAME"), 60 E_V("USERPROFILE"), 61 E_V("WINDIR"), 62 }; 63 64 65 static HANDLE uv_global_job_handle_; 66 static uv_once_t uv_global_job_handle_init_guard_ = UV_ONCE_INIT; 67 68 69 static void uv__init_global_job_handle(void) { 70 /* Create a job object and set it up to kill all contained processes when 71 * it's closed. Since this handle is made non-inheritable and we're not 72 * giving it to anyone, we're the only process holding a reference to it. 73 * That means that if this process exits it is closed and all the processes 74 * it contains are killed. All processes created with uv_spawn that are not 75 * spawned with the UV_PROCESS_DETACHED flag are assigned to this job. 76 * 77 * We're setting the JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag so only the 78 * processes that we explicitly add are affected, and *their* subprocesses 79 * are not. This ensures that our child processes are not limited in their 80 * ability to use job control on Windows versions that don't deal with 81 * nested jobs (prior to Windows 8 / Server 2012). It also lets our child 82 * processes created detached processes without explicitly breaking away 83 * from job control (which uv_spawn doesn't, either). 84 */ 85 SECURITY_ATTRIBUTES attr; 86 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; 87 88 memset(&attr, 0, sizeof attr); 89 attr.bInheritHandle = FALSE; 90 91 memset(&info, 0, sizeof info); 92 info.BasicLimitInformation.LimitFlags = 93 JOB_OBJECT_LIMIT_BREAKAWAY_OK | 94 JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | 95 JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | 96 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 97 98 uv_global_job_handle_ = CreateJobObjectW(&attr, NULL); 99 if (uv_global_job_handle_ == NULL) 100 uv_fatal_error(GetLastError(), "CreateJobObjectW"); 101 102 if (!SetInformationJobObject(uv_global_job_handle_, 103 JobObjectExtendedLimitInformation, 104 &info, 105 sizeof info)) 106 uv_fatal_error(GetLastError(), "SetInformationJobObject"); 107 108 109 if (!AssignProcessToJobObject(uv_global_job_handle_, GetCurrentProcess())) { 110 /* Make sure this handle is functional. The Windows kernel has a bug that 111 * if the first use of AssignProcessToJobObject is for a Windows Store 112 * program, subsequent attempts to use the handle with fail with 113 * INVALID_PARAMETER (87). This is possibly because all uses of the handle 114 * must be for the same Terminal Services session. We can ensure it is tied 115 * to our current session now by adding ourself to it. We could remove 116 * ourself afterwards, but there doesn't seem to be a reason to. 117 */ 118 DWORD err = GetLastError(); 119 if (err != ERROR_ACCESS_DENIED) 120 uv_fatal_error(err, "AssignProcessToJobObject"); 121 } 122 } 123 124 125 static int uv__utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) { 126 return uv__convert_utf8_to_utf16(s, ws_ptr); 127 } 128 129 130 static void uv__process_init(uv_loop_t* loop, uv_process_t* handle) { 131 uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS); 132 handle->exit_cb = NULL; 133 handle->pid = 0; 134 handle->exit_signal = 0; 135 handle->wait_handle = INVALID_HANDLE_VALUE; 136 handle->process_handle = INVALID_HANDLE_VALUE; 137 handle->exit_cb_pending = 0; 138 139 UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT); 140 handle->exit_req.data = handle; 141 } 142 143 144 /* 145 * Path search functions 146 */ 147 148 /* 149 * Helper function for search_path 150 */ 151 static WCHAR* search_path_join_test(const WCHAR* dir, 152 size_t dir_len, 153 const WCHAR* name, 154 size_t name_len, 155 const WCHAR* ext, 156 size_t ext_len, 157 const WCHAR* cwd, 158 size_t cwd_len) { 159 WCHAR *result, *result_pos; 160 DWORD attrs; 161 if (dir_len > 2 && 162 ((dir[0] == L'\\' || dir[0] == L'/') && 163 (dir[1] == L'\\' || dir[1] == L'/'))) { 164 /* It's a UNC path so ignore cwd */ 165 cwd_len = 0; 166 } else if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) { 167 /* It's a full path without drive letter, use cwd's drive letter only */ 168 cwd_len = 2; 169 } else if (dir_len >= 2 && dir[1] == L':' && 170 (dir_len < 3 || (dir[2] != L'/' && dir[2] != L'\\'))) { 171 /* It's a relative path with drive letter (ext.g. D:../some/file) 172 * Replace drive letter in dir by full cwd if it points to the same drive, 173 * otherwise use the dir only. 174 */ 175 if (cwd_len < 2 || _wcsnicmp(cwd, dir, 2) != 0) { 176 cwd_len = 0; 177 } else { 178 dir += 2; 179 dir_len -= 2; 180 } 181 } else if (dir_len > 2 && dir[1] == L':') { 182 /* It's an absolute path with drive letter 183 * Don't use the cwd at all 184 */ 185 cwd_len = 0; 186 } 187 188 /* Allocate buffer for output */ 189 result = result_pos = (WCHAR*)uv__malloc(sizeof(WCHAR) * 190 (cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1)); 191 192 /* Copy cwd */ 193 wcsncpy(result_pos, cwd, cwd_len); 194 result_pos += cwd_len; 195 196 /* Add a path separator if cwd didn't end with one */ 197 if (cwd_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) { 198 result_pos[0] = L'\\'; 199 result_pos++; 200 } 201 202 /* Copy dir */ 203 wcsncpy(result_pos, dir, dir_len); 204 result_pos += dir_len; 205 206 /* Add a separator if the dir didn't end with one */ 207 if (dir_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) { 208 result_pos[0] = L'\\'; 209 result_pos++; 210 } 211 212 /* Copy filename */ 213 wcsncpy(result_pos, name, name_len); 214 result_pos += name_len; 215 216 if (ext_len) { 217 /* Add a dot if the filename didn't end with one */ 218 if (name_len && result_pos[-1] != '.') { 219 result_pos[0] = L'.'; 220 result_pos++; 221 } 222 223 /* Copy extension */ 224 wcsncpy(result_pos, ext, ext_len); 225 result_pos += ext_len; 226 } 227 228 /* Null terminator */ 229 result_pos[0] = L'\0'; 230 231 attrs = GetFileAttributesW(result); 232 233 if (attrs != INVALID_FILE_ATTRIBUTES && 234 !(attrs & FILE_ATTRIBUTE_DIRECTORY)) { 235 return result; 236 } 237 238 uv__free(result); 239 return NULL; 240 } 241 242 243 /* 244 * Helper function for search_path 245 */ 246 static WCHAR* path_search_walk_ext(const WCHAR *dir, 247 size_t dir_len, 248 const WCHAR *name, 249 size_t name_len, 250 WCHAR *cwd, 251 size_t cwd_len, 252 int name_has_ext) { 253 WCHAR* result; 254 255 /* If the name itself has a nonempty extension, try this extension first */ 256 if (name_has_ext) { 257 result = search_path_join_test(dir, dir_len, 258 name, name_len, 259 L"", 0, 260 cwd, cwd_len); 261 if (result != NULL) { 262 return result; 263 } 264 } 265 266 /* Try .com extension */ 267 result = search_path_join_test(dir, dir_len, 268 name, name_len, 269 L"com", 3, 270 cwd, cwd_len); 271 if (result != NULL) { 272 return result; 273 } 274 275 /* Try .exe extension */ 276 result = search_path_join_test(dir, dir_len, 277 name, name_len, 278 L"exe", 3, 279 cwd, cwd_len); 280 if (result != NULL) { 281 return result; 282 } 283 284 return NULL; 285 } 286 287 288 /* 289 * search_path searches the system path for an executable filename - 290 * the windows API doesn't provide this as a standalone function nor as an 291 * option to CreateProcess. 292 * 293 * It tries to return an absolute filename. 294 * 295 * Furthermore, it tries to follow the semantics that cmd.exe, with this 296 * exception that PATHEXT environment variable isn't used. Since CreateProcess 297 * can start only .com and .exe files, only those extensions are tried. This 298 * behavior equals that of msvcrt's spawn functions. 299 * 300 * - Do not search the path if the filename already contains a path (either 301 * relative or absolute). 302 * 303 * - If there's really only a filename, check the current directory for file, 304 * then search all path directories. 305 * 306 * - If filename specified has *any* extension, or already contains a path 307 * and the UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME flag is specified, 308 * search for the file with the exact specified filename first. 309 * 310 * - If the literal filename is not found in a directory, try *appending* 311 * (not replacing) .com first and then .exe. 312 * 313 * - The path variable may contain relative paths; relative paths are relative 314 * to the cwd. 315 * 316 * - Directories in path may or may not end with a trailing backslash. 317 * 318 * - CMD does not trim leading/trailing whitespace from path/pathex entries 319 * nor from the environment variables as a whole. 320 * 321 * - When cmd.exe cannot read a directory, it will just skip it and go on 322 * searching. However, unlike posix-y systems, it will happily try to run a 323 * file that is not readable/executable; if the spawn fails it will not 324 * continue searching. 325 * 326 * UNC path support: we are dealing with UNC paths in both the path and the 327 * filename. This is a deviation from what cmd.exe does (it does not let you 328 * start a program by specifying an UNC path on the command line) but this is 329 * really a pointless restriction. 330 * 331 */ 332 static WCHAR* search_path(const WCHAR *file, 333 WCHAR *cwd, 334 const WCHAR *path, 335 unsigned int flags) { 336 int file_has_dir; 337 WCHAR* result = NULL; 338 WCHAR *file_name_start; 339 WCHAR *dot; 340 const WCHAR *dir_start, *dir_end, *dir_path; 341 size_t dir_len; 342 int name_has_ext; 343 344 size_t file_len = wcslen(file); 345 size_t cwd_len = wcslen(cwd); 346 347 /* If the caller supplies an empty filename, 348 * we're not gonna return c:\windows\.exe -- GFY! 349 */ 350 if (file_len == 0 351 || (file_len == 1 && file[0] == L'.')) { 352 return NULL; 353 } 354 355 /* Find the start of the filename so we can split the directory from the 356 * name. */ 357 for (file_name_start = (WCHAR*)file + file_len; 358 file_name_start > file 359 && file_name_start[-1] != L'\\' 360 && file_name_start[-1] != L'/' 361 && file_name_start[-1] != L':'; 362 file_name_start--); 363 364 file_has_dir = file_name_start != file; 365 366 /* Check if the filename includes an extension */ 367 dot = wcschr(file_name_start, L'.'); 368 name_has_ext = (dot != NULL && dot[1] != L'\0'); 369 370 if (file_has_dir) { 371 /* The file has a path inside, don't use path */ 372 result = path_search_walk_ext( 373 file, file_name_start - file, 374 file_name_start, file_len - (file_name_start - file), 375 cwd, cwd_len, 376 name_has_ext || (flags & UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME)); 377 378 } else { 379 dir_end = path; 380 381 if (NeedCurrentDirectoryForExePathW(L"")) { 382 /* The file is really only a name; look in cwd first, then scan path */ 383 result = path_search_walk_ext(L"", 0, 384 file, file_len, 385 cwd, cwd_len, 386 name_has_ext); 387 } 388 389 while (result == NULL) { 390 if (dir_end == NULL || *dir_end == L'\0') { 391 break; 392 } 393 394 /* Skip the separator that dir_end now points to */ 395 if (dir_end != path || *path == L';') { 396 dir_end++; 397 } 398 399 /* Next slice starts just after where the previous one ended */ 400 dir_start = dir_end; 401 402 /* If path is quoted, find quote end */ 403 if (*dir_start == L'"' || *dir_start == L'\'') { 404 dir_end = wcschr(dir_start + 1, *dir_start); 405 if (dir_end == NULL) { 406 dir_end = wcschr(dir_start, L'\0'); 407 } 408 } 409 /* Slice until the next ; or \0 is found */ 410 dir_end = wcschr(dir_end, L';'); 411 if (dir_end == NULL) { 412 dir_end = wcschr(dir_start, L'\0'); 413 } 414 415 /* If the slice is zero-length, don't bother */ 416 if (dir_end - dir_start == 0) { 417 continue; 418 } 419 420 dir_path = dir_start; 421 dir_len = dir_end - dir_start; 422 423 /* Adjust if the path is quoted. */ 424 if (dir_path[0] == '"' || dir_path[0] == '\'') { 425 ++dir_path; 426 --dir_len; 427 } 428 429 if (dir_path[dir_len - 1] == '"' || dir_path[dir_len - 1] == '\'') { 430 --dir_len; 431 } 432 433 result = path_search_walk_ext(dir_path, dir_len, 434 file, file_len, 435 cwd, cwd_len, 436 name_has_ext); 437 } 438 } 439 440 return result; 441 } 442 443 444 /* 445 * Quotes command line arguments 446 * Returns a pointer to the end (next char to be written) of the buffer 447 */ 448 WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) { 449 size_t len = wcslen(source); 450 size_t i; 451 int quote_hit; 452 WCHAR* start; 453 454 if (len == 0) { 455 /* Need double quotation for empty argument */ 456 *(target++) = L'"'; 457 *(target++) = L'"'; 458 return target; 459 } 460 461 if (NULL == wcspbrk(source, L" \t\"")) { 462 /* No quotation needed */ 463 wcsncpy(target, source, len); 464 target += len; 465 return target; 466 } 467 468 if (NULL == wcspbrk(source, L"\"\\")) { 469 /* 470 * No embedded double quotes or backlashes, so I can just wrap 471 * quote marks around the whole thing. 472 */ 473 *(target++) = L'"'; 474 wcsncpy(target, source, len); 475 target += len; 476 *(target++) = L'"'; 477 return target; 478 } 479 480 /* 481 * Expected input/output: 482 * input : hello"world 483 * output: "hello\"world" 484 * input : hello""world 485 * output: "hello\"\"world" 486 * input : hello\world 487 * output: hello\world 488 * input : hello\\world 489 * output: hello\\world 490 * input : hello\"world 491 * output: "hello\\\"world" 492 * input : hello\\"world 493 * output: "hello\\\\\"world" 494 * input : hello world\ 495 * output: "hello world\\" 496 */ 497 498 *(target++) = L'"'; 499 start = target; 500 quote_hit = 1; 501 502 for (i = len; i > 0; --i) { 503 *(target++) = source[i - 1]; 504 505 if (quote_hit && source[i - 1] == L'\\') { 506 *(target++) = L'\\'; 507 } else if(source[i - 1] == L'"') { 508 quote_hit = 1; 509 *(target++) = L'\\'; 510 } else { 511 quote_hit = 0; 512 } 513 } 514 target[0] = L'\0'; 515 _wcsrev(start); 516 *(target++) = L'"'; 517 return target; 518 } 519 520 521 int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) { 522 char** arg; 523 WCHAR* dst = NULL; 524 WCHAR* temp_buffer = NULL; 525 size_t dst_len = 0; 526 size_t temp_buffer_len = 0; 527 WCHAR* pos; 528 int arg_count = 0; 529 int err = 0; 530 531 /* Count the required size. */ 532 for (arg = args; *arg; arg++) { 533 ssize_t arg_len; 534 535 arg_len = uv_wtf8_length_as_utf16(*arg); 536 if (arg_len < 0) 537 return arg_len; 538 539 dst_len += arg_len; 540 541 if ((size_t) arg_len > temp_buffer_len) 542 temp_buffer_len = arg_len; 543 544 arg_count++; 545 } 546 547 /* Adjust for potential quotes. Also assume the worst-case scenario that 548 * every character needs escaping, so we need twice as much space. */ 549 dst_len = dst_len * 2 + arg_count * 2; 550 551 /* Allocate buffer for the final command line. */ 552 dst = uv__malloc(dst_len * sizeof(WCHAR)); 553 if (dst == NULL) { 554 err = UV_ENOMEM; 555 goto error; 556 } 557 558 /* Allocate temporary working buffer. */ 559 temp_buffer = uv__malloc(temp_buffer_len * sizeof(WCHAR)); 560 if (temp_buffer == NULL) { 561 err = UV_ENOMEM; 562 goto error; 563 } 564 565 pos = dst; 566 for (arg = args; *arg; arg++) { 567 ssize_t arg_len; 568 569 /* Convert argument to wide char. */ 570 arg_len = uv_wtf8_length_as_utf16(*arg); 571 assert(arg_len > 0); 572 assert(temp_buffer_len >= (size_t) arg_len); 573 uv_wtf8_to_utf16(*arg, temp_buffer, arg_len); 574 575 if (verbatim_arguments) { 576 /* Copy verbatim. */ 577 wcscpy(pos, temp_buffer); 578 pos += arg_len - 1; 579 } else { 580 /* Quote/escape, if needed. */ 581 pos = quote_cmd_arg(temp_buffer, pos); 582 } 583 584 *pos++ = *(arg + 1) ? L' ' : L'\0'; 585 assert(pos <= dst + dst_len); 586 } 587 588 uv__free(temp_buffer); 589 590 *dst_ptr = dst; 591 return 0; 592 593 error: 594 uv__free(dst); 595 uv__free(temp_buffer); 596 return err; 597 } 598 599 600 static int env_strncmp(const wchar_t* a, int na, const wchar_t* b) { 601 wchar_t* a_eq; 602 wchar_t* b_eq; 603 int nb; 604 int r; 605 606 if (na < 0) { 607 a_eq = wcschr(a, L'='); 608 assert(a_eq); 609 na = (int)(long)(a_eq - a); 610 } else { 611 na--; 612 } 613 b_eq = wcschr(b, L'='); 614 assert(b_eq); 615 nb = b_eq - b; 616 617 r = CompareStringOrdinal(a, na, b, nb, /*case insensitive*/TRUE); 618 return r - CSTR_EQUAL; 619 } 620 621 622 static int qsort_wcscmp(const void *a, const void *b) { 623 wchar_t* astr = *(wchar_t* const*)a; 624 wchar_t* bstr = *(wchar_t* const*)b; 625 return env_strncmp(astr, -1, bstr); 626 } 627 628 629 /* 630 * The way windows takes environment variables is different than what C does; 631 * Windows wants a contiguous block of null-terminated strings, terminated 632 * with an additional null. 633 * 634 * Windows has a few "essential" environment variables. winsock will fail 635 * to initialize if SYSTEMROOT is not defined; some APIs make reference to 636 * TEMP. SYSTEMDRIVE is probably also important. We therefore ensure that 637 * these get defined if the input environment block does not contain any 638 * values for them. 639 * 640 * Also add variables known to Cygwin to be required for correct 641 * subprocess operation in many cases: 642 * https://github.com/Alexpux/Cygwin/blob/b266b04fbbd3a595f02ea149e4306d3ab9b1fe3d/winsup/cygwin/environ.cc#L955 643 * 644 */ 645 int make_program_env(char* env_block[], WCHAR** dst_ptr) { 646 WCHAR* dst; 647 WCHAR* ptr; 648 char** env; 649 size_t env_len = 0; 650 size_t len; 651 size_t i; 652 size_t var_size; 653 size_t env_block_count = 1; /* 1 for null-terminator */ 654 WCHAR* dst_copy; 655 WCHAR** ptr_copy; 656 WCHAR** env_copy; 657 char* p; 658 size_t required_vars_value_len[ARRAY_SIZE(required_vars)]; 659 660 /* first pass: determine size in UTF-16 */ 661 for (env = env_block; *env; env++) { 662 ssize_t len; 663 if (strchr(*env, '=')) { 664 len = uv_wtf8_length_as_utf16(*env); 665 if (len < 0) 666 return len; 667 env_len += len; 668 env_block_count++; 669 } 670 } 671 672 /* second pass: copy to UTF-16 environment block */ 673 len = env_block_count * sizeof(WCHAR*); 674 p = uv__malloc(len + env_len * sizeof(WCHAR)); 675 if (p == NULL) { 676 return UV_ENOMEM; 677 } 678 env_copy = (void*) &p[0]; 679 dst_copy = (void*) &p[len]; 680 681 ptr = dst_copy; 682 ptr_copy = env_copy; 683 for (env = env_block; *env; env++) { 684 ssize_t len; 685 if (strchr(*env, '=')) { 686 len = uv_wtf8_length_as_utf16(*env); 687 assert(len > 0); 688 assert((size_t) len <= env_len - (ptr - dst_copy)); 689 uv_wtf8_to_utf16(*env, ptr, len); 690 *ptr_copy++ = ptr; 691 ptr += len; 692 } 693 } 694 *ptr_copy = NULL; 695 assert(env_len == 0 || env_len == (size_t) (ptr - dst_copy)); 696 697 /* sort our (UTF-16) copy */ 698 qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp); 699 700 /* third pass: check for required variables */ 701 for (ptr_copy = env_copy, i = 0; i < ARRAY_SIZE(required_vars); ) { 702 int cmp; 703 if (!*ptr_copy) { 704 cmp = -1; 705 } else { 706 cmp = env_strncmp(required_vars[i].wide_eq, 707 required_vars[i].len, 708 *ptr_copy); 709 } 710 if (cmp < 0) { 711 /* missing required var */ 712 var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0); 713 required_vars_value_len[i] = var_size; 714 if (var_size != 0) { 715 env_len += required_vars[i].len; 716 env_len += var_size; 717 } 718 i++; 719 } else { 720 ptr_copy++; 721 if (cmp == 0) 722 i++; 723 } 724 } 725 726 /* final pass: copy, in sort order, and inserting required variables */ 727 dst = uv__malloc((1+env_len) * sizeof(WCHAR)); 728 if (!dst) { 729 uv__free(p); 730 return UV_ENOMEM; 731 } 732 733 for (ptr = dst, ptr_copy = env_copy, i = 0; 734 *ptr_copy || i < ARRAY_SIZE(required_vars); 735 ptr += len) { 736 int cmp; 737 if (i >= ARRAY_SIZE(required_vars)) { 738 cmp = 1; 739 } else if (!*ptr_copy) { 740 cmp = -1; 741 } else { 742 cmp = env_strncmp(required_vars[i].wide_eq, 743 required_vars[i].len, 744 *ptr_copy); 745 } 746 if (cmp < 0) { 747 /* missing required var */ 748 len = required_vars_value_len[i]; 749 if (len) { 750 wcscpy(ptr, required_vars[i].wide_eq); 751 ptr += required_vars[i].len; 752 var_size = GetEnvironmentVariableW(required_vars[i].wide, 753 ptr, 754 (int) (env_len - (ptr - dst))); 755 if (var_size != (DWORD) (len - 1)) { /* TODO: handle race condition? */ 756 uv_fatal_error(GetLastError(), "GetEnvironmentVariableW"); 757 } 758 } 759 i++; 760 } else { 761 /* copy var from env_block */ 762 len = wcslen(*ptr_copy) + 1; 763 wmemcpy(ptr, *ptr_copy, len); 764 ptr_copy++; 765 if (cmp == 0) 766 i++; 767 } 768 } 769 770 /* Terminate with an extra NULL. */ 771 assert(env_len == (size_t) (ptr - dst)); 772 *ptr = L'\0'; 773 774 uv__free(p); 775 *dst_ptr = dst; 776 return 0; 777 } 778 779 /* 780 * Attempt to find the value of the PATH environment variable in the child's 781 * preprocessed environment. 782 * 783 * If found, a pointer into `env` is returned. If not found, NULL is returned. 784 */ 785 static WCHAR* find_path(WCHAR *env) { 786 for (; env != NULL && *env != 0; env += wcslen(env) + 1) { 787 if ((env[0] == L'P' || env[0] == L'p') && 788 (env[1] == L'A' || env[1] == L'a') && 789 (env[2] == L'T' || env[2] == L't') && 790 (env[3] == L'H' || env[3] == L'h') && 791 (env[4] == L'=')) { 792 return &env[5]; 793 } 794 } 795 796 return NULL; 797 } 798 799 /* 800 * Called on Windows thread-pool thread to indicate that 801 * a child process has exited. 802 */ 803 static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { 804 uv_process_t* process = (uv_process_t*) data; 805 uv_loop_t* loop = process->loop; 806 807 assert(didTimeout == FALSE); 808 assert(process); 809 assert(!process->exit_cb_pending); 810 811 process->exit_cb_pending = 1; 812 813 /* Post completed */ 814 POST_COMPLETION_FOR_REQ(loop, &process->exit_req); 815 } 816 817 818 /* Called on main thread after a child process has exited. */ 819 void uv__process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { 820 int64_t exit_code; 821 DWORD status; 822 823 assert(handle->exit_cb_pending); 824 handle->exit_cb_pending = 0; 825 826 /* If we're closing, don't call the exit callback. Just schedule a close 827 * callback now. */ 828 if (handle->flags & UV_HANDLE_CLOSING) { 829 uv__want_endgame(loop, (uv_handle_t*) handle); 830 return; 831 } 832 833 /* Unregister from process notification. */ 834 if (handle->wait_handle != INVALID_HANDLE_VALUE) { 835 UnregisterWait(handle->wait_handle); 836 handle->wait_handle = INVALID_HANDLE_VALUE; 837 } 838 839 /* Set the handle to inactive: no callbacks will be made after the exit 840 * callback. */ 841 uv__handle_stop(handle); 842 843 if (GetExitCodeProcess(handle->process_handle, &status)) { 844 exit_code = status; 845 } else { 846 /* Unable to obtain the exit code. This should never happen. */ 847 exit_code = uv_translate_sys_error(GetLastError()); 848 } 849 850 /* Fire the exit callback. */ 851 if (handle->exit_cb) { 852 handle->exit_cb(handle, exit_code, handle->exit_signal); 853 } 854 } 855 856 857 void uv__process_close(uv_loop_t* loop, uv_process_t* handle) { 858 uv__handle_closing(handle); 859 860 if (handle->wait_handle != INVALID_HANDLE_VALUE) { 861 /* This blocks until either the wait was cancelled, or the callback has 862 * completed. */ 863 BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE); 864 if (!r) { 865 /* This should never happen, and if it happens, we can't recover... */ 866 uv_fatal_error(GetLastError(), "UnregisterWaitEx"); 867 } 868 869 handle->wait_handle = INVALID_HANDLE_VALUE; 870 } 871 872 if (!handle->exit_cb_pending) { 873 uv__want_endgame(loop, (uv_handle_t*)handle); 874 } 875 } 876 877 878 void uv__process_endgame(uv_loop_t* loop, uv_process_t* handle) { 879 assert(!handle->exit_cb_pending); 880 assert(handle->flags & UV_HANDLE_CLOSING); 881 assert(!(handle->flags & UV_HANDLE_CLOSED)); 882 883 /* Clean-up the process handle. */ 884 CloseHandle(handle->process_handle); 885 886 uv__handle_close(handle); 887 } 888 889 890 int uv_spawn(uv_loop_t* loop, 891 uv_process_t* process, 892 const uv_process_options_t* options) { 893 int i; 894 int err = 0; 895 WCHAR* path = NULL, *alloc_path = NULL; 896 BOOL result; 897 WCHAR* application_path = NULL, *application = NULL, *arguments = NULL, 898 *env = NULL, *cwd = NULL; 899 STARTUPINFOW startup; 900 PROCESS_INFORMATION info; 901 DWORD process_flags, cwd_len; 902 BYTE* child_stdio_buffer; 903 904 uv__process_init(loop, process); 905 process->exit_cb = options->exit_cb; 906 child_stdio_buffer = NULL; 907 908 if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { 909 return UV_ENOTSUP; 910 } 911 912 if (options->file == NULL || 913 options->args == NULL) { 914 return UV_EINVAL; 915 } 916 917 assert(options->file != NULL); 918 assert(!(options->flags & ~(UV_PROCESS_DETACHED | 919 UV_PROCESS_SETGID | 920 UV_PROCESS_SETUID | 921 UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME | 922 UV_PROCESS_WINDOWS_HIDE | 923 UV_PROCESS_WINDOWS_HIDE_CONSOLE | 924 UV_PROCESS_WINDOWS_HIDE_GUI | 925 UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); 926 927 err = uv__utf8_to_utf16_alloc(options->file, &application); 928 if (err) 929 goto done_uv; 930 931 err = make_program_args( 932 options->args, 933 options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, 934 &arguments); 935 if (err) 936 goto done_uv; 937 938 if (options->env) { 939 err = make_program_env(options->env, &env); 940 if (err) 941 goto done_uv; 942 } 943 944 if (options->cwd) { 945 /* Explicit cwd */ 946 err = uv__utf8_to_utf16_alloc(options->cwd, &cwd); 947 if (err) 948 goto done_uv; 949 950 cwd_len = wcslen(cwd); 951 } else { 952 /* Inherit cwd */ 953 DWORD r; 954 955 cwd_len = GetCurrentDirectoryW(0, NULL); 956 if (!cwd_len) { 957 err = GetLastError(); 958 goto done; 959 } 960 961 cwd = (WCHAR*) uv__malloc(cwd_len * sizeof(WCHAR)); 962 if (cwd == NULL) { 963 err = ERROR_OUTOFMEMORY; 964 goto done; 965 } 966 967 r = GetCurrentDirectoryW(cwd_len, cwd); 968 if (r == 0 || r >= cwd_len) { 969 err = GetLastError(); 970 goto done; 971 } 972 } 973 974 /* If cwd is too long, shorten it */ 975 if (cwd_len >= MAX_PATH) { 976 cwd_len = GetShortPathNameW(cwd, cwd, cwd_len); 977 if (cwd_len == 0) { 978 err = GetLastError(); 979 goto done; 980 } 981 } 982 983 /* Get PATH environment variable. */ 984 path = find_path(env); 985 if (path == NULL) { 986 DWORD path_len, r; 987 988 path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); 989 if (path_len != 0) { 990 alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR)); 991 if (alloc_path == NULL) { 992 err = ERROR_OUTOFMEMORY; 993 goto done; 994 } 995 path = alloc_path; 996 997 r = GetEnvironmentVariableW(L"PATH", path, path_len); 998 if (r == 0 || r >= path_len) { 999 err = GetLastError(); 1000 goto done; 1001 } 1002 } 1003 } 1004 1005 err = uv__stdio_create(loop, options, &child_stdio_buffer); 1006 if (err) 1007 goto done; 1008 1009 application_path = search_path(application, 1010 cwd, 1011 path, 1012 options->flags); 1013 if (application_path == NULL) { 1014 /* Not found. */ 1015 err = ERROR_FILE_NOT_FOUND; 1016 goto done; 1017 } 1018 1019 startup.cb = sizeof(startup); 1020 startup.lpReserved = NULL; 1021 startup.lpDesktop = NULL; 1022 startup.lpTitle = NULL; 1023 startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; 1024 1025 startup.cbReserved2 = uv__stdio_size(child_stdio_buffer); 1026 startup.lpReserved2 = (BYTE*) child_stdio_buffer; 1027 1028 startup.hStdInput = uv__stdio_handle(child_stdio_buffer, 0); 1029 startup.hStdOutput = uv__stdio_handle(child_stdio_buffer, 1); 1030 startup.hStdError = uv__stdio_handle(child_stdio_buffer, 2); 1031 1032 process_flags = CREATE_UNICODE_ENVIRONMENT; 1033 1034 if ((options->flags & UV_PROCESS_WINDOWS_HIDE_CONSOLE) || 1035 (options->flags & UV_PROCESS_WINDOWS_HIDE)) { 1036 /* Avoid creating console window if stdio is not inherited. */ 1037 for (i = 0; i < options->stdio_count; i++) { 1038 if (options->stdio[i].flags & UV_INHERIT_FD) 1039 break; 1040 if (i == options->stdio_count - 1) 1041 process_flags |= CREATE_NO_WINDOW; 1042 } 1043 } 1044 if ((options->flags & UV_PROCESS_WINDOWS_HIDE_GUI) || 1045 (options->flags & UV_PROCESS_WINDOWS_HIDE)) { 1046 /* Use SW_HIDE to avoid any potential process window. */ 1047 startup.wShowWindow = SW_HIDE; 1048 } else { 1049 startup.wShowWindow = SW_SHOWDEFAULT; 1050 } 1051 1052 if (options->flags & UV_PROCESS_DETACHED) { 1053 /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That 1054 * means that libuv might not let you create a fully daemonized process 1055 * when run under job control. However the type of job control that libuv 1056 * itself creates doesn't trickle down to subprocesses so they can still 1057 * daemonize. 1058 * 1059 * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the 1060 * CreateProcess call fail if we're under job control that doesn't allow 1061 * breakaway. 1062 */ 1063 process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; 1064 process_flags |= CREATE_SUSPENDED; 1065 } 1066 1067 if (!CreateProcessW(application_path, 1068 arguments, 1069 NULL, 1070 NULL, 1071 1, 1072 process_flags, 1073 env, 1074 cwd, 1075 &startup, 1076 &info)) { 1077 /* CreateProcessW failed. */ 1078 err = GetLastError(); 1079 goto done; 1080 } 1081 1082 /* If the process isn't spawned as detached, assign to the global job object 1083 * so windows will kill it when the parent process dies. */ 1084 if (!(options->flags & UV_PROCESS_DETACHED)) { 1085 uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle); 1086 1087 if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) { 1088 /* AssignProcessToJobObject might fail if this process is under job 1089 * control and the job doesn't have the 1090 * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version 1091 * that doesn't support nested jobs. 1092 * 1093 * When that happens we just swallow the error and continue without 1094 * establishing a kill-child-on-parent-exit relationship, otherwise 1095 * there would be no way for libuv applications run under job control 1096 * to spawn processes at all. 1097 */ 1098 DWORD err = GetLastError(); 1099 if (err != ERROR_ACCESS_DENIED) 1100 uv_fatal_error(err, "AssignProcessToJobObject"); 1101 } 1102 } 1103 1104 if (process_flags & CREATE_SUSPENDED) { 1105 if (ResumeThread(info.hThread) == ((DWORD)-1)) { 1106 err = GetLastError(); 1107 TerminateProcess(info.hProcess, 1); 1108 goto done; 1109 } 1110 } 1111 1112 /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */ 1113 1114 process->process_handle = info.hProcess; 1115 process->pid = info.dwProcessId; 1116 1117 /* Set IPC pid to all IPC pipes. */ 1118 for (i = 0; i < options->stdio_count; i++) { 1119 const uv_stdio_container_t* fdopt = &options->stdio[i]; 1120 if (fdopt->flags & UV_CREATE_PIPE && 1121 fdopt->data.stream->type == UV_NAMED_PIPE && 1122 ((uv_pipe_t*) fdopt->data.stream)->ipc) { 1123 ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_remote_pid = 1124 info.dwProcessId; 1125 } 1126 } 1127 1128 /* Setup notifications for when the child process exits. */ 1129 result = RegisterWaitForSingleObject(&process->wait_handle, 1130 process->process_handle, exit_wait_callback, (void*)process, INFINITE, 1131 WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); 1132 if (!result) { 1133 uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); 1134 } 1135 1136 CloseHandle(info.hThread); 1137 1138 assert(!err); 1139 1140 /* Make the handle active. It will remain active until the exit callback is 1141 * made or the handle is closed, whichever happens first. */ 1142 uv__handle_start(process); 1143 1144 goto done_uv; 1145 1146 /* Cleanup, whether we succeeded or failed. */ 1147 done: 1148 err = uv_translate_sys_error(err); 1149 1150 done_uv: 1151 uv__free(application); 1152 uv__free(application_path); 1153 uv__free(arguments); 1154 uv__free(cwd); 1155 uv__free(env); 1156 uv__free(alloc_path); 1157 1158 if (child_stdio_buffer != NULL) { 1159 /* Clean up child stdio handles. */ 1160 uv__stdio_destroy(child_stdio_buffer); 1161 child_stdio_buffer = NULL; 1162 } 1163 1164 return err; 1165 } 1166 1167 1168 static int uv__kill(HANDLE process_handle, int signum) { 1169 if (signum < 0 || signum >= NSIG) { 1170 return UV_EINVAL; 1171 } 1172 1173 /* Create a dump file for the targeted process, if the registry key 1174 * `HKLM:Software\Microsoft\Windows\Windows Error Reporting\LocalDumps` 1175 * exists. The location of the dumps can be influenced by the `DumpFolder` 1176 * sub-key, which has a default value of `%LOCALAPPDATA%\CrashDumps`, see [0] 1177 * for more detail. Note that if the dump folder does not exist, we attempt 1178 * to create it, to match behavior with WER itself. 1179 * [0]: https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps */ 1180 if (signum == SIGQUIT) { 1181 HKEY registry_key; 1182 DWORD pid, ret; 1183 WCHAR basename[MAX_PATH]; 1184 1185 /* Get target process name. */ 1186 GetModuleBaseNameW(process_handle, NULL, &basename[0], sizeof(basename)); 1187 1188 /* Get PID of target process. */ 1189 pid = GetProcessId(process_handle); 1190 1191 /* Get LocalDumps directory path. */ 1192 ret = RegOpenKeyExW( 1193 HKEY_LOCAL_MACHINE, 1194 L"SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps", 1195 0, 1196 KEY_QUERY_VALUE, 1197 ®istry_key); 1198 if (ret == ERROR_SUCCESS) { 1199 HANDLE hDumpFile = NULL; 1200 WCHAR dump_folder[MAX_PATH], dump_name[MAX_PATH]; 1201 DWORD dump_folder_len = sizeof(dump_folder), key_type = 0; 1202 ret = RegGetValueW(registry_key, 1203 NULL, 1204 L"DumpFolder", 1205 RRF_RT_ANY, 1206 &key_type, 1207 (PVOID) dump_folder, 1208 &dump_folder_len); 1209 if (ret != ERROR_SUCCESS) { 1210 /* Workaround for missing uuid.dll on MinGW. */ 1211 static const GUID FOLDERID_LocalAppData_libuv = { 1212 0xf1b32785, 0x6fba, 0x4fcf, 1213 {0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91} 1214 }; 1215 1216 /* Default value for `dump_folder` is `%LOCALAPPDATA%\CrashDumps`. */ 1217 WCHAR* localappdata; 1218 SHGetKnownFolderPath(&FOLDERID_LocalAppData_libuv, 1219 0, 1220 NULL, 1221 &localappdata); 1222 _snwprintf_s(dump_folder, 1223 sizeof(dump_folder), 1224 _TRUNCATE, 1225 L"%ls\\CrashDumps", 1226 localappdata); 1227 CoTaskMemFree(localappdata); 1228 } 1229 RegCloseKey(registry_key); 1230 1231 /* Create dump folder if it doesn't already exist. */ 1232 CreateDirectoryW(dump_folder, NULL); 1233 1234 /* Construct dump filename from process name and PID. */ 1235 _snwprintf_s(dump_name, 1236 sizeof(dump_name), 1237 _TRUNCATE, 1238 L"%ls\\%ls.%d.dmp", 1239 dump_folder, 1240 basename, 1241 pid); 1242 1243 hDumpFile = CreateFileW(dump_name, 1244 GENERIC_WRITE, 1245 0, 1246 NULL, 1247 CREATE_NEW, 1248 FILE_ATTRIBUTE_NORMAL, 1249 NULL); 1250 if (hDumpFile != INVALID_HANDLE_VALUE) { 1251 DWORD dump_options, sym_options; 1252 FILE_DISPOSITION_INFO DeleteOnClose = { TRUE }; 1253 1254 /* If something goes wrong while writing it out, delete the file. */ 1255 SetFileInformationByHandle(hDumpFile, 1256 FileDispositionInfo, 1257 &DeleteOnClose, 1258 sizeof(DeleteOnClose)); 1259 1260 /* Tell wine to dump ELF modules as well. */ 1261 sym_options = SymGetOptions(); 1262 SymSetOptions(sym_options | 0x40000000); 1263 1264 /* MiniDumpWithAvxXStateContext might be undef in server2012r2 or mingw < 12 */ 1265 #ifndef MiniDumpWithAvxXStateContext 1266 #define MiniDumpWithAvxXStateContext 0x00200000 1267 #endif 1268 /* We default to a fairly complete dump. In the future, we may want to 1269 * allow clients to customize what kind of dump to create. */ 1270 dump_options = MiniDumpWithFullMemory | 1271 MiniDumpIgnoreInaccessibleMemory | 1272 MiniDumpWithAvxXStateContext; 1273 1274 if (MiniDumpWriteDump(process_handle, 1275 pid, 1276 hDumpFile, 1277 dump_options, 1278 NULL, 1279 NULL, 1280 NULL)) { 1281 /* Don't delete the file on close if we successfully wrote it out. */ 1282 FILE_DISPOSITION_INFO DontDeleteOnClose = { FALSE }; 1283 SetFileInformationByHandle(hDumpFile, 1284 FileDispositionInfo, 1285 &DontDeleteOnClose, 1286 sizeof(DontDeleteOnClose)); 1287 } 1288 SymSetOptions(sym_options); 1289 CloseHandle(hDumpFile); 1290 } 1291 } 1292 } 1293 1294 switch (signum) { 1295 case SIGQUIT: 1296 case SIGTERM: 1297 case SIGKILL: 1298 case SIGINT: { 1299 /* Unconditionally terminate the process. On Windows, killed processes 1300 * normally return 1. */ 1301 int err; 1302 DWORD status; 1303 1304 if (TerminateProcess(process_handle, 1)) 1305 return 0; 1306 1307 /* If the process already exited before TerminateProcess was called, 1308 * TerminateProcess will fail with ERROR_ACCESS_DENIED. */ 1309 err = GetLastError(); 1310 if (err == ERROR_ACCESS_DENIED) { 1311 /* First check using GetExitCodeProcess() with status different from 1312 * STILL_ACTIVE (259). This check can be set incorrectly by the process, 1313 * though that is uncommon. */ 1314 if (GetExitCodeProcess(process_handle, &status) && 1315 status != STILL_ACTIVE) { 1316 return UV_ESRCH; 1317 } 1318 1319 /* But the process could have exited with code == STILL_ACTIVE, use then 1320 * WaitForSingleObject with timeout zero. This is prone to a race 1321 * condition as it could return WAIT_TIMEOUT because the handle might 1322 * not have been signaled yet.That would result in returning the wrong 1323 * error code here (UV_EACCES instead of UV_ESRCH), but we cannot fix 1324 * the kernel synchronization issue that TerminateProcess is 1325 * inconsistent with WaitForSingleObject with just the APIs available to 1326 * us in user space. */ 1327 if (WaitForSingleObject(process_handle, 0) == WAIT_OBJECT_0) { 1328 return UV_ESRCH; 1329 } 1330 } 1331 1332 return uv_translate_sys_error(err); 1333 } 1334 1335 case 0: { 1336 /* Health check: is the process still alive? */ 1337 DWORD status; 1338 1339 if (!GetExitCodeProcess(process_handle, &status)) 1340 return uv_translate_sys_error(GetLastError()); 1341 1342 if (status != STILL_ACTIVE) 1343 return UV_ESRCH; 1344 1345 switch (WaitForSingleObject(process_handle, 0)) { 1346 case WAIT_OBJECT_0: 1347 return UV_ESRCH; 1348 case WAIT_FAILED: 1349 return uv_translate_sys_error(GetLastError()); 1350 case WAIT_TIMEOUT: 1351 return 0; 1352 default: 1353 return UV_UNKNOWN; 1354 } 1355 } 1356 1357 default: 1358 /* Unsupported signal. */ 1359 return UV_ENOSYS; 1360 } 1361 } 1362 1363 1364 int uv_process_kill(uv_process_t* process, int signum) { 1365 int err; 1366 1367 if (process->process_handle == INVALID_HANDLE_VALUE) { 1368 return UV_EINVAL; 1369 } 1370 1371 err = uv__kill(process->process_handle, signum); 1372 if (err) { 1373 return err; /* err is already translated. */ 1374 } 1375 1376 process->exit_signal = signum; 1377 1378 return 0; 1379 } 1380 1381 1382 int uv_kill(int pid, int signum) { 1383 int err; 1384 HANDLE process_handle; 1385 1386 if (pid == 0) { 1387 process_handle = GetCurrentProcess(); 1388 } else { 1389 process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE, 1390 FALSE, 1391 pid); 1392 } 1393 1394 if (process_handle == NULL) { 1395 err = GetLastError(); 1396 if (err == ERROR_INVALID_PARAMETER) { 1397 return UV_ESRCH; 1398 } else { 1399 return uv_translate_sys_error(err); 1400 } 1401 } 1402 1403 err = uv__kill(process_handle, signum); 1404 CloseHandle(process_handle); 1405 1406 return err; /* err is already translated. */ 1407 } 1408