subprocess.h (35891B)
1 /* 2 The latest version of this library is available on GitHub; 3 https://github.com/sheredom/subprocess.h 4 */ 5 6 /* 7 This is free and unencumbered software released into the public domain. 8 9 Anyone is free to copy, modify, publish, use, compile, sell, or 10 distribute this software, either in source code form or as a compiled 11 binary, for any purpose, commercial or non-commercial, and by any 12 means. 13 14 In jurisdictions that recognize copyright laws, the author or authors 15 of this software dedicate any and all copyright interest in the 16 software to the public domain. We make this dedication for the benefit 17 of the public at large and to the detriment of our heirs and 18 successors. We intend this dedication to be an overt act of 19 relinquishment in perpetuity of all present and future rights to this 20 software under copyright law. 21 22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 26 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 27 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 OTHER DEALINGS IN THE SOFTWARE. 29 30 For more information, please refer to <http://unlicense.org/> 31 */ 32 33 #ifndef SHEREDOM_SUBPROCESS_H_INCLUDED 34 #define SHEREDOM_SUBPROCESS_H_INCLUDED 35 36 #if defined(_MSC_VER) 37 #pragma warning(push, 1) 38 39 /* disable warning: '__cplusplus' is not defined as a preprocessor macro, 40 * replacing with '0' for '#if/#elif' */ 41 #pragma warning(disable : 4668) 42 #endif 43 44 #include <stdio.h> 45 #include <string.h> 46 47 #if defined(_MSC_VER) 48 #pragma warning(pop) 49 #endif 50 51 #if defined(__TINYC__) 52 #define subprocess_attribute(a) __attribute((a)) 53 #else 54 #define subprocess_attribute(a) __attribute__((a)) 55 #endif 56 57 #if defined(_MSC_VER) 58 #define subprocess_pure 59 #define subprocess_weak __inline 60 #define subprocess_tls __declspec(thread) 61 #elif defined(__MINGW32__) 62 #define subprocess_pure subprocess_attribute(pure) 63 #define subprocess_weak static subprocess_attribute(used) 64 #define subprocess_tls __thread 65 #elif defined(__clang__) || defined(__GNUC__) || defined(__TINYC__) 66 #define subprocess_pure subprocess_attribute(pure) 67 #define subprocess_weak subprocess_attribute(weak) 68 #define subprocess_tls __thread 69 #else 70 #error Non clang, non gcc, non MSVC, non tcc compiler found! 71 #endif 72 73 struct subprocess_s; 74 75 enum subprocess_option_e { 76 // stdout and stderr are the same FILE. 77 subprocess_option_combined_stdout_stderr = 0x1, 78 79 // The child process should inherit the environment variables of the parent. 80 subprocess_option_inherit_environment = 0x2, 81 82 // Enable asynchronous reading of stdout/stderr before it has completed. 83 subprocess_option_enable_async = 0x4, 84 85 // Enable the child process to be spawned with no window visible if supported 86 // by the platform. 87 subprocess_option_no_window = 0x8, 88 89 // Search for program names in the PATH variable. Always enabled on Windows. 90 // Note: this will **not** search for paths in any provided custom environment 91 // and instead uses the PATH of the spawning process. 92 subprocess_option_search_user_path = 0x10 93 }; 94 95 #if defined(__cplusplus) 96 extern "C" { 97 #endif 98 99 /// @brief Create a process. 100 /// @param command_line An array of strings for the command line to execute for 101 /// this process. The last element must be NULL to signify the end of the array. 102 /// The memory backing this parameter only needs to persist until this function 103 /// returns. 104 /// @param options A bit field of subprocess_option_e's to pass. 105 /// @param out_process The newly created process. 106 /// @return On success zero is returned. 107 subprocess_weak int subprocess_create(const char *const command_line[], 108 int options, 109 struct subprocess_s *const out_process); 110 111 /// @brief Create a process (extended create). 112 /// @param command_line An array of strings for the command line to execute for 113 /// this process. The last element must be NULL to signify the end of the array. 114 /// The memory backing this parameter only needs to persist until this function 115 /// returns. 116 /// @param options A bit field of subprocess_option_e's to pass. 117 /// @param environment An optional array of strings for the environment to use 118 /// for a child process (each element of the form FOO=BAR). The last element 119 /// must be NULL to signify the end of the array. 120 /// @param out_process The newly created process. 121 /// @return On success zero is returned. 122 /// 123 /// If `options` contains `subprocess_option_inherit_environment`, then 124 /// `environment` must be NULL. 125 subprocess_weak int 126 subprocess_create_ex(const char *const command_line[], int options, 127 const char *const environment[], 128 struct subprocess_s *const out_process); 129 130 /// @brief Get the standard input file for a process. 131 /// @param process The process to query. 132 /// @return The file for standard input of the process. 133 /// 134 /// The file returned can be written to by the parent process to feed data to 135 /// the standard input of the process. 136 subprocess_pure subprocess_weak FILE * 137 subprocess_stdin(const struct subprocess_s *const process); 138 139 /// @brief Get the standard output file for a process. 140 /// @param process The process to query. 141 /// @return The file for standard output of the process. 142 /// 143 /// The file returned can be read from by the parent process to read data from 144 /// the standard output of the child process. 145 subprocess_pure subprocess_weak FILE * 146 subprocess_stdout(const struct subprocess_s *const process); 147 148 /// @brief Get the standard error file for a process. 149 /// @param process The process to query. 150 /// @return The file for standard error of the process. 151 /// 152 /// The file returned can be read from by the parent process to read data from 153 /// the standard error of the child process. 154 /// 155 /// If the process was created with the subprocess_option_combined_stdout_stderr 156 /// option bit set, this function will return NULL, and the subprocess_stdout 157 /// function should be used for both the standard output and error combined. 158 subprocess_pure subprocess_weak FILE * 159 subprocess_stderr(const struct subprocess_s *const process); 160 161 /// @brief Wait for a process to finish execution. 162 /// @param process The process to wait for. 163 /// @param out_return_code The return code of the returned process (can be 164 /// NULL). 165 /// @return On success zero is returned. 166 /// 167 /// Joining a process will close the stdin pipe to the process. 168 subprocess_weak int subprocess_join(struct subprocess_s *const process, 169 int *const out_return_code); 170 171 /// @brief Destroy a previously created process. 172 /// @param process The process to destroy. 173 /// @return On success zero is returned. 174 /// 175 /// If the process to be destroyed had not finished execution, it may out live 176 /// the parent process. 177 subprocess_weak int subprocess_destroy(struct subprocess_s *const process); 178 179 /// @brief Terminate a previously created process. 180 /// @param process The process to terminate. 181 /// @return On success zero is returned. 182 /// 183 /// If the process to be destroyed had not finished execution, it will be 184 /// terminated (i.e killed). 185 subprocess_weak int subprocess_terminate(struct subprocess_s *const process); 186 187 /// @brief Read the standard output from the child process. 188 /// @param process The process to read from. 189 /// @param buffer The buffer to read into. 190 /// @param size The maximum number of bytes to read. 191 /// @return The number of bytes actually read into buffer. Can only be 0 if the 192 /// process has complete. 193 /// 194 /// The only safe way to read from the standard output of a process during it's 195 /// execution is to use the `subprocess_option_enable_async` option in 196 /// conjuction with this method. 197 subprocess_weak unsigned 198 subprocess_read_stdout(struct subprocess_s *const process, char *const buffer, 199 unsigned size); 200 201 /// @brief Read the standard error from the child process. 202 /// @param process The process to read from. 203 /// @param buffer The buffer to read into. 204 /// @param size The maximum number of bytes to read. 205 /// @return The number of bytes actually read into buffer. Can only be 0 if the 206 /// process has complete. 207 /// 208 /// The only safe way to read from the standard error of a process during it's 209 /// execution is to use the `subprocess_option_enable_async` option in 210 /// conjuction with this method. 211 subprocess_weak unsigned 212 subprocess_read_stderr(struct subprocess_s *const process, char *const buffer, 213 unsigned size); 214 215 /// @brief Returns if the subprocess is currently still alive and executing. 216 /// @param process The process to check. 217 /// @return If the process is still alive non-zero is returned. 218 subprocess_weak int subprocess_alive(struct subprocess_s *const process); 219 220 #if defined(__cplusplus) 221 #define SUBPROCESS_CAST(type, x) static_cast<type>(x) 222 #define SUBPROCESS_PTR_CAST(type, x) reinterpret_cast<type>(x) 223 #define SUBPROCESS_CONST_CAST(type, x) const_cast<type>(x) 224 #define SUBPROCESS_NULL NULL 225 #else 226 #define SUBPROCESS_CAST(type, x) ((type)(x)) 227 #define SUBPROCESS_PTR_CAST(type, x) ((type)(x)) 228 #define SUBPROCESS_CONST_CAST(type, x) ((type)(x)) 229 #define SUBPROCESS_NULL 0 230 #endif 231 232 #if !defined(_WIN32) 233 #include <signal.h> 234 #include <spawn.h> 235 #include <stdlib.h> 236 #include <sys/types.h> 237 #include <sys/wait.h> 238 #include <unistd.h> 239 #endif 240 241 #if defined(_WIN32) 242 243 #if (_MSC_VER < 1920) 244 #ifdef _WIN64 245 typedef __int64 subprocess_intptr_t; 246 typedef unsigned __int64 subprocess_size_t; 247 #else 248 typedef int subprocess_intptr_t; 249 typedef unsigned int subprocess_size_t; 250 #endif 251 #else 252 #include <inttypes.h> 253 254 typedef intptr_t subprocess_intptr_t; 255 typedef size_t subprocess_size_t; 256 #endif 257 258 #ifdef __clang__ 259 #pragma clang diagnostic push 260 #pragma clang diagnostic ignored "-Wreserved-identifier" 261 #endif 262 263 typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION; 264 typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES; 265 typedef struct _STARTUPINFOA *LPSTARTUPINFOA; 266 typedef struct _OVERLAPPED *LPOVERLAPPED; 267 268 #ifdef __clang__ 269 #pragma clang diagnostic pop 270 #endif 271 272 #ifdef _MSC_VER 273 #pragma warning(push, 1) 274 #endif 275 #ifdef __MINGW32__ 276 #pragma GCC diagnostic push 277 #pragma GCC diagnostic ignored "-Wpedantic" 278 #endif 279 280 struct subprocess_subprocess_information_s { 281 void *hProcess; 282 void *hThread; 283 unsigned long dwProcessId; 284 unsigned long dwThreadId; 285 }; 286 287 struct subprocess_security_attributes_s { 288 unsigned long nLength; 289 void *lpSecurityDescriptor; 290 int bInheritHandle; 291 }; 292 293 struct subprocess_startup_info_s { 294 unsigned long cb; 295 char *lpReserved; 296 char *lpDesktop; 297 char *lpTitle; 298 unsigned long dwX; 299 unsigned long dwY; 300 unsigned long dwXSize; 301 unsigned long dwYSize; 302 unsigned long dwXCountChars; 303 unsigned long dwYCountChars; 304 unsigned long dwFillAttribute; 305 unsigned long dwFlags; 306 unsigned short wShowWindow; 307 unsigned short cbReserved2; 308 unsigned char *lpReserved2; 309 void *hStdInput; 310 void *hStdOutput; 311 void *hStdError; 312 }; 313 314 struct subprocess_overlapped_s { 315 uintptr_t Internal; 316 uintptr_t InternalHigh; 317 union { 318 struct { 319 unsigned long Offset; 320 unsigned long OffsetHigh; 321 } DUMMYSTRUCTNAME; 322 void *Pointer; 323 } DUMMYUNIONNAME; 324 325 void *hEvent; 326 }; 327 328 #ifdef __MINGW32__ 329 #pragma GCC diagnostic pop 330 #endif 331 #ifdef _MSC_VER 332 #pragma warning(pop) 333 #endif 334 335 __declspec(dllimport) unsigned long __stdcall GetLastError(void); 336 __declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long, 337 unsigned long); 338 __declspec(dllimport) int __stdcall CreatePipe(void **, void **, 339 LPSECURITY_ATTRIBUTES, 340 unsigned long); 341 __declspec(dllimport) void *__stdcall CreateNamedPipeA( 342 const char *, unsigned long, unsigned long, unsigned long, unsigned long, 343 unsigned long, unsigned long, LPSECURITY_ATTRIBUTES); 344 __declspec(dllimport) int __stdcall ReadFile(void *, void *, unsigned long, 345 unsigned long *, LPOVERLAPPED); 346 __declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(void); 347 __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void); 348 __declspec(dllimport) void *__stdcall CreateFileA(const char *, unsigned long, 349 unsigned long, 350 LPSECURITY_ATTRIBUTES, 351 unsigned long, unsigned long, 352 void *); 353 __declspec(dllimport) void *__stdcall CreateEventA(LPSECURITY_ATTRIBUTES, int, 354 int, const char *); 355 __declspec(dllimport) int __stdcall CreateProcessA( 356 const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int, 357 unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION); 358 __declspec(dllimport) int __stdcall CloseHandle(void *); 359 __declspec(dllimport) unsigned long __stdcall WaitForSingleObject( 360 void *, unsigned long); 361 __declspec(dllimport) int __stdcall GetExitCodeProcess( 362 void *, unsigned long *lpExitCode); 363 __declspec(dllimport) int __stdcall TerminateProcess(void *, unsigned int); 364 __declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects( 365 unsigned long, void *const *, int, unsigned long); 366 __declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED, 367 unsigned long *, int); 368 369 #if defined(_DLL) 370 #define SUBPROCESS_DLLIMPORT __declspec(dllimport) 371 #else 372 #define SUBPROCESS_DLLIMPORT 373 #endif 374 375 #ifdef __clang__ 376 #pragma clang diagnostic push 377 #pragma clang diagnostic ignored "-Wreserved-identifier" 378 #endif 379 380 SUBPROCESS_DLLIMPORT int __cdecl _fileno(FILE *); 381 SUBPROCESS_DLLIMPORT int __cdecl _open_osfhandle(subprocess_intptr_t, int); 382 SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl _get_osfhandle(int); 383 384 #ifndef __MINGW32__ 385 void *__cdecl _alloca(subprocess_size_t); 386 #else 387 #include <malloc.h> 388 #endif 389 390 #ifdef __clang__ 391 #pragma clang diagnostic pop 392 #endif 393 394 #else 395 typedef size_t subprocess_size_t; 396 #endif 397 398 #ifdef __clang__ 399 #pragma clang diagnostic push 400 #pragma clang diagnostic ignored "-Wpadded" 401 #endif 402 struct subprocess_s { 403 FILE *stdin_file; 404 FILE *stdout_file; 405 FILE *stderr_file; 406 407 #if defined(_WIN32) 408 void *hProcess; 409 void *hStdInput; 410 void *hEventOutput; 411 void *hEventError; 412 #else 413 pid_t child; 414 int return_status; 415 #endif 416 417 subprocess_size_t alive; 418 }; 419 #ifdef __clang__ 420 #pragma clang diagnostic pop 421 #endif 422 423 #if defined(_WIN32) 424 subprocess_weak int subprocess_create_named_pipe_helper(void **rd, void **wr); 425 int subprocess_create_named_pipe_helper(void **rd, void **wr) { 426 const unsigned long pipeAccessInbound = 0x00000001; 427 const unsigned long fileFlagOverlapped = 0x40000000; 428 const unsigned long pipeTypeByte = 0x00000000; 429 const unsigned long pipeWait = 0x00000000; 430 const unsigned long genericWrite = 0x40000000; 431 const unsigned long openExisting = 3; 432 const unsigned long fileAttributeNormal = 0x00000080; 433 const void *const invalidHandleValue = 434 SUBPROCESS_PTR_CAST(void *, ~(SUBPROCESS_CAST(subprocess_intptr_t, 0))); 435 struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), 436 SUBPROCESS_NULL, 1}; 437 char name[256] = {0}; 438 static subprocess_tls long index = 0; 439 const long unique = index++; 440 441 #if defined(_MSC_VER) && _MSC_VER < 1900 442 #pragma warning(push, 1) 443 #pragma warning(disable : 4996) 444 _snprintf(name, sizeof(name) - 1, 445 "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld", 446 GetCurrentProcessId(), GetCurrentThreadId(), unique); 447 #pragma warning(pop) 448 #else 449 snprintf(name, sizeof(name) - 1, 450 "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld", 451 GetCurrentProcessId(), GetCurrentThreadId(), unique); 452 #endif 453 454 *rd = 455 CreateNamedPipeA(name, pipeAccessInbound | fileFlagOverlapped, 456 pipeTypeByte | pipeWait, 1, 4096, 4096, SUBPROCESS_NULL, 457 SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr)); 458 459 if (invalidHandleValue == *rd) { 460 return -1; 461 } 462 463 *wr = CreateFileA(name, genericWrite, SUBPROCESS_NULL, 464 SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 465 openExisting, fileAttributeNormal, SUBPROCESS_NULL); 466 467 if (invalidHandleValue == *wr) { 468 return -1; 469 } 470 471 return 0; 472 } 473 #endif 474 475 int subprocess_create(const char *const commandLine[], int options, 476 struct subprocess_s *const out_process) { 477 return subprocess_create_ex(commandLine, options, SUBPROCESS_NULL, 478 out_process); 479 } 480 481 int subprocess_create_ex(const char *const commandLine[], int options, 482 const char *const environment[], 483 struct subprocess_s *const out_process) { 484 #if defined(_WIN32) 485 int fd; 486 void *rd, *wr; 487 char *commandLineCombined; 488 subprocess_size_t len; 489 int i, j; 490 int need_quoting; 491 unsigned long flags = 0; 492 const unsigned long startFUseStdHandles = 0x00000100; 493 const unsigned long handleFlagInherit = 0x00000001; 494 const unsigned long createNoWindow = 0x08000000; 495 struct subprocess_subprocess_information_s processInfo; 496 struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), 497 SUBPROCESS_NULL, 1}; 498 char *used_environment = SUBPROCESS_NULL; 499 struct subprocess_startup_info_s startInfo = {0, 500 SUBPROCESS_NULL, 501 SUBPROCESS_NULL, 502 SUBPROCESS_NULL, 503 0, 504 0, 505 0, 506 0, 507 0, 508 0, 509 0, 510 0, 511 0, 512 0, 513 SUBPROCESS_NULL, 514 SUBPROCESS_NULL, 515 SUBPROCESS_NULL, 516 SUBPROCESS_NULL}; 517 518 startInfo.cb = sizeof(startInfo); 519 startInfo.dwFlags = startFUseStdHandles; 520 521 if (subprocess_option_no_window == (options & subprocess_option_no_window)) { 522 flags |= createNoWindow; 523 } 524 525 if (subprocess_option_inherit_environment != 526 (options & subprocess_option_inherit_environment)) { 527 if (SUBPROCESS_NULL == environment) { 528 used_environment = SUBPROCESS_CONST_CAST(char *, "\0\0"); 529 } else { 530 // We always end with two null terminators. 531 len = 2; 532 533 for (i = 0; environment[i]; i++) { 534 for (j = 0; '\0' != environment[i][j]; j++) { 535 len++; 536 } 537 538 // For the null terminator too. 539 len++; 540 } 541 542 used_environment = SUBPROCESS_CAST(char *, _alloca(len)); 543 544 // Re-use len for the insertion position 545 len = 0; 546 547 for (i = 0; environment[i]; i++) { 548 for (j = 0; '\0' != environment[i][j]; j++) { 549 used_environment[len++] = environment[i][j]; 550 } 551 552 used_environment[len++] = '\0'; 553 } 554 555 // End with the two null terminators. 556 used_environment[len++] = '\0'; 557 used_environment[len++] = '\0'; 558 } 559 } else { 560 if (SUBPROCESS_NULL != environment) { 561 return -1; 562 } 563 } 564 565 if (!CreatePipe(&rd, &wr, SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 566 0)) { 567 return -1; 568 } 569 570 if (!SetHandleInformation(wr, handleFlagInherit, 0)) { 571 return -1; 572 } 573 574 fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, wr), 0); 575 576 if (-1 != fd) { 577 out_process->stdin_file = _fdopen(fd, "wb"); 578 579 if (SUBPROCESS_NULL == out_process->stdin_file) { 580 return -1; 581 } 582 } 583 584 startInfo.hStdInput = rd; 585 586 if (options & subprocess_option_enable_async) { 587 if (subprocess_create_named_pipe_helper(&rd, &wr)) { 588 return -1; 589 } 590 } else { 591 if (!CreatePipe(&rd, &wr, 592 SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) { 593 return -1; 594 } 595 } 596 597 if (!SetHandleInformation(rd, handleFlagInherit, 0)) { 598 return -1; 599 } 600 601 fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0); 602 603 if (-1 != fd) { 604 out_process->stdout_file = _fdopen(fd, "rb"); 605 606 if (SUBPROCESS_NULL == out_process->stdout_file) { 607 return -1; 608 } 609 } 610 611 startInfo.hStdOutput = wr; 612 613 if (subprocess_option_combined_stdout_stderr == 614 (options & subprocess_option_combined_stdout_stderr)) { 615 out_process->stderr_file = out_process->stdout_file; 616 startInfo.hStdError = startInfo.hStdOutput; 617 } else { 618 if (options & subprocess_option_enable_async) { 619 if (subprocess_create_named_pipe_helper(&rd, &wr)) { 620 return -1; 621 } 622 } else { 623 if (!CreatePipe(&rd, &wr, 624 SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) { 625 return -1; 626 } 627 } 628 629 if (!SetHandleInformation(rd, handleFlagInherit, 0)) { 630 return -1; 631 } 632 633 fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0); 634 635 if (-1 != fd) { 636 out_process->stderr_file = _fdopen(fd, "rb"); 637 638 if (SUBPROCESS_NULL == out_process->stderr_file) { 639 return -1; 640 } 641 } 642 643 startInfo.hStdError = wr; 644 } 645 646 if (options & subprocess_option_enable_async) { 647 out_process->hEventOutput = 648 CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1, 649 SUBPROCESS_NULL); 650 out_process->hEventError = 651 CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1, 652 SUBPROCESS_NULL); 653 } else { 654 out_process->hEventOutput = SUBPROCESS_NULL; 655 out_process->hEventError = SUBPROCESS_NULL; 656 } 657 658 // Combine commandLine together into a single string 659 len = 0; 660 for (i = 0; commandLine[i]; i++) { 661 // for the trailing \0 662 len++; 663 664 // Quote the argument if it has a space in it 665 if (strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL) 666 len += 2; 667 668 for (j = 0; '\0' != commandLine[i][j]; j++) { 669 switch (commandLine[i][j]) { 670 default: 671 break; 672 case '\\': 673 if (commandLine[i][j + 1] == '"') { 674 len++; 675 } 676 677 break; 678 case '"': 679 len++; 680 break; 681 } 682 len++; 683 } 684 } 685 686 commandLineCombined = SUBPROCESS_CAST(char *, _alloca(len)); 687 688 if (!commandLineCombined) { 689 return -1; 690 } 691 692 // Gonna re-use len to store the write index into commandLineCombined 693 len = 0; 694 695 for (i = 0; commandLine[i]; i++) { 696 if (0 != i) { 697 commandLineCombined[len++] = ' '; 698 } 699 700 need_quoting = strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL; 701 if (need_quoting) { 702 commandLineCombined[len++] = '"'; 703 } 704 705 for (j = 0; '\0' != commandLine[i][j]; j++) { 706 switch (commandLine[i][j]) { 707 default: 708 break; 709 case '\\': 710 if (commandLine[i][j + 1] == '"') { 711 commandLineCombined[len++] = '\\'; 712 } 713 714 break; 715 case '"': 716 commandLineCombined[len++] = '\\'; 717 break; 718 } 719 720 commandLineCombined[len++] = commandLine[i][j]; 721 } 722 if (need_quoting) { 723 commandLineCombined[len++] = '"'; 724 } 725 } 726 727 commandLineCombined[len] = '\0'; 728 729 if (!CreateProcessA( 730 SUBPROCESS_NULL, 731 commandLineCombined, // command line 732 SUBPROCESS_NULL, // process security attributes 733 SUBPROCESS_NULL, // primary thread security attributes 734 1, // handles are inherited 735 flags, // creation flags 736 used_environment, // used environment 737 SUBPROCESS_NULL, // use parent's current directory 738 SUBPROCESS_PTR_CAST(LPSTARTUPINFOA, 739 &startInfo), // STARTUPINFO pointer 740 SUBPROCESS_PTR_CAST(LPPROCESS_INFORMATION, &processInfo))) { 741 return -1; 742 } 743 744 out_process->hProcess = processInfo.hProcess; 745 746 out_process->hStdInput = startInfo.hStdInput; 747 748 // We don't need the handle of the primary thread in the called process. 749 CloseHandle(processInfo.hThread); 750 751 if (SUBPROCESS_NULL != startInfo.hStdOutput) { 752 CloseHandle(startInfo.hStdOutput); 753 754 if (startInfo.hStdError != startInfo.hStdOutput) { 755 CloseHandle(startInfo.hStdError); 756 } 757 } 758 759 out_process->alive = 1; 760 761 return 0; 762 #else 763 int stdinfd[2]; 764 int stdoutfd[2]; 765 int stderrfd[2]; 766 pid_t child; 767 extern char **environ; 768 char *const empty_environment[1] = {SUBPROCESS_NULL}; 769 posix_spawn_file_actions_t actions; 770 char *const *used_environment; 771 772 if (subprocess_option_inherit_environment == 773 (options & subprocess_option_inherit_environment)) { 774 if (SUBPROCESS_NULL != environment) { 775 return -1; 776 } 777 } 778 779 if (0 != pipe(stdinfd)) { 780 return -1; 781 } 782 783 if (0 != pipe(stdoutfd)) { 784 return -1; 785 } 786 787 if (subprocess_option_combined_stdout_stderr != 788 (options & subprocess_option_combined_stdout_stderr)) { 789 if (0 != pipe(stderrfd)) { 790 return -1; 791 } 792 } 793 794 if (environment) { 795 #ifdef __clang__ 796 #pragma clang diagnostic push 797 #pragma clang diagnostic ignored "-Wcast-qual" 798 #pragma clang diagnostic ignored "-Wold-style-cast" 799 #endif 800 used_environment = (char *const *)environment; 801 #ifdef __clang__ 802 #pragma clang diagnostic pop 803 #endif 804 } else if (subprocess_option_inherit_environment == 805 (options & subprocess_option_inherit_environment)) { 806 used_environment = environ; 807 } else { 808 used_environment = empty_environment; 809 } 810 811 if (0 != posix_spawn_file_actions_init(&actions)) { 812 return -1; 813 } 814 815 // Close the stdin write end 816 if (0 != posix_spawn_file_actions_addclose(&actions, stdinfd[1])) { 817 posix_spawn_file_actions_destroy(&actions); 818 return -1; 819 } 820 821 // Map the read end to stdin 822 if (0 != 823 posix_spawn_file_actions_adddup2(&actions, stdinfd[0], STDIN_FILENO)) { 824 posix_spawn_file_actions_destroy(&actions); 825 return -1; 826 } 827 828 // Close the stdout read end 829 if (0 != posix_spawn_file_actions_addclose(&actions, stdoutfd[0])) { 830 posix_spawn_file_actions_destroy(&actions); 831 return -1; 832 } 833 834 // Map the write end to stdout 835 if (0 != 836 posix_spawn_file_actions_adddup2(&actions, stdoutfd[1], STDOUT_FILENO)) { 837 posix_spawn_file_actions_destroy(&actions); 838 return -1; 839 } 840 841 if (subprocess_option_combined_stdout_stderr == 842 (options & subprocess_option_combined_stdout_stderr)) { 843 if (0 != posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO, 844 STDERR_FILENO)) { 845 posix_spawn_file_actions_destroy(&actions); 846 return -1; 847 } 848 } else { 849 // Close the stderr read end 850 if (0 != posix_spawn_file_actions_addclose(&actions, stderrfd[0])) { 851 posix_spawn_file_actions_destroy(&actions); 852 return -1; 853 } 854 // Map the write end to stdout 855 if (0 != posix_spawn_file_actions_adddup2(&actions, stderrfd[1], 856 STDERR_FILENO)) { 857 posix_spawn_file_actions_destroy(&actions); 858 return -1; 859 } 860 } 861 862 #ifdef __clang__ 863 #pragma clang diagnostic push 864 #pragma clang diagnostic ignored "-Wcast-qual" 865 #pragma clang diagnostic ignored "-Wold-style-cast" 866 #endif 867 if (subprocess_option_search_user_path == 868 (options & subprocess_option_search_user_path)) { 869 if (0 != posix_spawnp(&child, commandLine[0], &actions, SUBPROCESS_NULL, 870 (char *const *)commandLine, used_environment)) { 871 posix_spawn_file_actions_destroy(&actions); 872 return -1; 873 } 874 } else { 875 if (0 != posix_spawn(&child, commandLine[0], &actions, SUBPROCESS_NULL, 876 (char *const *)commandLine, used_environment)) { 877 posix_spawn_file_actions_destroy(&actions); 878 return -1; 879 } 880 } 881 #ifdef __clang__ 882 #pragma clang diagnostic pop 883 #endif 884 885 // Close the stdin read end 886 close(stdinfd[0]); 887 // Store the stdin write end 888 out_process->stdin_file = fdopen(stdinfd[1], "wb"); 889 890 // Close the stdout write end 891 close(stdoutfd[1]); 892 // Store the stdout read end 893 out_process->stdout_file = fdopen(stdoutfd[0], "rb"); 894 895 if (subprocess_option_combined_stdout_stderr == 896 (options & subprocess_option_combined_stdout_stderr)) { 897 out_process->stderr_file = out_process->stdout_file; 898 } else { 899 // Close the stderr write end 900 close(stderrfd[1]); 901 // Store the stderr read end 902 out_process->stderr_file = fdopen(stderrfd[0], "rb"); 903 } 904 905 // Store the child's pid 906 out_process->child = child; 907 908 out_process->alive = 1; 909 910 posix_spawn_file_actions_destroy(&actions); 911 return 0; 912 #endif 913 } 914 915 FILE *subprocess_stdin(const struct subprocess_s *const process) { 916 return process->stdin_file; 917 } 918 919 FILE *subprocess_stdout(const struct subprocess_s *const process) { 920 return process->stdout_file; 921 } 922 923 FILE *subprocess_stderr(const struct subprocess_s *const process) { 924 if (process->stdout_file != process->stderr_file) { 925 return process->stderr_file; 926 } else { 927 return SUBPROCESS_NULL; 928 } 929 } 930 931 int subprocess_join(struct subprocess_s *const process, 932 int *const out_return_code) { 933 #if defined(_WIN32) 934 const unsigned long infinite = 0xFFFFFFFF; 935 936 if (process->stdin_file) { 937 fclose(process->stdin_file); 938 process->stdin_file = SUBPROCESS_NULL; 939 } 940 941 if (process->hStdInput) { 942 CloseHandle(process->hStdInput); 943 process->hStdInput = SUBPROCESS_NULL; 944 } 945 946 WaitForSingleObject(process->hProcess, infinite); 947 948 if (out_return_code) { 949 if (!GetExitCodeProcess( 950 process->hProcess, 951 SUBPROCESS_PTR_CAST(unsigned long *, out_return_code))) { 952 return -1; 953 } 954 } 955 956 process->alive = 0; 957 958 return 0; 959 #else 960 int status; 961 962 if (process->stdin_file) { 963 fclose(process->stdin_file); 964 process->stdin_file = SUBPROCESS_NULL; 965 } 966 967 if (process->child) { 968 if (process->child != waitpid(process->child, &status, 0)) { 969 return -1; 970 } 971 972 process->child = 0; 973 974 if (WIFEXITED(status)) { 975 process->return_status = WEXITSTATUS(status); 976 } else { 977 process->return_status = EXIT_FAILURE; 978 } 979 980 process->alive = 0; 981 } 982 983 if (out_return_code) { 984 *out_return_code = process->return_status; 985 } 986 987 return 0; 988 #endif 989 } 990 991 int subprocess_destroy(struct subprocess_s *const process) { 992 if (process->stdin_file) { 993 fclose(process->stdin_file); 994 process->stdin_file = SUBPROCESS_NULL; 995 } 996 997 if (process->stdout_file) { 998 fclose(process->stdout_file); 999 1000 if (process->stdout_file != process->stderr_file) { 1001 fclose(process->stderr_file); 1002 } 1003 1004 process->stdout_file = SUBPROCESS_NULL; 1005 process->stderr_file = SUBPROCESS_NULL; 1006 } 1007 1008 #if defined(_WIN32) 1009 if (process->hProcess) { 1010 CloseHandle(process->hProcess); 1011 process->hProcess = SUBPROCESS_NULL; 1012 1013 if (process->hStdInput) { 1014 CloseHandle(process->hStdInput); 1015 } 1016 1017 if (process->hEventOutput) { 1018 CloseHandle(process->hEventOutput); 1019 } 1020 1021 if (process->hEventError) { 1022 CloseHandle(process->hEventError); 1023 } 1024 } 1025 #endif 1026 1027 return 0; 1028 } 1029 1030 int subprocess_terminate(struct subprocess_s *const process) { 1031 #if defined(_WIN32) 1032 unsigned int killed_process_exit_code; 1033 int success_terminate; 1034 int windows_call_result; 1035 1036 killed_process_exit_code = 99; 1037 windows_call_result = 1038 TerminateProcess(process->hProcess, killed_process_exit_code); 1039 success_terminate = (windows_call_result == 0) ? 1 : 0; 1040 return success_terminate; 1041 #else 1042 int result; 1043 result = kill(process->child, 9); 1044 return result; 1045 #endif 1046 } 1047 1048 unsigned subprocess_read_stdout(struct subprocess_s *const process, 1049 char *const buffer, unsigned size) { 1050 #if defined(_WIN32) 1051 void *handle; 1052 unsigned long bytes_read = 0; 1053 struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL}; 1054 overlapped.hEvent = process->hEventOutput; 1055 1056 handle = SUBPROCESS_PTR_CAST(void *, 1057 _get_osfhandle(_fileno(process->stdout_file))); 1058 1059 if (!ReadFile(handle, buffer, size, &bytes_read, 1060 SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) { 1061 const unsigned long errorIoPending = 997; 1062 unsigned long error = GetLastError(); 1063 1064 // Means we've got an async read! 1065 if (error == errorIoPending) { 1066 if (!GetOverlappedResult(handle, 1067 SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped), 1068 &bytes_read, 1)) { 1069 const unsigned long errorIoIncomplete = 996; 1070 const unsigned long errorHandleEOF = 38; 1071 error = GetLastError(); 1072 1073 if ((error != errorIoIncomplete) && (error != errorHandleEOF)) { 1074 return 0; 1075 } 1076 } 1077 } 1078 } 1079 1080 return SUBPROCESS_CAST(unsigned, bytes_read); 1081 #else 1082 const int fd = fileno(process->stdout_file); 1083 const ssize_t bytes_read = read(fd, buffer, size); 1084 1085 if (bytes_read < 0) { 1086 return 0; 1087 } 1088 1089 return SUBPROCESS_CAST(unsigned, bytes_read); 1090 #endif 1091 } 1092 1093 unsigned subprocess_read_stderr(struct subprocess_s *const process, 1094 char *const buffer, unsigned size) { 1095 #if defined(_WIN32) 1096 void *handle; 1097 unsigned long bytes_read = 0; 1098 struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL}; 1099 overlapped.hEvent = process->hEventError; 1100 1101 handle = SUBPROCESS_PTR_CAST(void *, 1102 _get_osfhandle(_fileno(process->stderr_file))); 1103 1104 if (!ReadFile(handle, buffer, size, &bytes_read, 1105 SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) { 1106 const unsigned long errorIoPending = 997; 1107 unsigned long error = GetLastError(); 1108 1109 // Means we've got an async read! 1110 if (error == errorIoPending) { 1111 if (!GetOverlappedResult(handle, 1112 SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped), 1113 &bytes_read, 1)) { 1114 const unsigned long errorIoIncomplete = 996; 1115 const unsigned long errorHandleEOF = 38; 1116 error = GetLastError(); 1117 1118 if ((error != errorIoIncomplete) && (error != errorHandleEOF)) { 1119 return 0; 1120 } 1121 } 1122 } 1123 } 1124 1125 return SUBPROCESS_CAST(unsigned, bytes_read); 1126 #else 1127 const int fd = fileno(process->stderr_file); 1128 const ssize_t bytes_read = read(fd, buffer, size); 1129 1130 if (bytes_read < 0) { 1131 return 0; 1132 } 1133 1134 return SUBPROCESS_CAST(unsigned, bytes_read); 1135 #endif 1136 } 1137 1138 int subprocess_alive(struct subprocess_s *const process) { 1139 int is_alive = SUBPROCESS_CAST(int, process->alive); 1140 1141 if (!is_alive) { 1142 return 0; 1143 } 1144 #if defined(_WIN32) 1145 { 1146 const unsigned long zero = 0x0; 1147 const unsigned long wait_object_0 = 0x00000000L; 1148 1149 is_alive = wait_object_0 != WaitForSingleObject(process->hProcess, zero); 1150 } 1151 #else 1152 { 1153 int status; 1154 is_alive = 0 == waitpid(process->child, &status, WNOHANG); 1155 1156 // If the process was successfully waited on we need to cleanup now. 1157 if (!is_alive) { 1158 if (WIFEXITED(status)) { 1159 process->return_status = WEXITSTATUS(status); 1160 } else { 1161 process->return_status = EXIT_FAILURE; 1162 } 1163 1164 // Since we've already successfully waited on the process, we need to wipe 1165 // the child now. 1166 process->child = 0; 1167 1168 if (subprocess_join(process, SUBPROCESS_NULL)) { 1169 return -1; 1170 } 1171 } 1172 } 1173 #endif 1174 1175 if (!is_alive) { 1176 process->alive = 0; 1177 } 1178 1179 return is_alive; 1180 } 1181 1182 #if defined(__cplusplus) 1183 } // extern "C" 1184 #endif 1185 1186 #endif /* SHEREDOM_SUBPROCESS_H_INCLUDED */