-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
process.c
1389 lines (1158 loc) · 39.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
22
#include <assert.h>
23
#include <io.h>
24
25
#include <stdio.h>
#include <stdlib.h>
26
#include <signal.h>
27
28
29
30
#include "uv.h"
#include "internal.h"
#include "req-inl.h"
31
32
33
34
#define SIGKILL 9
35
36
37
38
39
40
41
42
43
44
45
46
/* CRT file descriptor mode flags */
#define FOPEN 0x01
#define FEOFLAG 0x02
#define FCRLF 0x04
#define FPIPE 0x08
#define FNOINHERIT 0x10
#define FAPPEND 0x20
#define FDEV 0x40
#define FTEXT 0x80
47
48
49
50
51
52
53
54
typedef struct env_var {
const char* narrow;
const wchar_t* wide;
int len; /* including null or '=' */
int supplied;
int value_len;
} env_var_t;
55
#define E_V(str) { str "=", L##str, sizeof(str), 0, 0 }
56
57
58
59
60
61
62
63
64
#define UTF8_TO_UTF16(s, t) \
size = uv_utf8_to_utf16(s, NULL, 0) * sizeof(wchar_t); \
t = (wchar_t*)malloc(size); \
if (!t) { \
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); \
} \
if (!uv_utf8_to_utf16(s, t, size / sizeof(wchar_t))) { \
65
uv__set_sys_error(loop, GetLastError()); \
66
67
68
69
70
err = -1; \
goto done; \
}
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/* The `child_stdio_buffer` buffer has the following layout:
* int number_of_fds
* unsigned char crt_flags[number_of_fds]
* HANDLE os_handle[number_of_fds]
*/
#define CHILD_STDIO_SIZE(count) \
(sizeof(int) + \
sizeof(unsigned char) * (count) + \
sizeof(uintptr_t) * (count))
#define CHILD_STDIO_COUNT(buffer) \
*((unsigned int*) (buffer))
#define CHILD_STDIO_LPRESERVED2(buffer) \
((LPBYTE) (buffer))
#define CHILD_STDIO_CBRESERVED2(buffer) \
CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer)))
#define CHILD_STDIO_CRT_FLAGS(buffer, fd) \
*((unsigned char*) (buffer) + sizeof(int) + fd)
#define CHILD_STDIO_HANDLE(buffer, fd) \
*((HANDLE*) ((unsigned char*) (buffer) + \
sizeof(int) + \
sizeof(unsigned char) * \
CHILD_STDIO_COUNT((buffer)) + \
sizeof(HANDLE) * (fd)))
102
static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) {
103
uv_handle_init(loop, (uv_handle_t*) handle);
104
105
106
107
108
109
110
handle->type = UV_PROCESS;
handle->exit_cb = NULL;
handle->pid = 0;
handle->exit_signal = 0;
handle->wait_handle = INVALID_HANDLE_VALUE;
handle->process_handle = INVALID_HANDLE_VALUE;
handle->close_handle = INVALID_HANDLE_VALUE;
111
handle->child_stdio_buffer = NULL;
112
113
uv_req_init(loop, (uv_req_t*)&handle->exit_req);
114
115
handle->exit_req.type = UV_PROCESS_EXIT;
handle->exit_req.data = handle;
116
uv_req_init(loop, (uv_req_t*)&handle->close_req);
117
118
119
handle->close_req.type = UV_PROCESS_CLOSE;
handle->close_req.data = handle;
120
121
loop->counters.handle_init++;
loop->counters.process_init++;
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
}
/*
* Path search functions
*/
/*
* Helper function for search_path
*/
static wchar_t* search_path_join_test(const wchar_t* dir,
int dir_len,
const wchar_t* name,
int name_len,
const wchar_t* ext,
int ext_len,
const wchar_t* cwd,
int cwd_len) {
wchar_t *result, *result_pos;
DWORD attrs;
if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) {
/* It's a full path without drive letter, use cwd's drive letter only */
cwd_len = 2;
} else if (dir_len >= 2 && dir[1] == L':' &&
(dir_len < 3 || (dir[2] != L'/' && dir[2] != L'\\'))) {
/* It's a relative path with drive letter (ext.g. D:../some/file)
* Replace drive letter in dir by full cwd if it points to the same drive,
* otherwise use the dir only.
*/
if (cwd_len < 2 || _wcsnicmp(cwd, dir, 2) != 0) {
cwd_len = 0;
} else {
dir += 2;
dir_len -= 2;
}
} else if (dir_len > 2 && dir[1] == L':') {
/* It's an absolute path with drive letter
* Don't use the cwd at all
*/
cwd_len = 0;
}
/* Allocate buffer for output */
166
167
result = result_pos = (wchar_t*)malloc(sizeof(wchar_t) *
(cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1));
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* Copy cwd */
wcsncpy(result_pos, cwd, cwd_len);
result_pos += cwd_len;
/* Add a path separator if cwd didn't end with one */
if (cwd_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
result_pos[0] = L'\\';
result_pos++;
}
/* Copy dir */
wcsncpy(result_pos, dir, dir_len);
result_pos += dir_len;
/* Add a separator if the dir didn't end with one */
if (dir_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
result_pos[0] = L'\\';
result_pos++;
}
/* Copy filename */
wcsncpy(result_pos, name, name_len);
result_pos += name_len;
if (ext_len) {
194
195
196
197
198
199
200
/* Add a dot if the filename didn't end with one */
if (name_len && result_pos[-1] != '.') {
result_pos[0] = L'.';
result_pos++;
}
/* Copy extension */
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
wcsncpy(result_pos, ext, ext_len);
result_pos += ext_len;
}
/* Null terminator */
result_pos[0] = L'\0';
attrs = GetFileAttributesW(result);
if (attrs != INVALID_FILE_ATTRIBUTES &&
!(attrs & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))) {
return result;
}
free(result);
return NULL;
}
/*
* Helper function for search_path
*/
static wchar_t* path_search_walk_ext(const wchar_t *dir,
int dir_len,
const wchar_t *name,
int name_len,
wchar_t *cwd,
int cwd_len,
int name_has_ext) {
230
wchar_t* result;
231
232
/* If the name itself has a nonempty extension, try this extension first */
233
234
235
236
237
if (name_has_ext) {
result = search_path_join_test(dir, dir_len,
name, name_len,
L"", 0,
cwd, cwd_len);
238
239
if (result != NULL) {
return result;
240
}
241
}
242
243
244
245
246
247
248
249
250
/* Try .com extension */
result = search_path_join_test(dir, dir_len,
name, name_len,
L"com", 3,
cwd, cwd_len);
if (result != NULL) {
return result;
}
251
252
253
254
255
256
257
258
/* Try .exe extension */
result = search_path_join_test(dir, dir_len,
name, name_len,
L"exe", 3,
cwd, cwd_len);
if (result != NULL) {
return result;
259
260
}
261
return NULL;
262
263
264
265
266
267
268
269
270
271
}
/*
* search_path searches the system path for an executable filename -
* the windows API doesn't provide this as a standalone function nor as an
* option to CreateProcess.
*
* It tries to return an absolute filename.
*
272
273
274
275
* Furthermore, it tries to follow the semantics that cmd.exe, with this
* exception that PATHEXT environment variable isn't used. Since CreateProcess
* can start only .com and .exe files, only those extensions are tried. This
* behavior equals that of msvcrt's spawn functions.
276
277
278
279
280
281
282
*
* - Do not search the path if the filename already contains a path (either
* relative or absolute).
*
* - If there's really only a filename, check the current directory for file,
* then search all path directories.
*
283
* - If filename specified has *any* extension, search for the file with the
284
285
286
* specified extension first.
*
* - If the literal filename is not found in a directory, try *appending*
287
* (not replacing) .com first and then .exe.
288
289
290
291
292
293
294
295
296
*
* - The path variable may contain relative paths; relative paths are relative
* to the cwd.
*
* - Directories in path may or may not end with a trailing backslash.
*
* - CMD does not trim leading/trailing whitespace from path/pathex entries
* nor from the environment variables as a whole.
*
297
* - When cmd.exe cannot read a directory, it will just skip it and go on
298
299
300
301
302
303
304
305
* searching. However, unlike posix-y systems, it will happily try to run a
* file that is not readable/executable; if the spawn fails it will not
* continue searching.
*
* TODO: correctly interpret UNC paths
*/
static wchar_t* search_path(const wchar_t *file,
wchar_t *cwd,
306
const wchar_t *path) {
307
308
309
310
int file_has_dir;
wchar_t* result = NULL;
wchar_t *file_name_start;
wchar_t *dot;
311
312
const wchar_t *dir_start, *dir_end, *dir_path;
int dir_len;
313
314
315
316
317
318
319
320
321
322
323
324
325
int name_has_ext;
int file_len = wcslen(file);
int cwd_len = wcslen(cwd);
/* If the caller supplies an empty filename,
* we're not gonna return c:\windows\.exe -- GFY!
*/
if (file_len == 0
|| (file_len == 1 && file[0] == L'.')) {
return NULL;
}
326
327
/* Find the start of the filename so we can split the directory from the */
/* name. */
328
329
330
331
332
333
334
335
336
337
338
339
340
341
for (file_name_start = (wchar_t*)file + file_len;
file_name_start > file
&& file_name_start[-1] != L'\\'
&& file_name_start[-1] != L'/'
&& file_name_start[-1] != L':';
file_name_start--);
file_has_dir = file_name_start != file;
/* Check if the filename includes an extension */
dot = wcschr(file_name_start, L'.');
name_has_ext = (dot != NULL && dot[1] != L'\0');
if (file_has_dir) {
342
/* The file has a path inside, don't use path */
343
344
345
346
result = path_search_walk_ext(
file, file_name_start - file,
file_name_start, file_len - (file_name_start - file),
cwd, cwd_len,
347
name_has_ext);
348
349
} else {
350
dir_end = path;
351
352
353
354
355
/* The file is really only a name; look in cwd first, then scan path */
result = path_search_walk_ext(L"", 0,
file, file_len,
cwd, cwd_len,
356
name_has_ext);
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
while (result == NULL) {
if (*dir_end == L'\0') {
break;
}
/* Skip the separator that dir_end now points to */
if (dir_end != path) {
dir_end++;
}
/* Next slice starts just after where the previous one ended */
dir_start = dir_end;
/* Slice until the next ; or \0 is found */
dir_end = wcschr(dir_start, L';');
if (dir_end == NULL) {
dir_end = wcschr(dir_start, L'\0');
}
/* If the slice is zero-length, don't bother */
if (dir_end - dir_start == 0) {
continue;
}
382
383
384
385
386
387
388
389
390
391
392
393
394
395
dir_path = dir_start;
dir_len = dir_end - dir_start;
/* Adjust if the path is quoted. */
if (dir_path[0] == '"' || dir_path[0] == '\'') {
++dir_path;
--dir_len;
}
if (dir_path[dir_len - 1] == '"' || dir_path[dir_len - 1] == '\'') {
--dir_len;
}
result = path_search_walk_ext(dir_path, dir_len,
396
397
file, file_len,
cwd, cwd_len,
398
name_has_ext);
399
400
401
402
403
404
}
}
return result;
}
405
406
407
408
409
410
411
412
413
414
/*
* Quotes command line arguments
* Returns a pointer to the end (next char to be written) of the buffer
*/
wchar_t* quote_cmd_arg(const wchar_t *source, wchar_t *target) {
int len = wcslen(source),
i, quote_hit;
wchar_t* start;
415
/*
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
* Check if the string must be quoted;
* if unnecessary, don't do it, it may only confuse older programs.
*/
if (len == 0) {
return target;
}
if (NULL == wcspbrk(source, L" \t\"")) {
/* No quotation needed */
wcsncpy(target, source, len);
target += len;
return target;
}
if (NULL == wcspbrk(source, L"\"\\")) {
431
/*
432
433
434
435
436
437
438
439
440
441
442
* No embedded double quotes or backlashes, so I can just wrap
* quote marks around the whole thing.
*/
*(target++) = L'"';
wcsncpy(target, source, len);
target += len;
*(target++) = L'"';
return target;
}
/*
443
* Expected input/output:
444
445
446
447
448
* input : hello"world
* output: "hello\"world"
* input : hello""world
* output: "hello\"\"world"
* input : hello\world
449
* output: hello\world
450
* input : hello\\world
451
* output: hello\\world
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
* input : hello\"world
* output: "hello\\\"world"
* input : hello\\"world
* output: "hello\\\\\"world"
* input : hello world\
* output: "hello world\"
*/
*(target++) = L'"';
start = target;
quote_hit = 1;
for (i = len; i > 0; --i) {
*(target++) = source[i - 1];
if (quote_hit && source[i - 1] == L'\\') {
*(target++) = L'\\';
} else if(source[i - 1] == L'"') {
quote_hit = 1;
*(target++) = L'\\';
} else {
quote_hit = 0;
}
}
target[0] = L'\0';
wcsrev(start);
*(target++) = L'"';
return target;
}
481
482
483
wchar_t* make_program_args(char** args, int verbatim_arguments) {
484
485
486
487
488
489
wchar_t* dst;
wchar_t* ptr;
char** arg;
size_t size = 0;
size_t len;
int arg_count = 0;
490
491
492
wchar_t* buffer;
int arg_size;
int buffer_size = 0;
493
494
495
/* Count the required size. */
for (arg = args; *arg; arg++) {
496
497
498
arg_size = uv_utf8_to_utf16(*arg, NULL, 0) * sizeof(wchar_t);
size += arg_size;
buffer_size = arg_size > buffer_size ? arg_size : buffer_size;
499
500
501
arg_count++;
}
502
/* Adjust for potential quotes. Also assume the worst-case scenario
503
504
/* that every character needs escaping, so we need twice as much space. */
size = size * 2 + arg_count * 2;
505
506
507
508
509
510
dst = (wchar_t*)malloc(size);
if (!dst) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
511
512
513
514
515
buffer = (wchar_t*)malloc(buffer_size);
if (!buffer) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
516
ptr = dst;
517
518
for (arg = args; *arg; arg++) {
len = uv_utf8_to_utf16(*arg, buffer, (size_t)(size - (ptr - dst)));
519
if (!len) {
520
goto error;
521
}
522
523
524
525
526
527
528
if (verbatim_arguments) {
wcscpy(ptr, buffer);
ptr += len - 1;
} else {
ptr = quote_cmd_arg(buffer, ptr);
}
*ptr++ = *(arg + 1) ? L' ' : L'\0';
529
530
}
531
free(buffer);
532
return dst;
533
534
535
536
537
error:
free(dst);
free(buffer);
return NULL;
538
539
}
540
541
542
543
544
/*
* If we learn that people are passing in huge environment blocks
* then we should probably qsort() the array and then bsearch()
* to see if it contains this variable. But there are ownership
545
* issues associated with that solution; this is the caller's
546
547
* char**, and modifying it is rude.
*/
548
549
static void check_required_vars_contains_var(env_var_t* required, int size,
const char* var) {
550
551
552
553
554
555
556
557
558
559
int i;
for (i = 0; i < size; ++i) {
if (_strnicmp(required[i].narrow, var, required[i].len) == 0) {
required[i].supplied = 1;
return;
}
}
}
560
/*
561
562
563
* The way windows takes environment variables is different than what C does;
* Windows wants a contiguous block of null-terminated strings, terminated
* with an additional null.
564
*
565
566
567
568
569
570
* Windows has a few "essential" environment variables. winsock will fail
* to initialize if SYSTEMROOT is not defined; some APIs make reference to
* TEMP. SYSTEMDRIVE is probably also important. We therefore ensure that
* these get defined if the input environment block does not contain any
* values for them.
*/
571
wchar_t* make_program_env(char** env_block) {
572
573
574
575
576
wchar_t* dst;
wchar_t* ptr;
char** env;
int env_len = 1 * sizeof(wchar_t); /* room for closing null */
int len;
577
578
579
580
581
582
583
584
int i;
DWORD var_size;
env_var_t required_vars[] = {
E_V("SYSTEMROOT"),
E_V("SYSTEMDRIVE"),
E_V("TEMP"),
};
585
586
for (env = env_block; *env; env++) {
587
check_required_vars_contains_var(required_vars,
588
ARRAY_SIZE(required_vars),
589
*env);
590
591
592
env_len += (uv_utf8_to_utf16(*env, NULL, 0) * sizeof(wchar_t));
}
593
for (i = 0; i < ARRAY_SIZE(required_vars); ++i) {
594
595
596
597
598
599
600
601
602
603
604
605
if (!required_vars[i].supplied) {
env_len += required_vars[i].len * sizeof(wchar_t);
var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0);
if (var_size == 0) {
uv_fatal_error(GetLastError(), "GetEnvironmentVariableW");
}
required_vars[i].value_len = (int)var_size;
env_len += (int)var_size * sizeof(wchar_t);
}
}
dst = malloc(env_len);
606
607
608
609
610
611
612
613
614
615
616
617
618
619
if (!dst) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
ptr = dst;
for (env = env_block; *env; env++, ptr += len) {
len = uv_utf8_to_utf16(*env, ptr, (size_t)(env_len - (ptr - dst)));
if (!len) {
free(dst);
return NULL;
}
}
620
for (i = 0; i < ARRAY_SIZE(required_vars); ++i) {
621
622
623
624
if (!required_vars[i].supplied) {
wcscpy(ptr, required_vars[i].wide);
ptr += required_vars[i].len - 1;
*ptr++ = L'=';
625
626
627
var_size = GetEnvironmentVariableW(required_vars[i].wide,
ptr,
required_vars[i].value_len);
628
629
630
631
632
633
634
if (var_size == 0) {
uv_fatal_error(GetLastError(), "GetEnvironmentVariableW");
}
ptr += required_vars[i].value_len;
}
}
635
636
637
638
639
*ptr = L'\0';
return dst;
}
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
static int uv_create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* server_pipe,
HANDLE* child_pipe_ptr, unsigned int flags) {
char pipe_name[64];
SECURITY_ATTRIBUTES sa;
DWORD server_access = 0;
DWORD client_access = 0;
HANDLE child_pipe = INVALID_HANDLE_VALUE;
if (flags & UV_READABLE_PIPE) {
server_access |= PIPE_ACCESS_OUTBOUND;
client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
}
if (flags & UV_WRITABLE_PIPE) {
server_access |= PIPE_ACCESS_INBOUND;
client_access |= GENERIC_WRITE;
}
/* Create server pipe handle. */
if (uv_stdio_pipe_server(loop,
server_pipe,
server_access,
pipe_name,
sizeof(pipe_name)) < 0) {
goto error;
}
/* Create child pipe handle. */
sa.nLength = sizeof sa;
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
child_pipe = CreateFileA(pipe_name,
client_access,
0,
&sa,
OPEN_EXISTING,
server_pipe->ipc ? FILE_FLAG_OVERLAPPED : 0,
NULL);
if (child_pipe == INVALID_HANDLE_VALUE) {
uv__set_sys_error(loop, GetLastError());
goto error;
}
#ifndef NDEBUG
/* Validate that the pipe was opened in the right mode. */
{
DWORD mode;
BOOL r = GetNamedPipeHandleState(child_pipe,
&mode,
NULL,
NULL,
NULL,
NULL,
0);
assert(r == TRUE);
assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT));
}
#endif
/* Do a blocking ConnectNamedPipe. This should not block because we have */
/* both ends of the pipe created. */
if (!ConnectNamedPipe(server_pipe->handle, NULL)) {
if (GetLastError() != ERROR_PIPE_CONNECTED) {
uv__set_sys_error(loop, GetLastError());
goto error;
}
}
*child_pipe_ptr = child_pipe;
return 0;
error:
if (server_pipe->handle != INVALID_HANDLE_VALUE) {
uv_pipe_cleanup(loop, server_pipe);
}
if (child_pipe != INVALID_HANDLE_VALUE) {
CloseHandle(child_pipe);
}
return -1;
}
static int duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) {
HANDLE current_process;
current_process = GetCurrentProcess();
if (!DuplicateHandle(current_process,
handle,
current_process,
dup,
0,
TRUE,
DUPLICATE_SAME_ACCESS)) {
*dup = INVALID_HANDLE_VALUE;
uv__set_sys_error(loop, GetLastError());
return -1;
}
return 0;
}
static int duplicate_fd(uv_loop_t* loop, int fd, HANDLE* dup) {
HANDLE handle;
if (fd == -1) {
*dup = INVALID_HANDLE_VALUE;
uv__set_artificial_error(loop, UV_EBADF);
return -1;
}
handle = (HANDLE)_get_osfhandle(fd);
return duplicate_handle(loop, handle, dup);
}
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
static int create_nul_handle(uv_loop_t* loop, HANDLE* handle_ptr,
DWORD access) {
HANDLE handle;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof sa;
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
handle = CreateFileW(L"NUL",
access,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sa,
OPEN_EXISTING,
0,
NULL);
if (handle == INVALID_HANDLE_VALUE) {
uv__set_sys_error(loop, GetLastError());
return -1;
}
*handle_ptr = handle;
return 0;
}
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
static void set_child_stdio_noinherit(void* buffer) {
int i, count;
count = CHILD_STDIO_COUNT(buffer);
for (i = 0; i < count; i++) {
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
if (handle != INVALID_HANDLE_VALUE) {
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
}
}
}
static void close_and_free_child_stdio(void* buffer) {
int i, count;
count = CHILD_STDIO_COUNT(buffer);
for (i = 0; i < count; i++) {
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
if (handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle);
}
}
free(buffer);
}
813
814
815
/*
* Called on Windows thread-pool thread to indicate that
* a child process has exited.
816
817
818
*/
static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) {
uv_process_t* process = (uv_process_t*)data;
819
uv_loop_t* loop = process->loop;
820
821
822
assert(didTimeout == FALSE);
assert(process);
823
824
/* Post completed */
825
POST_COMPLETION_FOR_REQ(loop, &process->exit_req);
826
827
828
}
829
830
831
/*
* Called on Windows thread-pool thread to indicate that
* UnregisterWaitEx has completed.
832
833
834
*/
static void CALLBACK close_wait_callback(void* data, BOOLEAN didTimeout) {
uv_process_t* process = (uv_process_t*)data;
835
uv_loop_t* loop = process->loop;
836
837
838
839
840
assert(didTimeout == FALSE);
assert(process);
/* Post completed */
841
POST_COMPLETION_FOR_REQ(loop, &process->close_req);
842
843
844
}
845
846
847
848
849
850
851
852
853
/*
* Called on windows thread pool when CreateProcess failed. It writes an error
* message to the process' intended stderr and then posts a PROCESS_EXIT
* packet to the completion port.
*/
static DWORD WINAPI spawn_failure(void* data) {
char syscall[] = "CreateProcessW: ";
char unknown[] = "unknown error\n";
uv_process_t* process = (uv_process_t*) data;
854
uv_loop_t* loop = process->loop;
855
HANDLE child_stderr = CHILD_STDIO_HANDLE(process->child_stdio_buffer, 2);
856
857
858
char* buf = NULL;
DWORD count, written;
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
if (child_stderr != INVALID_HANDLE_VALUE) {
WriteFile(child_stderr, syscall, sizeof(syscall) - 1, &written, NULL);
count = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
process->spawn_errno,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) &buf,
0,
NULL);
if (buf != NULL && count > 0) {
WriteFile(child_stderr, buf, count, &written, NULL);
LocalFree(buf);
} else {
WriteFile(child_stderr, unknown, sizeof(unknown) - 1, &written, NULL);
}
878
879
880
FlushFileBuffers(child_stderr);
}
881
882
/* Post completed */
883
POST_COMPLETION_FOR_REQ(loop, &process->exit_req);
884
885
886
887
888
return 0;
}
889
890
891
/* Called on main thread after a child process has exited. */
void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
DWORD exit_code;
892
893
894
895
896
897
/* FIXME: race condition. */
if (handle->flags & UV_HANDLE_CLOSING) {
return;
}
898
899
900
901
902
903
/* Unregister from process notification. */
if (handle->wait_handle != INVALID_HANDLE_VALUE) {
UnregisterWait(handle->wait_handle);
handle->wait_handle = INVALID_HANDLE_VALUE;
}
904
905
906
907
if (handle->process_handle == INVALID_HANDLE_VALUE ||
!GetExitCodeProcess(handle->process_handle, &exit_code)) {
/* The process never even started in the first place, or we were unable */
/* to obtain the exit code. */
908
exit_code = 127;
909
910
}
911
912
913
914
/* Set the handle to inactive: no callbacks will be made after the exit */
/* callback.*/
uv__handle_stop(handle);
915
916
917
918
919
920
921
922
/* Fire the exit callback. */
if (handle->exit_cb) {
handle->exit_cb(handle, exit_code, handle->exit_signal);
}
}
/* Called on main thread after UnregisterWaitEx finishes. */
923
924
void uv_process_proc_close(uv_loop_t* loop, uv_process_t* handle) {
uv_want_endgame(loop, (uv_handle_t*)handle);
925
926
927
}
928
void uv_process_close(uv_loop_t* loop, uv_process_t* handle) {
929
930
uv__handle_start(handle);
931
932
933
934
935
936
937
938
939
if (handle->wait_handle != INVALID_HANDLE_VALUE) {
handle->close_handle = CreateEvent(NULL, FALSE, FALSE, NULL);
UnregisterWaitEx(handle->wait_handle, handle->close_handle);
handle->wait_handle = NULL;
RegisterWaitForSingleObject(&handle->wait_handle, handle->close_handle,
close_wait_callback, (void*)handle, INFINITE,
WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
} else {
940
uv_want_endgame(loop, (uv_handle_t*)handle);
941
942
943
944
}
}
945
946
947
948
949
950
951
952
953
void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) {
if (handle->flags & UV_HANDLE_CLOSING) {
assert(!(handle->flags & UV_HANDLE_CLOSED));
uv__handle_stop(handle);
/* Clean-up the process handle. */
CloseHandle(handle->process_handle);
/* Clean up the child stdio ends that may have been left open. */
954
955
956
957
if (handle->child_stdio_buffer != NULL) {
close_and_free_child_stdio(handle->child_stdio_buffer);
}
958
uv__handle_close(handle);
959
960
961
962
}
}
963
964
965
966
static int init_child_stdio(uv_loop_t* loop, uv_process_options_t* options,
void** buffer_ptr) {
void* buffer;
int count, i;
967
968
count = options->stdio_count;
969
970
971
972
973
974
975
976
if (count < 0 || count > 255) {
/* Only support FDs 0-255 */
uv__set_artificial_error(loop, UV_ENOTSUP);
return -1;
} else if (count < 3) {
/* There should always be at least 3 stdio handles. */
count = 3;
977
978
}
979
980
981
982
983
/* Allocate the child stdio buffer */
buffer = malloc(CHILD_STDIO_SIZE(count));
if (buffer == NULL) {
uv__set_artificial_error(loop, UV_ENOMEM);
return -1;
984
985
}
986
987
988
989
990
991
/* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can */
/* clean up on failure. */
CHILD_STDIO_COUNT(buffer) = count;
for (i = 0; i < count; i++) {
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
992
993
}
994
995
996
997
998
999
1000
for (i = 0; i < count; i++) {
uv_stdio_container_t fdopt;
if (i < options->stdio_count) {
fdopt = options->stdio[i];
} else {
fdopt.flags = UV_IGNORE;
}