Mercurial
comparison third_party/libuv/src/win/process-stdio.c @ 160:948de3f54cea
[ThirdParty] Added libuv
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 14 Jan 2026 19:39:52 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 159:05cf9467a1c3 | 160:948de3f54cea |
|---|---|
| 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 } |