|
160
|
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
|
|
|
27 #include "uv.h"
|
|
|
28 #include "internal.h"
|
|
|
29 #include "handle-inl.h"
|
|
|
30
|
|
|
31
|
|
|
32 /*
|
|
|
33 * The `child_stdio_buffer` buffer has the following layout:
|
|
|
34 * int number_of_fds
|
|
|
35 * unsigned char crt_flags[number_of_fds]
|
|
|
36 * HANDLE os_handle[number_of_fds]
|
|
|
37 */
|
|
|
38 #define CHILD_STDIO_SIZE(count) \
|
|
|
39 (sizeof(int) + \
|
|
|
40 sizeof(unsigned char) * (count) + \
|
|
|
41 sizeof(uintptr_t) * (count))
|
|
|
42
|
|
|
43 #define CHILD_STDIO_COUNT(buffer) \
|
|
|
44 *((unsigned int*) (buffer))
|
|
|
45
|
|
|
46 #define CHILD_STDIO_CRT_FLAGS(buffer, fd) \
|
|
|
47 *((unsigned char*) (buffer) + sizeof(int) + fd)
|
|
|
48
|
|
|
49 #define CHILD_STDIO_HANDLE(buffer, fd) \
|
|
|
50 ((void*) ((unsigned char*) (buffer) + \
|
|
|
51 sizeof(int) + \
|
|
|
52 sizeof(unsigned char) * \
|
|
|
53 CHILD_STDIO_COUNT((buffer)) + \
|
|
|
54 sizeof(HANDLE) * (fd)))
|
|
|
55
|
|
|
56
|
|
|
57 /* CRT file descriptor mode flags */
|
|
|
58 #define FOPEN 0x01
|
|
|
59 #define FEOFLAG 0x02
|
|
|
60 #define FCRLF 0x04
|
|
|
61 #define FPIPE 0x08
|
|
|
62 #define FNOINHERIT 0x10
|
|
|
63 #define FAPPEND 0x20
|
|
|
64 #define FDEV 0x40
|
|
|
65 #define FTEXT 0x80
|
|
|
66
|
|
|
67
|
|
|
68 /*
|
|
|
69 * Clear the HANDLE_FLAG_INHERIT flag from all HANDLEs that were inherited
|
|
|
70 * the parent process. Don't check for errors - the stdio handles may not be
|
|
|
71 * valid, or may be closed already. There is no guarantee that this function
|
|
|
72 * does a perfect job.
|
|
|
73 */
|
|
|
74 void uv_disable_stdio_inheritance(void) {
|
|
|
75 HANDLE handle;
|
|
|
76 STARTUPINFOW si;
|
|
|
77
|
|
|
78 /* Make the windows stdio handles non-inheritable. */
|
|
|
79 handle = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
80 if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
|
|
81 SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
|
|
82
|
|
|
83 handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
84 if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
|
|
85 SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
|
|
86
|
|
|
87 handle = GetStdHandle(STD_ERROR_HANDLE);
|
|
|
88 if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
|
|
89 SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
|
|
90
|
|
|
91 /* Make inherited CRT FDs non-inheritable. */
|
|
|
92 GetStartupInfoW(&si);
|
|
|
93 if (uv__stdio_verify(si.lpReserved2, si.cbReserved2))
|
|
|
94 uv__stdio_noinherit(si.lpReserved2);
|
|
|
95 }
|
|
|
96
|
|
|
97
|
|
|
98 static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) {
|
|
|
99 HANDLE current_process;
|
|
|
100
|
|
|
101
|
|
|
102 /* _get_osfhandle will sometimes return -2 in case of an error. This seems to
|
|
|
103 * happen when fd <= 2 and the process' corresponding stdio handle is set to
|
|
|
104 * NULL. Unfortunately DuplicateHandle will happily duplicate (HANDLE) -2, so
|
|
|
105 * this situation goes unnoticed until someone tries to use the duplicate.
|
|
|
106 * Therefore we filter out known-invalid handles here. */
|
|
|
107 if (handle == INVALID_HANDLE_VALUE ||
|
|
|
108 handle == NULL ||
|
|
|
109 handle == (HANDLE) -2) {
|
|
|
110 *dup = INVALID_HANDLE_VALUE;
|
|
|
111 return ERROR_INVALID_HANDLE;
|
|
|
112 }
|
|
|
113
|
|
|
114 current_process = GetCurrentProcess();
|
|
|
115
|
|
|
116 if (!DuplicateHandle(current_process,
|
|
|
117 handle,
|
|
|
118 current_process,
|
|
|
119 dup,
|
|
|
120 0,
|
|
|
121 TRUE,
|
|
|
122 DUPLICATE_SAME_ACCESS)) {
|
|
|
123 *dup = INVALID_HANDLE_VALUE;
|
|
|
124 return GetLastError();
|
|
|
125 }
|
|
|
126
|
|
|
127 return 0;
|
|
|
128 }
|
|
|
129
|
|
|
130
|
|
|
131 static int uv__duplicate_fd(uv_loop_t* loop, int fd, HANDLE* dup) {
|
|
|
132 HANDLE handle;
|
|
|
133
|
|
|
134 if (fd == -1) {
|
|
|
135 *dup = INVALID_HANDLE_VALUE;
|
|
|
136 return ERROR_INVALID_HANDLE;
|
|
|
137 }
|
|
|
138
|
|
|
139 handle = uv__get_osfhandle(fd);
|
|
|
140 return uv__duplicate_handle(loop, handle, dup);
|
|
|
141 }
|
|
|
142
|
|
|
143
|
|
|
144 int uv__create_nul_handle(HANDLE* handle_ptr,
|
|
|
145 DWORD access) {
|
|
|
146 HANDLE handle;
|
|
|
147 SECURITY_ATTRIBUTES sa;
|
|
|
148
|
|
|
149 sa.nLength = sizeof sa;
|
|
|
150 sa.lpSecurityDescriptor = NULL;
|
|
|
151 sa.bInheritHandle = TRUE;
|
|
|
152
|
|
|
153 handle = CreateFileW(L"NUL",
|
|
|
154 access,
|
|
|
155 FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
156 &sa,
|
|
|
157 OPEN_EXISTING,
|
|
|
158 0,
|
|
|
159 NULL);
|
|
|
160 if (handle == INVALID_HANDLE_VALUE) {
|
|
|
161 return GetLastError();
|
|
|
162 }
|
|
|
163
|
|
|
164 *handle_ptr = handle;
|
|
|
165 return 0;
|
|
|
166 }
|
|
|
167
|
|
|
168
|
|
|
169 int uv__stdio_create(uv_loop_t* loop,
|
|
|
170 const uv_process_options_t* options,
|
|
|
171 BYTE** buffer_ptr) {
|
|
|
172 BYTE* buffer;
|
|
|
173 int count, i;
|
|
|
174 int err;
|
|
|
175
|
|
|
176 count = options->stdio_count;
|
|
|
177
|
|
|
178 if (count < 0 || count > 255) {
|
|
|
179 /* Only support FDs 0-255 */
|
|
|
180 return ERROR_NOT_SUPPORTED;
|
|
|
181 } else if (count < 3) {
|
|
|
182 /* There should always be at least 3 stdio handles. */
|
|
|
183 count = 3;
|
|
|
184 }
|
|
|
185
|
|
|
186 /* Allocate the child stdio buffer */
|
|
|
187 buffer = (BYTE*) uv__malloc(CHILD_STDIO_SIZE(count));
|
|
|
188 if (buffer == NULL) {
|
|
|
189 return ERROR_OUTOFMEMORY;
|
|
|
190 }
|
|
|
191
|
|
|
192 /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can clean
|
|
|
193 * up on failure. */
|
|
|
194 CHILD_STDIO_COUNT(buffer) = count;
|
|
|
195 for (i = 0; i < count; i++) {
|
|
|
196 CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
|
|
|
197 memset(CHILD_STDIO_HANDLE(buffer, i), 0xFF, sizeof(HANDLE));
|
|
|
198 }
|
|
|
199
|
|
|
200 for (i = 0; i < count; i++) {
|
|
|
201 uv_stdio_container_t fdopt;
|
|
|
202 if (i < options->stdio_count) {
|
|
|
203 fdopt = options->stdio[i];
|
|
|
204 } else {
|
|
|
205 fdopt.flags = UV_IGNORE;
|
|
|
206 }
|
|
|
207
|
|
|
208 switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
|
|
|
209 UV_INHERIT_STREAM)) {
|
|
|
210 case UV_IGNORE:
|
|
|
211 /* Starting a process with no stdin/stout/stderr can confuse it. So no
|
|
|
212 * matter what the user specified, we make sure the first three FDs are
|
|
|
213 * always open in their typical modes, e. g. stdin be readable and
|
|
|
214 * stdout/err should be writable. For FDs > 2, don't do anything - all
|
|
|
215 * handles in the stdio buffer are initialized with.
|
|
|
216 * INVALID_HANDLE_VALUE, which should be okay. */
|
|
|
217 if (i <= 2) {
|
|
|
218 HANDLE nul;
|
|
|
219 DWORD access = (i == 0) ? FILE_GENERIC_READ :
|
|
|
220 FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
|
|
221
|
|
|
222 err = uv__create_nul_handle(&nul, access);
|
|
|
223 if (err)
|
|
|
224 goto error;
|
|
|
225
|
|
|
226 memcpy(CHILD_STDIO_HANDLE(buffer, i), &nul, sizeof(HANDLE));
|
|
|
227 CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
|
|
|
228 }
|
|
|
229 break;
|
|
|
230
|
|
|
231 case UV_CREATE_PIPE: {
|
|
|
232 /* Create a pair of two connected pipe ends; one end is turned into an
|
|
|
233 * uv_pipe_t for use by the parent. The other one is given to the
|
|
|
234 * child. */
|
|
|
235 uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream;
|
|
|
236 HANDLE child_pipe = INVALID_HANDLE_VALUE;
|
|
|
237
|
|
|
238 /* Create a new, connected pipe pair. stdio[i]. stream should point to
|
|
|
239 * an uninitialized, but not connected pipe handle. */
|
|
|
240 assert(fdopt.data.stream->type == UV_NAMED_PIPE);
|
|
|
241 assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION));
|
|
|
242 assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER));
|
|
|
243
|
|
|
244 err = uv__create_stdio_pipe_pair(loop,
|
|
|
245 parent_pipe,
|
|
|
246 &child_pipe,
|
|
|
247 fdopt.flags);
|
|
|
248 if (err)
|
|
|
249 goto error;
|
|
|
250
|
|
|
251 memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_pipe, sizeof(HANDLE));
|
|
|
252 CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
|
|
|
253 break;
|
|
|
254 }
|
|
|
255
|
|
|
256 case UV_INHERIT_FD: {
|
|
|
257 /* Inherit a raw FD. */
|
|
|
258 HANDLE child_handle;
|
|
|
259
|
|
|
260 /* Make an inheritable duplicate of the handle. */
|
|
|
261 err = uv__duplicate_fd(loop, fdopt.data.fd, &child_handle);
|
|
|
262 if (err) {
|
|
|
263 /* If fdopt. data. fd is not valid and fd <= 2, then ignore the
|
|
|
264 * error. */
|
|
|
265 if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
|
|
|
266 CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
|
|
|
267 memset(CHILD_STDIO_HANDLE(buffer, i), 0xFF, sizeof(HANDLE));
|
|
|
268 break;
|
|
|
269 }
|
|
|
270 goto error;
|
|
|
271 }
|
|
|
272
|
|
|
273 /* Figure out what the type is. */
|
|
|
274 switch (GetFileType(child_handle)) {
|
|
|
275 case FILE_TYPE_DISK:
|
|
|
276 CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN;
|
|
|
277 break;
|
|
|
278
|
|
|
279 case FILE_TYPE_PIPE:
|
|
|
280 CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
|
|
|
281 break;
|
|
|
282
|
|
|
283 case FILE_TYPE_CHAR:
|
|
|
284 case FILE_TYPE_REMOTE:
|
|
|
285 CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
|
|
|
286 break;
|
|
|
287
|
|
|
288 case FILE_TYPE_UNKNOWN:
|
|
|
289 if (GetLastError() != 0) {
|
|
|
290 err = GetLastError();
|
|
|
291 CloseHandle(child_handle);
|
|
|
292 goto error;
|
|
|
293 }
|
|
|
294 CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
|
|
|
295 break;
|
|
|
296
|
|
|
297 default:
|
|
|
298 assert(0);
|
|
|
299 return -1;
|
|
|
300 }
|
|
|
301
|
|
|
302 memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE));
|
|
|
303 break;
|
|
|
304 }
|
|
|
305
|
|
|
306 case UV_INHERIT_STREAM: {
|
|
|
307 /* Use an existing stream as the stdio handle for the child. */
|
|
|
308 HANDLE stream_handle, child_handle;
|
|
|
309 unsigned char crt_flags;
|
|
|
310 uv_stream_t* stream = fdopt.data.stream;
|
|
|
311
|
|
|
312 /* Leech the handle out of the stream. */
|
|
|
313 if (stream->type == UV_TTY) {
|
|
|
314 stream_handle = ((uv_tty_t*) stream)->handle;
|
|
|
315 crt_flags = FOPEN | FDEV;
|
|
|
316 } else if (stream->type == UV_NAMED_PIPE &&
|
|
|
317 stream->flags & UV_HANDLE_CONNECTION) {
|
|
|
318 stream_handle = ((uv_pipe_t*) stream)->handle;
|
|
|
319 crt_flags = FOPEN | FPIPE;
|
|
|
320 } else {
|
|
|
321 stream_handle = INVALID_HANDLE_VALUE;
|
|
|
322 crt_flags = 0;
|
|
|
323 }
|
|
|
324
|
|
|
325 if (stream_handle == NULL ||
|
|
|
326 stream_handle == INVALID_HANDLE_VALUE) {
|
|
|
327 /* The handle is already closed, or not yet created, or the stream
|
|
|
328 * type is not supported. */
|
|
|
329 err = ERROR_NOT_SUPPORTED;
|
|
|
330 goto error;
|
|
|
331 }
|
|
|
332
|
|
|
333 /* Make an inheritable copy of the handle. */
|
|
|
334 err = uv__duplicate_handle(loop, stream_handle, &child_handle);
|
|
|
335 if (err)
|
|
|
336 goto error;
|
|
|
337
|
|
|
338 memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE));
|
|
|
339 CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
|
|
|
340 break;
|
|
|
341 }
|
|
|
342
|
|
|
343 default:
|
|
|
344 assert(0);
|
|
|
345 return -1;
|
|
|
346 }
|
|
|
347 }
|
|
|
348
|
|
|
349 *buffer_ptr = buffer;
|
|
|
350 return 0;
|
|
|
351
|
|
|
352 error:
|
|
|
353 uv__stdio_destroy(buffer);
|
|
|
354 return err;
|
|
|
355 }
|
|
|
356
|
|
|
357
|
|
|
358 void uv__stdio_destroy(BYTE* buffer) {
|
|
|
359 int i, count;
|
|
|
360
|
|
|
361 count = CHILD_STDIO_COUNT(buffer);
|
|
|
362 for (i = 0; i < count; i++) {
|
|
|
363 HANDLE handle = uv__stdio_handle(buffer, i);
|
|
|
364 if (handle != INVALID_HANDLE_VALUE) {
|
|
|
365 CloseHandle(handle);
|
|
|
366 }
|
|
|
367 }
|
|
|
368
|
|
|
369 uv__free(buffer);
|
|
|
370 }
|
|
|
371
|
|
|
372
|
|
|
373 void uv__stdio_noinherit(BYTE* buffer) {
|
|
|
374 int i, count;
|
|
|
375
|
|
|
376 count = CHILD_STDIO_COUNT(buffer);
|
|
|
377 for (i = 0; i < count; i++) {
|
|
|
378 HANDLE handle = uv__stdio_handle(buffer, i);
|
|
|
379 if (handle != INVALID_HANDLE_VALUE) {
|
|
|
380 SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
|
|
381 }
|
|
|
382 }
|
|
|
383 }
|
|
|
384
|
|
|
385
|
|
|
386 int uv__stdio_verify(BYTE* buffer, WORD size) {
|
|
|
387 unsigned int count;
|
|
|
388
|
|
|
389 /* Check the buffer pointer. */
|
|
|
390 if (buffer == NULL)
|
|
|
391 return 0;
|
|
|
392
|
|
|
393 /* Verify that the buffer is at least big enough to hold the count. */
|
|
|
394 if (size < CHILD_STDIO_SIZE(0))
|
|
|
395 return 0;
|
|
|
396
|
|
|
397 /* Verify if the count is within range. */
|
|
|
398 count = CHILD_STDIO_COUNT(buffer);
|
|
|
399 if (count > 256)
|
|
|
400 return 0;
|
|
|
401
|
|
|
402 /* Verify that the buffer size is big enough to hold info for N FDs. */
|
|
|
403 if (size < CHILD_STDIO_SIZE(count))
|
|
|
404 return 0;
|
|
|
405
|
|
|
406 return 1;
|
|
|
407 }
|
|
|
408
|
|
|
409
|
|
|
410 WORD uv__stdio_size(BYTE* buffer) {
|
|
|
411 return (WORD) CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer)));
|
|
|
412 }
|
|
|
413
|
|
|
414
|
|
|
415 HANDLE uv__stdio_handle(BYTE* buffer, int fd) {
|
|
|
416 HANDLE handle;
|
|
|
417 memcpy(&handle, CHILD_STDIO_HANDLE(buffer, fd), sizeof(HANDLE));
|
|
|
418 return handle;
|
|
|
419 }
|