Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmlibuv/src/win/process-stdio.c
3153 views
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
*((HANDLE*) ((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
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
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
DWORD access = (i == 0) ? FILE_GENERIC_READ :
219
FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
220
221
err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i),
222
access);
223
if (err)
224
goto error;
225
226
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
227
}
228
break;
229
230
case UV_CREATE_PIPE: {
231
/* Create a pair of two connected pipe ends; one end is turned into an
232
* uv_pipe_t for use by the parent. The other one is given to the
233
* child. */
234
uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream;
235
HANDLE child_pipe = INVALID_HANDLE_VALUE;
236
237
/* Create a new, connected pipe pair. stdio[i]. stream should point to
238
* an uninitialized, but not connected pipe handle. */
239
assert(fdopt.data.stream->type == UV_NAMED_PIPE);
240
assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION));
241
assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER));
242
243
err = uv__create_stdio_pipe_pair(loop,
244
parent_pipe,
245
&child_pipe,
246
fdopt.flags);
247
if (err)
248
goto error;
249
250
CHILD_STDIO_HANDLE(buffer, i) = child_pipe;
251
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
252
break;
253
}
254
255
case UV_INHERIT_FD: {
256
/* Inherit a raw FD. */
257
HANDLE child_handle;
258
259
/* Make an inheritable duplicate of the handle. */
260
err = uv__duplicate_fd(loop, fdopt.data.fd, &child_handle);
261
if (err) {
262
/* If fdopt. data. fd is not valid and fd <= 2, then ignore the
263
* error. */
264
if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
265
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
266
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
267
break;
268
}
269
goto error;
270
}
271
272
/* Figure out what the type is. */
273
switch (GetFileType(child_handle)) {
274
case FILE_TYPE_DISK:
275
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN;
276
break;
277
278
case FILE_TYPE_PIPE:
279
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
280
break;
281
282
case FILE_TYPE_CHAR:
283
case FILE_TYPE_REMOTE:
284
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
285
break;
286
287
case FILE_TYPE_UNKNOWN:
288
if (GetLastError() != 0) {
289
err = GetLastError();
290
CloseHandle(child_handle);
291
goto error;
292
}
293
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
294
break;
295
296
default:
297
assert(0);
298
return -1;
299
}
300
301
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
302
break;
303
}
304
305
case UV_INHERIT_STREAM: {
306
/* Use an existing stream as the stdio handle for the child. */
307
HANDLE stream_handle, child_handle;
308
unsigned char crt_flags;
309
uv_stream_t* stream = fdopt.data.stream;
310
311
/* Leech the handle out of the stream. */
312
if (stream->type == UV_TTY) {
313
stream_handle = ((uv_tty_t*) stream)->handle;
314
crt_flags = FOPEN | FDEV;
315
} else if (stream->type == UV_NAMED_PIPE &&
316
stream->flags & UV_HANDLE_CONNECTION) {
317
stream_handle = ((uv_pipe_t*) stream)->handle;
318
crt_flags = FOPEN | FPIPE;
319
} else {
320
stream_handle = INVALID_HANDLE_VALUE;
321
crt_flags = 0;
322
}
323
324
if (stream_handle == NULL ||
325
stream_handle == INVALID_HANDLE_VALUE) {
326
/* The handle is already closed, or not yet created, or the stream
327
* type is not supported. */
328
err = ERROR_NOT_SUPPORTED;
329
goto error;
330
}
331
332
/* Make an inheritable copy of the handle. */
333
err = uv__duplicate_handle(loop, stream_handle, &child_handle);
334
if (err)
335
goto error;
336
337
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
338
CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
339
break;
340
}
341
342
default:
343
assert(0);
344
return -1;
345
}
346
}
347
348
*buffer_ptr = buffer;
349
return 0;
350
351
error:
352
uv__stdio_destroy(buffer);
353
return err;
354
}
355
356
357
void uv__stdio_destroy(BYTE* buffer) {
358
int i, count;
359
360
count = CHILD_STDIO_COUNT(buffer);
361
for (i = 0; i < count; i++) {
362
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
363
if (handle != INVALID_HANDLE_VALUE) {
364
CloseHandle(handle);
365
}
366
}
367
368
uv__free(buffer);
369
}
370
371
372
void uv__stdio_noinherit(BYTE* buffer) {
373
int i, count;
374
375
count = CHILD_STDIO_COUNT(buffer);
376
for (i = 0; i < count; i++) {
377
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
378
if (handle != INVALID_HANDLE_VALUE) {
379
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
380
}
381
}
382
}
383
384
385
int uv__stdio_verify(BYTE* buffer, WORD size) {
386
unsigned int count;
387
388
/* Check the buffer pointer. */
389
if (buffer == NULL)
390
return 0;
391
392
/* Verify that the buffer is at least big enough to hold the count. */
393
if (size < CHILD_STDIO_SIZE(0))
394
return 0;
395
396
/* Verify if the count is within range. */
397
count = CHILD_STDIO_COUNT(buffer);
398
if (count > 256)
399
return 0;
400
401
/* Verify that the buffer size is big enough to hold info for N FDs. */
402
if (size < CHILD_STDIO_SIZE(count))
403
return 0;
404
405
return 1;
406
}
407
408
409
WORD uv__stdio_size(BYTE* buffer) {
410
return (WORD) CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer)));
411
}
412
413
414
HANDLE uv__stdio_handle(BYTE* buffer, int fd) {
415
return CHILD_STDIO_HANDLE(buffer, fd);
416
}
417
418