-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
process.c
1277 lines (1080 loc) · 35 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
#include <limits.h>
28
#include <wchar.h>
29
#include <malloc.h> /* alloca */
30
31
32
#include "uv.h"
#include "internal.h"
33
#include "handle-inl.h"
34
#include "req-inl.h"
35
36
37
38
#define SIGKILL 9
39
40
typedef struct env_var {
41
42
43
const WCHAR* const wide;
const WCHAR* const wide_eq;
const size_t len; /* including null or '=' */
44
45
} env_var_t;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#define E_V(str) { L##str, L##str L"=", sizeof(str) }
static const env_var_t required_vars[] = { /* keep me sorted */
E_V("HOMEDRIVE"),
E_V("HOMEPATH"),
E_V("LOGONSERVER"),
E_V("PATH"),
E_V("SYSTEMDRIVE"),
E_V("SYSTEMROOT"),
E_V("TEMP"),
E_V("USERDOMAIN"),
E_V("USERNAME"),
E_V("USERPROFILE"),
E_V("WINDIR"),
};
static size_t n_required_vars = ARRAY_SIZE(required_vars);
62
63
64
65
66
67
static HANDLE uv_global_job_handle_;
static uv_once_t uv_global_job_handle_init_guard_ = UV_ONCE_INIT;
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
static void uv__init_global_job_handle(void) {
/* Create a job object and set it up to kill all contained processes when
* it's closed. Since this handle is made non-inheritable and we're not
* giving it to anyone, we're the only process holding a reference to it.
* That means that if this process exits it is closed and all the processes
* it contains are killed. All processes created with uv_spawn that are not
* spawned with the UV_PROCESS_DETACHED flag are assigned to this job.
*
* We're setting the JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag so only the
* processes that we explicitly add are affected, and *their* subprocesses
* are not. This ensures that our child processes are not limited in their
* ability to use job control on Windows versions that don't deal with
* nested jobs (prior to Windows 8 / Server 2012). It also lets our child
* processes created detached processes without explicitly breaking away
* from job control (which uv_spawn doesn't, either).
*/
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
SECURITY_ATTRIBUTES attr;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
memset(&attr, 0, sizeof attr);
attr.bInheritHandle = FALSE;
memset(&info, 0, sizeof info);
info.BasicLimitInformation.LimitFlags =
JOB_OBJECT_LIMIT_BREAKAWAY_OK |
JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK |
JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION |
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
uv_global_job_handle_ = CreateJobObjectW(&attr, NULL);
if (uv_global_job_handle_ == NULL)
uv_fatal_error(GetLastError(), "CreateJobObjectW");
if (!SetInformationJobObject(uv_global_job_handle_,
JobObjectExtendedLimitInformation,
&info,
sizeof info))
uv_fatal_error(GetLastError(), "SetInformationJobObject");
}
109
static int uv_utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) {
110
111
112
113
114
115
116
117
118
119
int ws_len, r;
WCHAR* ws;
ws_len = MultiByteToWideChar(CP_UTF8,
0,
s,
-1,
NULL,
0);
if (ws_len <= 0) {
120
return GetLastError();
121
122
}
123
ws = (WCHAR*) uv__malloc(ws_len * sizeof(WCHAR));
124
if (ws == NULL) {
125
return ERROR_OUTOFMEMORY;
126
127
128
129
130
131
132
133
134
135
136
}
r = MultiByteToWideChar(CP_UTF8,
0,
s,
-1,
ws,
ws_len);
assert(r == ws_len);
*ws_ptr = ws;
137
return 0;
138
}
139
140
141
static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) {
142
uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS);
143
144
145
146
147
handle->exit_cb = NULL;
handle->pid = 0;
handle->exit_signal = 0;
handle->wait_handle = INVALID_HANDLE_VALUE;
handle->process_handle = INVALID_HANDLE_VALUE;
148
handle->child_stdio_buffer = NULL;
149
handle->exit_cb_pending = 0;
150
151
UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT);
152
153
154
155
156
157
158
159
160
161
162
handle->exit_req.data = handle;
}
/*
* Path search functions
*/
/*
* Helper function for search_path
*/
163
static WCHAR* search_path_join_test(const WCHAR* dir,
164
165
166
167
168
169
170
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) {
171
WCHAR *result, *result_pos;
172
DWORD attrs;
173
174
175
176
if (dir_len > 2 && dir[0] == L'\\' && dir[1] == L'\\') {
/* It's a UNC path so ignore cwd */
cwd_len = 0;
} else if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) {
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* 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 */
199
result = result_pos = (WCHAR*)uv__malloc(sizeof(WCHAR) *
200
(cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1));
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
/* 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) {
227
228
229
230
231
232
233
/* 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 */
234
235
236
237
238
239
240
241
242
243
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 &&
244
!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
245
246
247
return result;
}
248
uv__free(result);
249
250
251
252
253
254
255
return NULL;
}
/*
* Helper function for search_path
*/
256
static WCHAR* path_search_walk_ext(const WCHAR *dir,
257
258
259
260
261
262
size_t dir_len,
const WCHAR *name,
size_t name_len,
WCHAR *cwd,
size_t cwd_len,
int name_has_ext) {
263
WCHAR* result;
264
265
/* If the name itself has a nonempty extension, try this extension first */
266
267
268
269
270
if (name_has_ext) {
result = search_path_join_test(dir, dir_len,
name, name_len,
L"", 0,
cwd, cwd_len);
271
272
if (result != NULL) {
return result;
273
}
274
}
275
276
277
278
279
280
281
282
283
/* 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;
}
284
285
286
287
288
289
290
291
/* 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;
292
293
}
294
return NULL;
295
296
297
298
299
300
301
302
303
304
}
/*
* 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.
*
305
306
307
308
* 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.
309
310
311
312
313
314
315
*
* - 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.
*
316
* - If filename specified has *any* extension, search for the file with the
317
318
319
* specified extension first.
*
* - If the literal filename is not found in a directory, try *appending*
320
* (not replacing) .com first and then .exe.
321
322
323
324
325
326
327
328
329
*
* - 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.
*
330
* - When cmd.exe cannot read a directory, it will just skip it and go on
331
332
333
334
* 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.
*
335
336
337
338
339
* UNC path support: we are dealing with UNC paths in both the path and the
* filename. This is a deviation from what cmd.exe does (it does not let you
* start a program by specifying an UNC path on the command line) but this is
* really a pointless restriction.
*
340
*/
341
342
343
static WCHAR* search_path(const WCHAR *file,
WCHAR *cwd,
const WCHAR *path) {
344
int file_has_dir;
345
346
347
348
WCHAR* result = NULL;
WCHAR *file_name_start;
WCHAR *dot;
const WCHAR *dir_start, *dir_end, *dir_path;
349
size_t dir_len;
350
351
int name_has_ext;
352
353
size_t file_len = wcslen(file);
size_t cwd_len = wcslen(cwd);
354
355
356
357
358
359
360
361
362
/* 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;
}
363
364
/* Find the start of the filename so we can split the directory from the
* name. */
365
for (file_name_start = (WCHAR*)file + file_len;
366
367
368
369
370
371
372
373
374
375
376
377
378
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) {
379
/* The file has a path inside, don't use path */
380
381
382
383
result = path_search_walk_ext(
file, file_name_start - file,
file_name_start, file_len - (file_name_start - file),
cwd, cwd_len,
384
name_has_ext);
385
386
} else {
387
dir_end = path;
388
389
390
391
392
/* 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,
393
name_has_ext);
394
395
396
397
398
399
400
while (result == NULL) {
if (*dir_end == L'\0') {
break;
}
/* Skip the separator that dir_end now points to */
401
if (dir_end != path || *path == L';') {
402
403
404
405
406
407
dir_end++;
}
/* Next slice starts just after where the previous one ended */
dir_start = dir_end;
408
409
410
411
412
413
414
/* If path is quoted, find quote end */
if (*dir_start == L'"' || *dir_start == L'\'') {
dir_end = wcschr(dir_start + 1, *dir_start);
if (dir_end == NULL) {
dir_end = wcschr(dir_start, L'\0');
}
}
415
/* Slice until the next ; or \0 is found */
416
dir_end = wcschr(dir_end, L';');
417
418
419
420
421
422
423
424
425
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;
}
426
427
428
429
430
431
432
433
434
435
436
437
438
439
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,
440
441
file, file_len,
cwd, cwd_len,
442
name_has_ext);
443
444
445
446
447
448
}
}
return result;
}
449
450
451
452
453
/*
* Quotes command line arguments
* Returns a pointer to the end (next char to be written) of the buffer
*/
454
WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) {
455
456
457
size_t len = wcslen(source);
size_t i;
int quote_hit;
458
WCHAR* start;
459
460
if (len == 0) {
461
462
463
/* Need double quotation for empty argument */
*(target++) = L'"';
*(target++) = L'"';
464
465
466
467
468
469
470
471
472
473
474
return target;
}
if (NULL == wcspbrk(source, L" \t\"")) {
/* No quotation needed */
wcsncpy(target, source, len);
target += len;
return target;
}
if (NULL == wcspbrk(source, L"\"\\")) {
475
/*
476
477
478
479
480
481
482
483
484
485
486
* 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;
}
/*
487
* Expected input/output:
488
489
490
491
492
* input : hello"world
* output: "hello\"world"
* input : hello""world
* output: "hello\"\"world"
* input : hello\world
493
* output: hello\world
494
* input : hello\\world
495
* output: hello\\world
496
497
498
499
500
* input : hello\"world
* output: "hello\\\"world"
* input : hello\\"world
* output: "hello\\\\\"world"
* input : hello world\
501
* output: "hello world\\"
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
*/
*(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;
}
525
526
527
int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) {
528
char** arg;
529
530
531
532
533
WCHAR* dst = NULL;
WCHAR* temp_buffer = NULL;
size_t dst_len = 0;
size_t temp_buffer_len = 0;
WCHAR* pos;
534
int arg_count = 0;
535
int err = 0;
536
537
538
/* Count the required size. */
for (arg = args; *arg; arg++) {
539
540
541
542
543
544
545
546
547
DWORD arg_len;
arg_len = MultiByteToWideChar(CP_UTF8,
0,
*arg,
-1,
NULL,
0);
if (arg_len == 0) {
548
return GetLastError();
549
550
551
552
553
554
555
}
dst_len += arg_len;
if (arg_len > temp_buffer_len)
temp_buffer_len = arg_len;
556
557
558
arg_count++;
}
559
560
/* Adjust for potential quotes. Also assume the worst-case scenario that
* every character needs escaping, so we need twice as much space. */
561
dst_len = dst_len * 2 + arg_count * 2;
562
563
/* Allocate buffer for the final command line. */
564
dst = (WCHAR*) uv__malloc(dst_len * sizeof(WCHAR));
565
if (dst == NULL) {
566
err = ERROR_OUTOFMEMORY;
567
goto error;
568
569
}
570
/* Allocate temporary working buffer. */
571
temp_buffer = (WCHAR*) uv__malloc(temp_buffer_len * sizeof(WCHAR));
572
if (temp_buffer == NULL) {
573
err = ERROR_OUTOFMEMORY;
574
goto error;
575
576
}
577
pos = dst;
578
for (arg = args; *arg; arg++) {
579
580
581
582
583
584
585
586
DWORD arg_len;
/* Convert argument to wide char. */
arg_len = MultiByteToWideChar(CP_UTF8,
0,
*arg,
-1,
temp_buffer,
587
(int) (dst + dst_len - pos));
588
if (arg_len == 0) {
589
err = GetLastError();
590
goto error;
591
}
592
593
if (verbatim_arguments) {
594
595
596
/* Copy verbatim. */
wcscpy(pos, temp_buffer);
pos += arg_len - 1;
597
} else {
598
599
/* Quote/escape, if needed. */
pos = quote_cmd_arg(temp_buffer, pos);
600
}
601
602
*pos++ = *(arg + 1) ? L' ' : L'\0';
603
604
}
605
uv__free(temp_buffer);
606
607
*dst_ptr = dst;
608
return 0;
609
610
error:
611
612
uv__free(dst);
uv__free(temp_buffer);
613
return err;
614
615
}
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
wchar_t* a_eq;
wchar_t* b_eq;
wchar_t* A;
wchar_t* B;
int nb;
int r;
if (na < 0) {
a_eq = wcschr(a, L'=');
assert(a_eq);
na = (int)(long)(a_eq - a);
} else {
na--;
}
b_eq = wcschr(b, L'=');
assert(b_eq);
nb = b_eq - b;
A = alloca((na+1) * sizeof(wchar_t));
B = alloca((nb+1) * sizeof(wchar_t));
r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na);
assert(r==na);
A[na] = L'\0';
r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb);
assert(r==nb);
B[nb] = L'\0';
while (1) {
wchar_t AA = *A++;
wchar_t BB = *B++;
if (AA < BB) {
return -1;
} else if (AA > BB) {
return 1;
} else if (!AA && !BB) {
return 0;
655
656
657
658
659
}
}
}
660
661
662
663
664
665
666
static int qsort_wcscmp(const void *a, const void *b) {
wchar_t* astr = *(wchar_t* const*)a;
wchar_t* bstr = *(wchar_t* const*)b;
return env_strncmp(astr, -1, bstr);
}
667
/*
668
669
670
* 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.
671
*
672
673
674
675
676
* 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.
677
678
679
680
681
*
* Also add variables known to Cygwin to be required for correct
* subprocess operation in many cases:
* https://github.com/Alexpux/Cygwin/blob/b266b04fbbd3a595f02ea149e4306d3ab9b1fe3d/winsup/cygwin/environ.cc#L955
*
682
*/
683
int make_program_env(char* env_block[], WCHAR** dst_ptr) {
684
685
WCHAR* dst;
WCHAR* ptr;
686
char** env;
687
size_t env_len = 0;
688
int len;
689
size_t i;
690
DWORD var_size;
691
692
693
694
695
size_t env_block_count = 1; /* 1 for null-terminator */
WCHAR* dst_copy;
WCHAR** ptr_copy;
WCHAR** env_copy;
DWORD* required_vars_value_len = alloca(n_required_vars * sizeof(DWORD*));
696
697
/* first pass: determine size in UTF-16 */
698
for (env = env_block; *env; env++) {
699
int len;
700
701
702
703
704
705
706
707
708
709
710
711
if (strchr(*env, '=')) {
len = MultiByteToWideChar(CP_UTF8,
0,
*env,
-1,
NULL,
0);
if (len <= 0) {
return GetLastError();
}
env_len += len;
env_block_count++;
712
}
713
714
715
}
/* second pass: copy to UTF-16 environment block */
716
dst_copy = (WCHAR*)uv__malloc(env_len * sizeof(WCHAR));
717
718
719
720
if (!dst_copy) {
return ERROR_OUTOFMEMORY;
}
env_copy = alloca(env_block_count * sizeof(WCHAR*));
721
722
723
724
725
726
727
728
729
730
731
732
733
ptr = dst_copy;
ptr_copy = env_copy;
for (env = env_block; *env; env++) {
if (strchr(*env, '=')) {
len = MultiByteToWideChar(CP_UTF8,
0,
*env,
-1,
ptr,
(int) (env_len - (ptr - dst_copy)));
if (len <= 0) {
DWORD err = GetLastError();
734
uv__free(dst_copy);
735
736
737
738
739
return err;
}
*ptr_copy++ = ptr;
ptr += len;
}
740
}
741
742
743
744
745
*ptr_copy = NULL;
assert(env_len == ptr - dst_copy);
/* sort our (UTF-16) copy */
qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp);
746
747
748
749
750
751
752
753
754
755
756
757
758
/* third pass: check for required variables */
for (ptr_copy = env_copy, i = 0; i < n_required_vars; ) {
int cmp;
if (!*ptr_copy) {
cmp = -1;
} else {
cmp = env_strncmp(required_vars[i].wide_eq,
required_vars[i].len,
*ptr_copy);
}
if (cmp < 0) {
/* missing required var */
759
var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0);
760
required_vars_value_len[i] = var_size;
761
762
763
764
if (var_size != 0) {
env_len += required_vars[i].len;
env_len += var_size;
}
765
766
767
768
769
i++;
} else {
ptr_copy++;
if (cmp == 0)
i++;
770
771
772
}
}
773
/* final pass: copy, in sort order, and inserting required variables */
774
dst = uv__malloc((1+env_len) * sizeof(WCHAR));
775
if (!dst) {
776
uv__free(dst_copy);
777
return ERROR_OUTOFMEMORY;
778
779
}
780
781
782
783
784
785
786
787
788
789
790
791
for (ptr = dst, ptr_copy = env_copy, i = 0;
*ptr_copy || i < n_required_vars;
ptr += len) {
int cmp;
if (i >= n_required_vars) {
cmp = 1;
} else if (!*ptr_copy) {
cmp = -1;
} else {
cmp = env_strncmp(required_vars[i].wide_eq,
required_vars[i].len,
*ptr_copy);
792
}
793
794
795
796
797
798
799
800
801
802
803
804
if (cmp < 0) {
/* missing required var */
len = required_vars_value_len[i];
if (len) {
wcscpy(ptr, required_vars[i].wide_eq);
ptr += required_vars[i].len;
var_size = GetEnvironmentVariableW(required_vars[i].wide,
ptr,
(int) (env_len - (ptr - dst)));
if (var_size != len-1) { /* race condition? */
uv_fatal_error(GetLastError(), "GetEnvironmentVariableW");
}
805
}
806
807
808
809
i++;
} else {
/* copy var from env_block */
len = wcslen(*ptr_copy) + 1;
810
wmemcpy(ptr, *ptr_copy, len);
811
812
813
ptr_copy++;
if (cmp == 0)
i++;
814
815
816
}
}
817
/* Terminate with an extra NULL. */
818
assert(env_len == (ptr - dst));
819
*ptr = L'\0';
820
821
uv__free(dst_copy);
822
*dst_ptr = dst;
823
return 0;
824
825
}
826
827
828
829
830
831
832
833
/*
* Attempt to find the value of the PATH environment variable in the child's
* preprocessed environment.
*
* If found, a pointer into `env` is returned. If not found, NULL is returned.
*/
static WCHAR* find_path(WCHAR *env) {
for (; env != NULL && *env != 0; env += wcslen(env) + 1) {
834
835
836
837
838
if ((env[0] == L'P' || env[0] == L'p') &&
(env[1] == L'A' || env[1] == L'a') &&
(env[2] == L'T' || env[2] == L't') &&
(env[3] == L'H' || env[3] == L'h') &&
(env[4] == L'=')) {
839
return &env[5];
840
}
841
842
843
844
}
return NULL;
}
845
846
847
848
/*
* Called on Windows thread-pool thread to indicate that
* a child process has exited.
849
850
*/
static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) {
851
uv_process_t* process = (uv_process_t*) data;
852
uv_loop_t* loop = process->loop;
853
854
855
assert(didTimeout == FALSE);
assert(process);
856
assert(!process->exit_cb_pending);
857
858
process->exit_cb_pending = 1;
859
860
/* Post completed */
861
POST_COMPLETION_FOR_REQ(loop, &process->exit_req);
862
863
864
}
865
866
/* Called on main thread after a child process has exited. */
void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
867
int64_t exit_code;
868
DWORD status;
869
870
871
872
assert(handle->exit_cb_pending);
handle->exit_cb_pending = 0;
873
874
/* If we're closing, don't call the exit callback. Just schedule a close
* callback now. */
875
if (handle->flags & UV_HANDLE_CLOSING) {
876
uv_want_endgame(loop, (uv_handle_t*) handle);
877
878
879
return;
}
880
881
882
883
884
885
/* Unregister from process notification. */
if (handle->wait_handle != INVALID_HANDLE_VALUE) {
UnregisterWait(handle->wait_handle);
handle->wait_handle = INVALID_HANDLE_VALUE;
}
886
887
/* Set the handle to inactive: no callbacks will be made after the exit
* callback. */
888
889
uv__handle_stop(handle);
890
if (GetExitCodeProcess(handle->process_handle, &status)) {
891
892
exit_code = status;
} else {
893
/* Unable to obtain the exit code. This should never happen. */
894
exit_code = uv_translate_sys_error(GetLastError());
895
896
}
897
898
899
900
901
902
903
/* Fire the exit callback. */
if (handle->exit_cb) {
handle->exit_cb(handle, exit_code, handle->exit_signal);
}
}
904
void uv_process_close(uv_loop_t* loop, uv_process_t* handle) {
905
uv__handle_closing(handle);
906
907
if (handle->wait_handle != INVALID_HANDLE_VALUE) {
908
909
/* This blocks until either the wait was cancelled, or the callback has
* completed. */
910
911
912
913
914
BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE);
if (!r) {
/* This should never happen, and if it happens, we can't recover... */
uv_fatal_error(GetLastError(), "UnregisterWaitEx");
}
915
916
917
918
919
handle->wait_handle = INVALID_HANDLE_VALUE;
}
if (!handle->exit_cb_pending) {
920
uv_want_endgame(loop, (uv_handle_t*)handle);
921
922
923
924
}
}
925
void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) {
926
assert(!handle->exit_cb_pending);
927
assert(handle->flags & UV_HANDLE_CLOSING);
928
assert(!(handle->flags & UV_HANDLE_CLOSED));
929
930
931
/* Clean-up the process handle. */
CloseHandle(handle->process_handle);
932
933
uv__handle_close(handle);
934
935
936
}
937
938
939
int uv_spawn(uv_loop_t* loop,
uv_process_t* process,
const uv_process_options_t* options) {
940
int i;
941
int err = 0;
942
WCHAR* path = NULL, *alloc_path = NULL;
943
BOOL result;
944
WCHAR* application_path = NULL, *application = NULL, *arguments = NULL,
945
*env = NULL, *cwd = NULL;
946
947
STARTUPINFOW startup;
PROCESS_INFORMATION info;
948
DWORD process_flags;
949
950
951
952
uv_process_init(loop, process);
process->exit_cb = options->exit_cb;
953
if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
954
return UV_ENOTSUP;
955
956
}
957
958
if (options->file == NULL ||
options->args == NULL) {
959
return UV_EINVAL;
960
961
}
962
963
964
965
966
967
assert(options->file != NULL);
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
UV_PROCESS_SETGID |
UV_PROCESS_SETUID |
UV_PROCESS_WINDOWS_HIDE |
UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
968
969
err = uv_utf8_to_utf16_alloc(options->file, &application);
970
if (err)
971
goto done;
972
973
974
975
976
err = make_program_args(
options->args,
options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
&arguments);
977
if (err)
978
goto done;
979
980
981
if (options->env) {
err = make_program_env(options->env, &env);
982
if (err)
983
goto done;
984
985
}
986
if (options->cwd) {
987
/* Explicit cwd */
988
err = uv_utf8_to_utf16_alloc(options->cwd, &cwd);
989
if (err)
990
goto done;
991
992
} else {
993
994
/* Inherit cwd */
DWORD cwd_len, r;
995
996
997
cwd_len = GetCurrentDirectoryW(0, NULL);
if (!cwd_len) {
998
err = GetLastError();
999
goto done;
1000
}