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
uv__init_global_job_handle(void)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
uv__utf8_to_utf16_alloc(const char * s,WCHAR ** ws_ptr)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
uv__process_init(uv_loop_t * loop,uv_process_t * handle)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 */
search_path_join_test(const WCHAR * dir,size_t dir_len,const WCHAR * name,size_t name_len,const WCHAR * ext,size_t ext_len,const WCHAR * cwd,size_t cwd_len)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 */
path_search_walk_ext(const WCHAR * dir,size_t dir_len,const WCHAR * name,size_t name_len,WCHAR * cwd,size_t cwd_len,int name_has_ext)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 */
search_path(const WCHAR * file,WCHAR * cwd,const WCHAR * path,unsigned int flags)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 */
quote_cmd_arg(const WCHAR * source,WCHAR * target)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
make_program_args(char ** args,int verbatim_arguments,WCHAR ** dst_ptr)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
env_strncmp(const wchar_t * a,int na,const wchar_t * b)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
qsort_wcscmp(const void * a,const void * b)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 */
make_program_env(char * env_block[],WCHAR ** dst_ptr)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 */
find_path(WCHAR * env)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 */
exit_wait_callback(void * data,BOOLEAN didTimeout)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. */
uv__process_proc_exit(uv_loop_t * loop,uv_process_t * handle)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
uv__process_close(uv_loop_t * loop,uv_process_t * handle)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
uv__process_endgame(uv_loop_t * loop,uv_process_t * handle)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
uv_spawn(uv_loop_t * loop,uv_process_t * process,const uv_process_options_t * options)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;
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 } else {
951 /* Inherit cwd */
952 DWORD cwd_len, r;
953
954 cwd_len = GetCurrentDirectoryW(0, NULL);
955 if (!cwd_len) {
956 err = GetLastError();
957 goto done;
958 }
959
960 cwd = (WCHAR*) uv__malloc(cwd_len * sizeof(WCHAR));
961 if (cwd == NULL) {
962 err = ERROR_OUTOFMEMORY;
963 goto done;
964 }
965
966 r = GetCurrentDirectoryW(cwd_len, cwd);
967 if (r == 0 || r >= cwd_len) {
968 err = GetLastError();
969 goto done;
970 }
971 }
972
973 /* Get PATH environment variable. */
974 path = find_path(env);
975 if (path == NULL) {
976 DWORD path_len, r;
977
978 path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
979 if (path_len != 0) {
980 alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR));
981 if (alloc_path == NULL) {
982 err = ERROR_OUTOFMEMORY;
983 goto done;
984 }
985 path = alloc_path;
986
987 r = GetEnvironmentVariableW(L"PATH", path, path_len);
988 if (r == 0 || r >= path_len) {
989 err = GetLastError();
990 goto done;
991 }
992 }
993 }
994
995 err = uv__stdio_create(loop, options, &child_stdio_buffer);
996 if (err)
997 goto done;
998
999 application_path = search_path(application,
1000 cwd,
1001 path,
1002 options->flags);
1003 if (application_path == NULL) {
1004 /* Not found. */
1005 err = ERROR_FILE_NOT_FOUND;
1006 goto done;
1007 }
1008
1009 startup.cb = sizeof(startup);
1010 startup.lpReserved = NULL;
1011 startup.lpDesktop = NULL;
1012 startup.lpTitle = NULL;
1013 startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1014
1015 startup.cbReserved2 = uv__stdio_size(child_stdio_buffer);
1016 startup.lpReserved2 = (BYTE*) child_stdio_buffer;
1017
1018 startup.hStdInput = uv__stdio_handle(child_stdio_buffer, 0);
1019 startup.hStdOutput = uv__stdio_handle(child_stdio_buffer, 1);
1020 startup.hStdError = uv__stdio_handle(child_stdio_buffer, 2);
1021
1022 process_flags = CREATE_UNICODE_ENVIRONMENT;
1023
1024 if ((options->flags & UV_PROCESS_WINDOWS_HIDE_CONSOLE) ||
1025 (options->flags & UV_PROCESS_WINDOWS_HIDE)) {
1026 /* Avoid creating console window if stdio is not inherited. */
1027 for (i = 0; i < options->stdio_count; i++) {
1028 if (options->stdio[i].flags & UV_INHERIT_FD)
1029 break;
1030 if (i == options->stdio_count - 1)
1031 process_flags |= CREATE_NO_WINDOW;
1032 }
1033 }
1034 if ((options->flags & UV_PROCESS_WINDOWS_HIDE_GUI) ||
1035 (options->flags & UV_PROCESS_WINDOWS_HIDE)) {
1036 /* Use SW_HIDE to avoid any potential process window. */
1037 startup.wShowWindow = SW_HIDE;
1038 } else {
1039 startup.wShowWindow = SW_SHOWDEFAULT;
1040 }
1041
1042 if (options->flags & UV_PROCESS_DETACHED) {
1043 /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That
1044 * means that libuv might not let you create a fully daemonized process
1045 * when run under job control. However the type of job control that libuv
1046 * itself creates doesn't trickle down to subprocesses so they can still
1047 * daemonize.
1048 *
1049 * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the
1050 * CreateProcess call fail if we're under job control that doesn't allow
1051 * breakaway.
1052 */
1053 process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
1054 process_flags |= CREATE_SUSPENDED;
1055 }
1056
1057 if (!CreateProcessW(application_path,
1058 arguments,
1059 NULL,
1060 NULL,
1061 1,
1062 process_flags,
1063 env,
1064 cwd,
1065 &startup,
1066 &info)) {
1067 /* CreateProcessW failed. */
1068 err = GetLastError();
1069 goto done;
1070 }
1071
1072 /* If the process isn't spawned as detached, assign to the global job object
1073 * so windows will kill it when the parent process dies. */
1074 if (!(options->flags & UV_PROCESS_DETACHED)) {
1075 uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle);
1076
1077 if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) {
1078 /* AssignProcessToJobObject might fail if this process is under job
1079 * control and the job doesn't have the
1080 * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version
1081 * that doesn't support nested jobs.
1082 *
1083 * When that happens we just swallow the error and continue without
1084 * establishing a kill-child-on-parent-exit relationship, otherwise
1085 * there would be no way for libuv applications run under job control
1086 * to spawn processes at all.
1087 */
1088 DWORD err = GetLastError();
1089 if (err != ERROR_ACCESS_DENIED)
1090 uv_fatal_error(err, "AssignProcessToJobObject");
1091 }
1092 }
1093
1094 if (process_flags & CREATE_SUSPENDED) {
1095 if (ResumeThread(info.hThread) == ((DWORD)-1)) {
1096 err = GetLastError();
1097 TerminateProcess(info.hProcess, 1);
1098 goto done;
1099 }
1100 }
1101
1102 /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */
1103
1104 process->process_handle = info.hProcess;
1105 process->pid = info.dwProcessId;
1106
1107 /* Set IPC pid to all IPC pipes. */
1108 for (i = 0; i < options->stdio_count; i++) {
1109 const uv_stdio_container_t* fdopt = &options->stdio[i];
1110 if (fdopt->flags & UV_CREATE_PIPE &&
1111 fdopt->data.stream->type == UV_NAMED_PIPE &&
1112 ((uv_pipe_t*) fdopt->data.stream)->ipc) {
1113 ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_remote_pid =
1114 info.dwProcessId;
1115 }
1116 }
1117
1118 /* Setup notifications for when the child process exits. */
1119 result = RegisterWaitForSingleObject(&process->wait_handle,
1120 process->process_handle, exit_wait_callback, (void*)process, INFINITE,
1121 WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
1122 if (!result) {
1123 uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject");
1124 }
1125
1126 CloseHandle(info.hThread);
1127
1128 assert(!err);
1129
1130 /* Make the handle active. It will remain active until the exit callback is
1131 * made or the handle is closed, whichever happens first. */
1132 uv__handle_start(process);
1133
1134 goto done_uv;
1135
1136 /* Cleanup, whether we succeeded or failed. */
1137 done:
1138 err = uv_translate_sys_error(err);
1139
1140 done_uv:
1141 uv__free(application);
1142 uv__free(application_path);
1143 uv__free(arguments);
1144 uv__free(cwd);
1145 uv__free(env);
1146 uv__free(alloc_path);
1147
1148 if (child_stdio_buffer != NULL) {
1149 /* Clean up child stdio handles. */
1150 uv__stdio_destroy(child_stdio_buffer);
1151 child_stdio_buffer = NULL;
1152 }
1153
1154 return err;
1155 }
1156
1157
uv__kill(HANDLE process_handle,int signum)1158 static int uv__kill(HANDLE process_handle, int signum) {
1159 if (signum < 0 || signum >= NSIG) {
1160 return UV_EINVAL;
1161 }
1162
1163 /* Create a dump file for the targeted process, if the registry key
1164 * `HKLM:Software\Microsoft\Windows\Windows Error Reporting\LocalDumps`
1165 * exists. The location of the dumps can be influenced by the `DumpFolder`
1166 * sub-key, which has a default value of `%LOCALAPPDATA%\CrashDumps`, see [0]
1167 * for more detail. Note that if the dump folder does not exist, we attempt
1168 * to create it, to match behavior with WER itself.
1169 * [0]: https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps */
1170 if (signum == SIGQUIT) {
1171 HKEY registry_key;
1172 DWORD pid, ret;
1173 WCHAR basename[MAX_PATH];
1174
1175 /* Get target process name. */
1176 GetModuleBaseNameW(process_handle, NULL, &basename[0], sizeof(basename));
1177
1178 /* Get PID of target process. */
1179 pid = GetProcessId(process_handle);
1180
1181 /* Get LocalDumps directory path. */
1182 ret = RegOpenKeyExW(
1183 HKEY_LOCAL_MACHINE,
1184 L"SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps",
1185 0,
1186 KEY_QUERY_VALUE,
1187 ®istry_key);
1188 if (ret == ERROR_SUCCESS) {
1189 HANDLE hDumpFile = NULL;
1190 WCHAR dump_folder[MAX_PATH], dump_name[MAX_PATH];
1191 DWORD dump_folder_len = sizeof(dump_folder), key_type = 0;
1192 ret = RegGetValueW(registry_key,
1193 NULL,
1194 L"DumpFolder",
1195 RRF_RT_ANY,
1196 &key_type,
1197 (PVOID) dump_folder,
1198 &dump_folder_len);
1199 if (ret != ERROR_SUCCESS) {
1200 /* Workaround for missing uuid.dll on MinGW. */
1201 static const GUID FOLDERID_LocalAppData_libuv = {
1202 0xf1b32785, 0x6fba, 0x4fcf,
1203 {0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91}
1204 };
1205
1206 /* Default value for `dump_folder` is `%LOCALAPPDATA%\CrashDumps`. */
1207 WCHAR* localappdata;
1208 SHGetKnownFolderPath(&FOLDERID_LocalAppData_libuv,
1209 0,
1210 NULL,
1211 &localappdata);
1212 _snwprintf_s(dump_folder,
1213 sizeof(dump_folder),
1214 _TRUNCATE,
1215 L"%ls\\CrashDumps",
1216 localappdata);
1217 CoTaskMemFree(localappdata);
1218 }
1219 RegCloseKey(registry_key);
1220
1221 /* Create dump folder if it doesn't already exist. */
1222 CreateDirectoryW(dump_folder, NULL);
1223
1224 /* Construct dump filename from process name and PID. */
1225 _snwprintf_s(dump_name,
1226 sizeof(dump_name),
1227 _TRUNCATE,
1228 L"%ls\\%ls.%d.dmp",
1229 dump_folder,
1230 basename,
1231 pid);
1232
1233 hDumpFile = CreateFileW(dump_name,
1234 GENERIC_WRITE,
1235 0,
1236 NULL,
1237 CREATE_NEW,
1238 FILE_ATTRIBUTE_NORMAL,
1239 NULL);
1240 if (hDumpFile != INVALID_HANDLE_VALUE) {
1241 DWORD dump_options, sym_options;
1242 FILE_DISPOSITION_INFO DeleteOnClose = { TRUE };
1243
1244 /* If something goes wrong while writing it out, delete the file. */
1245 SetFileInformationByHandle(hDumpFile,
1246 FileDispositionInfo,
1247 &DeleteOnClose,
1248 sizeof(DeleteOnClose));
1249
1250 /* Tell wine to dump ELF modules as well. */
1251 sym_options = SymGetOptions();
1252 SymSetOptions(sym_options | 0x40000000);
1253
1254 /* MiniDumpWithAvxXStateContext might be undef in server2012r2 or mingw < 12 */
1255 #ifndef MiniDumpWithAvxXStateContext
1256 #define MiniDumpWithAvxXStateContext 0x00200000
1257 #endif
1258 /* We default to a fairly complete dump. In the future, we may want to
1259 * allow clients to customize what kind of dump to create. */
1260 dump_options = MiniDumpWithFullMemory |
1261 MiniDumpIgnoreInaccessibleMemory |
1262 MiniDumpWithAvxXStateContext;
1263
1264 if (MiniDumpWriteDump(process_handle,
1265 pid,
1266 hDumpFile,
1267 dump_options,
1268 NULL,
1269 NULL,
1270 NULL)) {
1271 /* Don't delete the file on close if we successfully wrote it out. */
1272 FILE_DISPOSITION_INFO DontDeleteOnClose = { FALSE };
1273 SetFileInformationByHandle(hDumpFile,
1274 FileDispositionInfo,
1275 &DontDeleteOnClose,
1276 sizeof(DontDeleteOnClose));
1277 }
1278 SymSetOptions(sym_options);
1279 CloseHandle(hDumpFile);
1280 }
1281 }
1282 }
1283
1284 switch (signum) {
1285 case SIGQUIT:
1286 case SIGTERM:
1287 case SIGKILL:
1288 case SIGINT: {
1289 /* Unconditionally terminate the process. On Windows, killed processes
1290 * normally return 1. */
1291 int err;
1292 DWORD status;
1293
1294 if (TerminateProcess(process_handle, 1))
1295 return 0;
1296
1297 /* If the process already exited before TerminateProcess was called,
1298 * TerminateProcess will fail with ERROR_ACCESS_DENIED. */
1299 err = GetLastError();
1300 if (err == ERROR_ACCESS_DENIED) {
1301 /* First check using GetExitCodeProcess() with status different from
1302 * STILL_ACTIVE (259). This check can be set incorrectly by the process,
1303 * though that is uncommon. */
1304 if (GetExitCodeProcess(process_handle, &status) &&
1305 status != STILL_ACTIVE) {
1306 return UV_ESRCH;
1307 }
1308
1309 /* But the process could have exited with code == STILL_ACTIVE, use then
1310 * WaitForSingleObject with timeout zero. This is prone to a race
1311 * condition as it could return WAIT_TIMEOUT because the handle might
1312 * not have been signaled yet.That would result in returning the wrong
1313 * error code here (UV_EACCES instead of UV_ESRCH), but we cannot fix
1314 * the kernel synchronization issue that TerminateProcess is
1315 * inconsistent with WaitForSingleObject with just the APIs available to
1316 * us in user space. */
1317 if (WaitForSingleObject(process_handle, 0) == WAIT_OBJECT_0) {
1318 return UV_ESRCH;
1319 }
1320 }
1321
1322 return uv_translate_sys_error(err);
1323 }
1324
1325 case 0: {
1326 /* Health check: is the process still alive? */
1327 DWORD status;
1328
1329 if (!GetExitCodeProcess(process_handle, &status))
1330 return uv_translate_sys_error(GetLastError());
1331
1332 if (status != STILL_ACTIVE)
1333 return UV_ESRCH;
1334
1335 switch (WaitForSingleObject(process_handle, 0)) {
1336 case WAIT_OBJECT_0:
1337 return UV_ESRCH;
1338 case WAIT_FAILED:
1339 return uv_translate_sys_error(GetLastError());
1340 case WAIT_TIMEOUT:
1341 return 0;
1342 default:
1343 return UV_UNKNOWN;
1344 }
1345 }
1346
1347 default:
1348 /* Unsupported signal. */
1349 return UV_ENOSYS;
1350 }
1351 }
1352
1353
uv_process_kill(uv_process_t * process,int signum)1354 int uv_process_kill(uv_process_t* process, int signum) {
1355 int err;
1356
1357 if (process->process_handle == INVALID_HANDLE_VALUE) {
1358 return UV_EINVAL;
1359 }
1360
1361 err = uv__kill(process->process_handle, signum);
1362 if (err) {
1363 return err; /* err is already translated. */
1364 }
1365
1366 process->exit_signal = signum;
1367
1368 return 0;
1369 }
1370
1371
uv_kill(int pid,int signum)1372 int uv_kill(int pid, int signum) {
1373 int err;
1374 HANDLE process_handle;
1375
1376 if (pid == 0) {
1377 process_handle = GetCurrentProcess();
1378 } else {
1379 process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
1380 FALSE,
1381 pid);
1382 }
1383
1384 if (process_handle == NULL) {
1385 err = GetLastError();
1386 if (err == ERROR_INVALID_PARAMETER) {
1387 return UV_ESRCH;
1388 } else {
1389 return uv_translate_sys_error(err);
1390 }
1391 }
1392
1393 err = uv__kill(process_handle, signum);
1394 CloseHandle(process_handle);
1395
1396 return err; /* err is already translated. */
1397 }
1398