char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
6657ded4f
char/aliceai-t5-moe
1/* 2The latest version of this library is available on GitHub; 3https://github.com/sheredom/subprocess.h 4*/ 5 6/* 7This is free and unencumbered software released into the public domain. 8 9Anyone is free to copy, modify, publish, use, compile, sell, or 10distribute this software, either in source code form or as a compiled 11binary, for any purpose, commercial or non-commercial, and by any 12means. 13 14In jurisdictions that recognize copyright laws, the author or authors 15of this software dedicate any and all copyright interest in the 16software to the public domain. We make this dedication for the benefit 17of the public at large and to the detriment of our heirs and 18successors. We intend this dedication to be an overt act of 19relinquishment in perpetuity of all present and future rights to this 20software under copyright law. 21 22THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 26OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 27ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28OTHER DEALINGS IN THE SOFTWARE. 29 30For 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 compiler found! 71#endif 72 73struct subprocess_s ; 74 75enum subprocess_option_e { 76// stdout and stderr are the same FILE. 77subprocess_option_combined_stdout_stderr = 0x1 , 78 79// The child process should inherit the environment variables of the parent. 80subprocess_option_inherit_environment = 0x2 , 81 82// Enable asynchronous reading of stdout/stderr before it has completed. 83subprocess_option_enable_async = 0x4 , 84 85// Enable the child process to be spawned with no window visible if supported 86// by the platform. 87subprocess_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. 92subprocess_option_search_user_path = 0x10 , 93 94// Make subprocess_read_stdout and subprocess_read_stderr return immediately 95// with 0 if no data is available. Requires subprocess_option_enable_async. 96subprocess_option_enable_async_no_wait = 0x20 97}; 98 99// Error codes returned by subprocess_create and subprocess_create_ex. 100// subprocess_error_success is always zero; all errors are non-zero. 101enum subprocess_error_e { 102subprocess_error_success = 0 , 103subprocess_error_unknown = -1 , 104subprocess_error_invalid_options = -2 , 105subprocess_error_invalid_environment = -3 , 106subprocess_error_not_found = -4 , 107subprocess_error_permission_denied = -5 , 108subprocess_error_no_memory = -6 , 109subprocess_error_pipe = -7 , 110subprocess_error_spawn = -8 , 111subprocess_error_not_supported = -9 112}; 113 114#if defined(__cplusplus ) 115extern "C" { 116#endif 117 118/// @brief Create a process. 119/// @param command_line An array of strings for the command line to execute for 120/// this process. The last element must be NULL to signify the end of the array. 121/// On Windows these strings are interpreted as UTF-8 and passed to the Unicode 122/// process creation APIs. The memory backing this parameter only needs to 123/// persist until this function returns. 124/// @param options A bit field of subprocess_option_e's to pass. 125/// @param out_process The newly created process. 126/// @return On success zero is returned. On failure a non-zero 127/// `subprocess_error_e` value is returned; inspect `errno` on POSIX platforms 128/// or `GetLastError()` on Windows for the platform-specific failure reason. 129subprocess_weak int subprocess_create (const char * const command_line [], 130int options , 131struct subprocess_s * const out_process ); 132 133/// @brief Create a process (extended create). 134/// @param command_line An array of strings for the command line to execute for 135/// this process. The last element must be NULL to signify the end of the array. 136/// On Windows these strings are interpreted as UTF-8 and passed to the Unicode 137/// process creation APIs. The memory backing this parameter only needs to 138/// persist until this function returns. 139/// @param options A bit field of subprocess_option_e's to pass. 140/// @param environment An optional array of strings for the environment to use 141/// for a child process (each element of the form FOO=BAR). The last element 142/// must be NULL to signify the end of the array. On Windows these strings are 143/// interpreted as UTF-8. 144/// @param process_cwd The current working directory of the newly created 145/// process. If NULL, will be the same as the parent process. On Windows this 146/// string is interpreted as UTF-8. 147/// @param out_process The newly created process. 148/// @return On success zero is returned. On failure a non-zero 149/// `subprocess_error_e` value is returned; inspect `errno` on POSIX platforms 150/// or `GetLastError()` on Windows for the platform-specific failure reason. 151/// 152/// If `options` contains `subprocess_option_inherit_environment`, then 153/// `environment` must be NULL. 154subprocess_weak int 155subprocess_create_ex (const char * const command_line [],int options , 156const char * const environment [], 157const char * const process_cwd , 158struct subprocess_s * const out_process ); 159 160/// @brief Get the standard input file for a process. 161/// @param process The process to query. 162/// @return The file for standard input of the process. 163/// 164/// The file returned can be written to by the parent process to feed data to 165/// the standard input of the process. 166subprocess_pure subprocess_weak FILE * 167subprocess_stdin (const struct subprocess_s * const process ); 168 169/// @brief Get the standard output file for a process. 170/// @param process The process to query. 171/// @return The file for standard output of the process. 172/// 173/// The file returned can be read from by the parent process to read data from 174/// the standard output of the child process. 175subprocess_pure subprocess_weak FILE * 176subprocess_stdout (const struct subprocess_s * const process ); 177 178/// @brief Get the standard error file for a process. 179/// @param process The process to query. 180/// @return The file for standard error of the process. 181/// 182/// The file returned can be read from by the parent process to read data from 183/// the standard error of the child process. 184/// 185/// If the process was created with the subprocess_option_combined_stdout_stderr 186/// option bit set, this function will return NULL, and the subprocess_stdout 187/// function should be used for both the standard output and error combined. 188subprocess_pure subprocess_weak FILE * 189subprocess_stderr (const struct subprocess_s * const process ); 190 191/// @brief Wait for a process to finish execution. 192/// @param process The process to wait for. 193/// @param out_return_code The return code of the returned process (can be 194/// NULL). 195/// @return On success zero is returned. 196/// 197/// Joining a process will close the stdin pipe to the process. 198subprocess_weak int subprocess_join (struct subprocess_s * const process , 199int * const out_return_code ); 200 201/// @brief Destroy a previously created process. 202/// @param process The process to destroy. 203/// @return On success zero is returned. 204/// 205/// If the process to be destroyed had not finished execution, it may out live 206/// the parent process. 207subprocess_weak int subprocess_destroy (struct subprocess_s * const process ); 208 209/// @brief Terminate a previously created process. 210/// @param process The process to terminate. 211/// @return On success zero is returned. 212/// 213/// If the process to be destroyed had not finished execution, it will be 214/// terminated (i.e killed). 215subprocess_weak int subprocess_terminate (struct subprocess_s * const process ); 216 217/// @brief Read the standard output from the child process. 218/// @param process The process to read from. 219/// @param buffer The buffer to read into. 220/// @param size The maximum number of bytes to read. 221/// @return The number of bytes actually read into buffer. Can only be 0 if the 222/// process has complete, or if the process was created with 223/// `subprocess_option_enable_async_no_wait` and no data is currently available. 224/// 225/// The only safe way to read from the standard output of a process during it's 226/// execution is to use the `subprocess_option_enable_async` option in 227/// conjunction with this method. 228subprocess_weak unsigned 229subprocess_read_stdout (struct subprocess_s * const process ,char * const buffer , 230unsigned size ); 231 232/// @brief Read the standard error from the child process. 233/// @param process The process to read from. 234/// @param buffer The buffer to read into. 235/// @param size The maximum number of bytes to read. 236/// @return The number of bytes actually read into buffer. Can only be 0 if the 237/// process has complete, or if the process was created with 238/// `subprocess_option_enable_async_no_wait` and no data is currently available. 239/// 240/// The only safe way to read from the standard error of a process during it's 241/// execution is to use the `subprocess_option_enable_async` option in 242/// conjunction with this method. 243subprocess_weak unsigned 244subprocess_read_stderr (struct subprocess_s * const process ,char * const buffer , 245unsigned size ); 246 247/// @brief Returns if the subprocess is currently still alive and executing. 248/// @param process The process to check. 249/// @return If the process is still alive non-zero is returned. 250subprocess_weak int subprocess_alive (struct subprocess_s * const process ); 251 252#if defined(__cplusplus ) 253#define SUBPROCESS_CAST (type ,x ) static_cast<type>(x) 254#define SUBPROCESS_PTR_CAST (type ,x ) reinterpret_cast<type>(x) 255#define SUBPROCESS_CONST_CAST (type ,x ) const_cast<type>(x) 256#define SUBPROCESS_NULL NULL 257#else 258#define SUBPROCESS_CAST (type ,x ) ((type)(x)) 259#define SUBPROCESS_PTR_CAST (type ,x ) ((type)(x)) 260#define SUBPROCESS_CONST_CAST (type ,x ) ((type)(x)) 261#define SUBPROCESS_NULL 0 262#endif 263 264#if !defined(_WIN32 ) 265#include <errno.h> 266#include <fcntl.h> 267#include <signal.h> 268#include <spawn.h> 269#include <stdlib.h> 270#if defined(__APPLE__ ) 271#include <AvailabilityMacros.h> 272#endif 273#include <sys/types.h> 274#include <sys/wait.h> 275#include <unistd.h> 276#endif 277 278#if defined(__NetBSD__ ) 279#include <sys/param.h> 280#endif 281 282/* Which spelling of the chdir file action the platform provides, if any. 283POSIX 2024 standardised posix_spawn_file_actions_addchdir; implementations 284that shipped it earlier called it ..._np. macOS 26 and NetBSD 10 use the 285standard name, glibc 2.29+, macOS 10.15+ and FreeBSD 13.1+ use the _np name, 286and AIX, NetBSD 9 and older, and OpenBSD provide neither. */ 287#if !defined(SUBPROCESS_ADDCHDIR_IS_POSIX ) 288#if (defined(__APPLE__ )&& MAC_OS_X_VERSION_MIN_REQUIRED >=260000 )|| \ 289 (defined(__NetBSD__ )&& __NetBSD_Version__ >=1000000000 ) 290#define SUBPROCESS_ADDCHDIR_IS_POSIX 1 291#else 292#define SUBPROCESS_ADDCHDIR_IS_POSIX 0 293#endif 294#endif 295 296/* Whether to launch the child with fork()+exec() instead of posix_spawn(), 297for platforms with no posix_spawn_file_actions_addchdir under either 298spelling: the child chdir()s before exec, and a close-on-exec pipe carries 299exec's errno back. Define this yourself to force either implementation. */ 300#if !defined(SUBPROCESS_SPAWN_VIA_FORK ) 301#if defined(_AIX )|| defined(__OpenBSD__ )|| \ 302 (defined(__NetBSD__ )&& (__NetBSD_Version__ < 1000000000 )) 303#define SUBPROCESS_SPAWN_VIA_FORK 1 304#else 305#define SUBPROCESS_SPAWN_VIA_FORK 0 306#endif 307#endif 308 309/* Whether subprocess_create_ex can honour process_cwd. glibc only gained 310posix_spawn_file_actions_addchdir_np in 2.29, and macOS in 10.15; the SDKs 311mark it unavailable on iOS, tvOS and watchOS, where the undefined version 312macro folds to 0 and so answers correctly. Define this yourself to override 313the detection, for instance on musl older than 1.1.24. */ 314#if !defined(SUBPROCESS_HAVE_CWD ) 315#if SUBPROCESS_SPAWN_VIA_FORK 316#define SUBPROCESS_HAVE_CWD 1 317#elif defined(__GLIBC__ ) 318#if __GLIBC_PREREQ (2 ,29 ) 319#define SUBPROCESS_HAVE_CWD 1 320#else 321#define SUBPROCESS_HAVE_CWD 0 322#endif 323#elif defined(__APPLE__ )&& MAC_OS_X_VERSION_MIN_REQUIRED < 101500 324#define SUBPROCESS_HAVE_CWD 0 325#else 326#define SUBPROCESS_HAVE_CWD 1 327#endif 328#endif 329 330/* Whether a failed exec is reported back to the caller. The fork() path always 331reports it through its error pipe. glibc's posix_spawn only started doing so 332in 2.24; before that the child silently exits with 127. */ 333#if !defined(SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS ) 334#if SUBPROCESS_SPAWN_VIA_FORK 335#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1 336#elif defined(__GLIBC__ ) 337#if __GLIBC_PREREQ (2 ,24 ) 338#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1 339#else 340#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 0 341#endif 342#else 343#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1 344#endif 345#endif 346 347#if defined(_WIN32 ) 348 349#include <wchar.h> 350 351#if defined(__clang__ ) 352#if __has_warning ("- Wc ++- keyword ") 353#pragma clang diagnostic push 354#pragma clang diagnostic ignored "-Wc++-keyword" 355#endif 356#endif 357 358typedef wchar_t subprocess_wchar_t ; 359 360#if defined(__clang__ ) 361#if __has_warning ("- Wc ++- keyword ") 362#pragma clang diagnostic pop 363#endif 364#endif 365 366#if (_MSC_VER < 1920 ) 367#ifdef _WIN64 368typedef __int64 subprocess_intptr_t ; 369typedef unsigned __int64 subprocess_size_t ; 370#else 371typedef int subprocess_intptr_t ; 372typedef unsigned int subprocess_size_t ; 373#endif 374#else 375#include <inttypes.h> 376 377typedef intptr_t subprocess_intptr_t ; 378typedef size_t subprocess_size_t ; 379#endif 380 381/* SIZE_T is ULONG_PTR, which is not size_t: on Win32 both are 32 bits wide but 382unsigned long and unsigned int are still distinct types. */ 383#ifdef _WIN64 384typedef subprocess_size_t subprocess_ulongptr_t ; 385#else 386typedef unsigned long subprocess_ulongptr_t ; 387#endif 388 389#ifdef __clang__ 390#pragma clang diagnostic push 391#pragma clang diagnostic ignored "-Wreserved-identifier" 392#endif 393 394typedef struct _PROCESS_INFORMATION * LPPROCESS_INFORMATION ; 395typedef struct _SECURITY_ATTRIBUTES * LPSECURITY_ATTRIBUTES ; 396typedef struct _STARTUPINFOW * LPSTARTUPINFOW ; 397typedef struct _OVERLAPPED * LPOVERLAPPED ; 398typedef struct _PROC_THREAD_ATTRIBUTE_LIST * LPPROC_THREAD_ATTRIBUTE_LIST ; 399 400#ifdef __clang__ 401#pragma clang diagnostic pop 402#endif 403 404#ifdef _MSC_VER 405#pragma warning(push, 1) 406#endif 407#ifdef __MINGW32__ 408#pragma GCC diagnostic push 409#pragma GCC diagnostic ignored "-Wpedantic" 410#endif 411#ifdef __clang__ 412#pragma clang diagnostic push 413#pragma clang diagnostic ignored "-Wpadded" 414#endif 415 416struct subprocess_subprocess_information_s { 417void * hProcess ; 418void * hThread ; 419unsigned long dwProcessId ; 420unsigned long dwThreadId ; 421}; 422 423struct subprocess_security_attributes_s { 424unsigned long nLength ; 425void * lpSecurityDescriptor ; 426int bInheritHandle ; 427}; 428 429struct subprocess_startup_info_s { 430unsigned long cb ; 431subprocess_wchar_t * lpReserved ; 432subprocess_wchar_t * lpDesktop ; 433subprocess_wchar_t * lpTitle ; 434unsigned long dwX ; 435unsigned long dwY ; 436unsigned long dwXSize ; 437unsigned long dwYSize ; 438unsigned long dwXCountChars ; 439unsigned long dwYCountChars ; 440unsigned long dwFillAttribute ; 441unsigned long dwFlags ; 442unsigned short wShowWindow ; 443unsigned short cbReserved2 ; 444unsigned char * lpReserved2 ; 445void * hStdInput ; 446void * hStdOutput ; 447void * hStdError ; 448}; 449 450struct subprocess_startup_info_ex_s { 451struct subprocess_startup_info_s startupInfo ; 452void * attributeList ; 453}; 454 455struct subprocess_overlapped_s { 456uintptr_t Internal ; 457uintptr_t InternalHigh ; 458union { 459struct { 460unsigned long Offset ; 461unsigned long OffsetHigh ; 462 }DUMMYSTRUCTNAME ; 463void * Pointer ; 464 }DUMMYUNIONNAME ; 465 466void * hEvent ; 467}; 468 469#ifdef __clang__ 470#pragma clang diagnostic pop 471#endif 472#ifdef __MINGW32__ 473#pragma GCC diagnostic pop 474#endif 475#ifdef _MSC_VER 476#pragma warning(pop) 477#endif 478 479__declspec(dllimport )unsigned long __stdcallGetLastError (void ); 480__declspec(dllimport )void __stdcallSetLastError (unsigned long ); 481__declspec(dllimport )int __stdcallSetHandleInformation (void * ,unsigned long , 482unsigned long ); 483__declspec(dllimport )int __stdcallCreatePipe (void ** ,void ** , 484LPSECURITY_ATTRIBUTES , 485unsigned long ); 486__declspec(dllimport )void * __stdcall CreateNamedPipeA ( 487const char * ,unsigned long ,unsigned long ,unsigned long ,unsigned long , 488unsigned long ,unsigned long ,LPSECURITY_ATTRIBUTES ); 489__declspec(dllimport )int __stdcallReadFile (void * ,void * ,unsigned long , 490unsigned long * ,LPOVERLAPPED ); 491__declspec(dllimport )unsigned long __stdcallGetCurrentProcessId (void ); 492__declspec(dllimport )unsigned long __stdcallGetCurrentThreadId (void ); 493__declspec(dllimport )void * __stdcall CreateFileA (const char * ,unsigned long , 494unsigned long , 495LPSECURITY_ATTRIBUTES , 496unsigned long ,unsigned long , 497void * ); 498__declspec(dllimport )void * __stdcall CreateEventA (LPSECURITY_ATTRIBUTES ,int , 499int ,const char * ); 500__declspec(dllimport )int __stdcallCreateProcessW ( 501const subprocess_wchar_t * ,subprocess_wchar_t * ,LPSECURITY_ATTRIBUTES , 502LPSECURITY_ATTRIBUTES ,int ,unsigned long ,void * , 503const subprocess_wchar_t * ,LPSTARTUPINFOW ,LPPROCESS_INFORMATION ); 504__declspec(dllimport )int __stdcall 505InitializeProcThreadAttributeList (LPPROC_THREAD_ATTRIBUTE_LIST ,unsigned long , 506unsigned long ,subprocess_ulongptr_t * ); 507__declspec(dllimport )int __stdcallUpdateProcThreadAttribute ( 508LPPROC_THREAD_ATTRIBUTE_LIST ,unsigned long ,subprocess_ulongptr_t ,void * , 509subprocess_ulongptr_t ,void * ,subprocess_ulongptr_t * ); 510__declspec(dllimport )void __stdcall 511DeleteProcThreadAttributeList (LPPROC_THREAD_ATTRIBUTE_LIST ); 512__declspec(dllimport )int __stdcallMultiByteToWideChar ( 513unsigned int ,unsigned long ,const char * ,int ,subprocess_wchar_t * ,int ); 514__declspec(dllimport )int __stdcallCloseHandle (void * ); 515__declspec(dllimport )unsigned long __stdcallWaitForSingleObject ( 516void * ,unsigned long ); 517__declspec(dllimport )int __stdcallGetExitCodeProcess ( 518void * ,unsigned long * lpExitCode ); 519__declspec(dllimport )int __stdcallTerminateProcess (void * ,unsigned int ); 520__declspec(dllimport )unsigned long __stdcallWaitForMultipleObjects ( 521unsigned long ,void * const * ,int ,unsigned long ); 522__declspec(dllimport )int __stdcallGetOverlappedResult (void * ,LPOVERLAPPED , 523unsigned long * ,int ); 524__declspec(dllimport )int __stdcallPeekNamedPipe (void * ,void * ,unsigned long , 525unsigned long * , 526unsigned long * , 527unsigned long * ); 528 529#if defined(_DLL ) 530#define SUBPROCESS_DLLIMPORT __declspec(dllimport) 531#else 532#define SUBPROCESS_DLLIMPORT 533#endif 534 535#ifdef __clang__ 536#pragma clang diagnostic push 537#pragma clang diagnostic ignored "-Wreserved-identifier" 538#endif 539 540SUBPROCESS_DLLIMPORT int __cdecl_fileno (FILE * ); 541SUBPROCESS_DLLIMPORT int __cdecl_open_osfhandle (subprocess_intptr_t ,int ); 542SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl_get_osfhandle (int ); 543SUBPROCESS_DLLIMPORT int __cdecl_close (int ); 544 545#ifndef __MINGW32__ 546void * __cdecl _alloca (subprocess_size_t ); 547#else 548#include <malloc.h> 549#endif 550 551#ifdef __clang__ 552#pragma clang diagnostic pop 553#endif 554 555#else 556typedef size_t subprocess_size_t ; 557#endif 558 559#ifdef __clang__ 560#pragma clang diagnostic push 561#pragma clang diagnostic ignored "-Wpadded" 562#endif 563struct subprocess_s { 564FILE * stdin_file ; 565FILE * stdout_file ; 566FILE * stderr_file ; 567 568#if defined(_WIN32 ) 569void * hProcess ; 570void * hStdInput ; 571void * hEventOutput ; 572void * hEventError ; 573#else 574pid_t child ; 575int return_status ; 576#endif 577 578int alive ; 579int no_wait ; 580}; 581#ifdef __clang__ 582#pragma clang diagnostic pop 583#endif 584 585#if defined(_WIN32 ) 586subprocess_weak int subprocess_error_from_windows_error (unsigned long error ); 587int subprocess_error_from_windows_error (unsigned long error ) { 588enum { 589errorFileNotFound = 2 , 590errorPathNotFound = 3 , 591errorTooManyOpenFiles = 4 , 592errorAccessDenied = 5 , 593errorInvalidHandle = 6 , 594errorNotEnoughMemory = 8 , 595errorOutOfMemory = 14 , 596errorInvalidDrive = 15 , 597errorBadPathname = 161 , 598errorDirectory = 267 599 }; 600 601switch (error ) { 602case errorFileNotFound : 603case errorPathNotFound : 604case errorInvalidDrive : 605case errorBadPathname : 606case errorDirectory : 607return subprocess_error_not_found ; 608case errorAccessDenied : 609return subprocess_error_permission_denied ; 610case errorTooManyOpenFiles : 611case errorNotEnoughMemory : 612case errorOutOfMemory : 613return subprocess_error_no_memory ; 614case errorInvalidHandle : 615return subprocess_error_pipe ; 616default : 617return subprocess_error_unknown ; 618 } 619} 620#else 621subprocess_weak int subprocess_error_from_errno (int error ); 622int subprocess_error_from_errno (int error ) { 623switch (error ) { 624case ENOENT : 625case ENOTDIR : 626return subprocess_error_not_found ; 627case EACCES : 628case EPERM : 629return subprocess_error_permission_denied ; 630case EMFILE : 631case ENFILE : 632case ENOMEM : 633return subprocess_error_no_memory ; 634case ENOSYS : 635return subprocess_error_not_supported ; 636default : 637return subprocess_error_unknown ; 638 } 639} 640#endif 641 642#if defined(__clang__ ) 643#if __has_warning ("- Wunsafe - buffer - usage ") 644#pragma clang diagnostic push 645#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" 646#endif 647#endif 648 649#if defined(_WIN32 ) 650subprocess_weak int subprocess_create_named_pipe_helper (void ** rd ,void ** wr ); 651subprocess_weak void subprocess_close_handle (void ** handle ); 652void subprocess_close_handle (void ** handle ) { 653const void * const invalidHandleValue = 654SUBPROCESS_PTR_CAST (void * , ~(SUBPROCESS_CAST (subprocess_intptr_t ,0 ))); 655 656if (* handle && (invalidHandleValue != * handle )) { 657CloseHandle (* handle ); 658 } 659 660* handle = SUBPROCESS_NULL ; 661} 662 663int subprocess_create_named_pipe_helper (void ** rd ,void ** wr ) { 664const unsigned long pipeAccessInbound = 0x00000001 ; 665const unsigned long fileFlagOverlapped = 0x40000000 ; 666const unsigned long pipeTypeByte = 0x00000000 ; 667const unsigned long pipeWait = 0x00000000 ; 668const unsigned long genericWrite = 0x40000000 ; 669const unsigned long openExisting = 3 ; 670const unsigned long fileAttributeNormal = 0x00000080 ; 671const void * const invalidHandleValue = 672SUBPROCESS_PTR_CAST (void * , ~(SUBPROCESS_CAST (subprocess_intptr_t ,0 ))); 673struct subprocess_security_attributes_s saAttr = {sizeof (saAttr ), 674SUBPROCESS_NULL ,1 }; 675char name [256 ]= {0 }; 676#if defined(__clang__ ) 677#if __has_warning ("- Wunique - object - duplication ") 678#pragma clang diagnostic push 679#pragma clang diagnostic ignored "-Wunique-object-duplication" 680#endif 681#endif 682static subprocess_tls long index = 0 ; 683#if defined(__clang__ ) 684#if __has_warning ("- Wunique - object - duplication ") 685#pragma clang diagnostic pop 686#endif 687#endif 688const long unique = index ++ ; 689 690* rd = SUBPROCESS_NULL ; 691* wr = SUBPROCESS_NULL ; 692 693#if defined(_MSC_VER )&& _MSC_VER < 1900 694#pragma warning(push, 1) 695#pragma warning(disable : 4996) 696_snprintf (name ,sizeof (name )- 1 , 697"\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld" , 698GetCurrentProcessId (),GetCurrentThreadId (),unique ); 699#pragma warning(pop) 700#else 701snprintf (name ,sizeof (name )- 1 , 702"\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld" , 703GetCurrentProcessId (),GetCurrentThreadId (),unique ); 704#endif 705 706* rd = 707CreateNamedPipeA (name ,pipeAccessInbound |fileFlagOverlapped , 708pipeTypeByte |pipeWait ,1 ,4096 ,4096 ,SUBPROCESS_NULL , 709SUBPROCESS_PTR_CAST (LPSECURITY_ATTRIBUTES ,& saAttr )); 710 711if (invalidHandleValue == * rd ) { 712return -1 ; 713 } 714 715* wr = CreateFileA (name ,genericWrite ,SUBPROCESS_NULL , 716SUBPROCESS_PTR_CAST (LPSECURITY_ATTRIBUTES ,& saAttr ), 717openExisting ,fileAttributeNormal ,SUBPROCESS_NULL ); 718 719if (invalidHandleValue == * wr ) { 720subprocess_close_handle (rd ); 721return -1 ; 722 } 723 724return 0 ; 725} 726#endif 727 728#if !defined(_WIN32 ) 729/* Move a pipe end off 0, 1 or 2. Duplicating a descriptor onto itself is a 730no-op, so a pipe end already sitting on a standard descriptor would keep its 731FD_CLOEXEC and be closed by exec, leaving the child without that stream. */ 732static int subprocess_fds_above_std (int fds [2 ]) { 733int fd_flags ; 734int index ; 735int moved ; 736int saved_errno ; 737 738for (index = 0 ;index < 2 ;index ++ ) { 739if (fds [index ]> STDERR_FILENO ) { 740continue ; 741 } 742 743moved = fcntl (fds [index ],F_DUPFD ,STDERR_FILENO + 1 ); 744if (-1 != moved ) { 745fd_flags = fcntl (moved ,F_GETFD ,0 ); 746if ((-1 == fd_flags )|| 747 (-1 == fcntl (moved ,F_SETFD ,fd_flags |FD_CLOEXEC ))) { 748saved_errno = errno ; 749close (moved ); 750errno = saved_errno ; 751moved = -1 ; 752 } 753 } 754 755if (-1 == moved ) { 756saved_errno = errno ; 757close (fds [0 ]); 758close (fds [1 ]); 759fds [0 ]= -1 ; 760fds [1 ]= -1 ; 761errno = saved_errno ; 762return -1 ; 763 } 764 765close (fds [index ]); 766fds [index ]= moved ; 767 } 768 769return 0 ; 770} 771 772/* Create pipes with close-on-exec set so later subprocesses do not inherit 773descriptors belonging to subprocesses which are already running. */ 774static int subprocess_pipe_cloexec (int fds [2 ]) { 775int fd_flags ; 776int index ; 777int saved_errno ; 778 779#if defined(__linux__ )|| defined(__FreeBSD__ )|| defined(__NetBSD__ )|| \ 780 defined(__OpenBSD__ )|| defined(__DragonFly__ )|| \ 781 (defined(__sun )&& defined(__SVR4 )) 782if (0 == pipe2 (fds ,O_CLOEXEC )) { 783return subprocess_fds_above_std (fds ); 784 } 785 786/* Older kernels can lack pipe2 even when the C library declares it. */ 787if (ENOSYS != errno ) { 788return -1 ; 789 } 790#endif 791 792if (0 != pipe (fds )) { 793return -1 ; 794 } 795 796for (index = 0 ;index < 2 ;index ++ ) { 797fd_flags = fcntl (fds [index ],F_GETFD ,0 ); 798if ((-1 == fd_flags )|| 799 (-1 == fcntl (fds [index ],F_SETFD ,fd_flags |FD_CLOEXEC ))) { 800saved_errno = errno ; 801close (fds [0 ]); 802close (fds [1 ]); 803fds [0 ]= -1 ; 804fds [1 ]= -1 ; 805errno = saved_errno ; 806return -1 ; 807 } 808 } 809 810return subprocess_fds_above_std (fds ); 811} 812#endif 813 814int subprocess_create (const char * const commandLine [],int options , 815struct subprocess_s * const out_process ) { 816return subprocess_create_ex (commandLine ,options ,SUBPROCESS_NULL , 817SUBPROCESS_NULL ,out_process ); 818} 819 820#if SUBPROCESS_SPAWN_VIA_FORK 821/* Not every platform declares execvpe: AIX exports it from libc without ever 822naming it in a header, and glibc hides it behind _GNU_SOURCE. */ 823extern int execvpe (const char * ,char * const * ,char * const * ); 824#endif 825 826int subprocess_create_ex (const char * const commandLine [],int options , 827const char * const environment [], 828const char * const process_cwd , 829struct subprocess_s * const out_process ) { 830#if defined(_WIN32 ) 831int fd ; 832int async_no_wait ; 833void * rd = SUBPROCESS_NULL ; 834void * wr = SUBPROCESS_NULL ; 835char * commandLineCombined ; 836subprocess_wchar_t * commandLineCombinedWide = SUBPROCESS_NULL ; 837subprocess_wchar_t * process_cwd_wide = SUBPROCESS_NULL ; 838subprocess_size_t len ; 839int wide_len ; 840int i ,j ; 841int need_quoting ; 842subprocess_size_t bs_run ; 843unsigned long flags = 0 ; 844unsigned long last_error = 0 ; 845int attribute_list_initialized = 0 ; 846int result = subprocess_error_unknown ; 847const unsigned int codePageUtf8 = 65001 ; 848const unsigned long mbErrInvalidChars = 0x00000008 ; 849const unsigned long startFUseStdHandles = 0x00000100 ; 850const unsigned long handleFlagInherit = 0x00000001 ; 851const unsigned long createNoWindow = 0x08000000 ; 852const unsigned long createUnicodeEnvironment = 0x00000400 ; 853const unsigned long extendedStartupInfoPresent = 0x00080000 ; 854const subprocess_size_t procThreadAttributeHandleList = 0x00020002 ; 855struct subprocess_subprocess_information_s processInfo = {SUBPROCESS_NULL , 856SUBPROCESS_NULL ,0 , 8570 }; 858struct subprocess_security_attributes_s saAttr = {sizeof (saAttr ), 859SUBPROCESS_NULL ,1 }; 860subprocess_wchar_t empty_environment [2 ]= {0 ,0 }; 861subprocess_wchar_t * used_environment = SUBPROCESS_NULL ; 862subprocess_ulongptr_t attribute_list_size = 0 ; 863subprocess_size_t inherited_handle_count = 0 ; 864LPPROC_THREAD_ATTRIBUTE_LIST attribute_list = SUBPROCESS_NULL ; 865void * inherited_handles [3 ]; 866struct subprocess_startup_info_ex_s startInfoEx ; 867struct subprocess_startup_info_s startInfo = {0 , 868SUBPROCESS_NULL , 869SUBPROCESS_NULL , 870SUBPROCESS_NULL , 8710 , 8720 , 8730 , 8740 , 8750 , 8760 , 8770 , 8780 , 8790 , 8800 , 881SUBPROCESS_NULL , 882SUBPROCESS_NULL , 883SUBPROCESS_NULL , 884SUBPROCESS_NULL }; 885 886async_no_wait = subprocess_option_enable_async_no_wait == 887 (options & subprocess_option_enable_async_no_wait ); 888 889if (async_no_wait && (subprocess_option_enable_async != 890 (options & subprocess_option_enable_async ))) { 891return subprocess_error_invalid_options ; 892 } 893 894startInfo .cb = sizeof (startInfo ); 895startInfo .dwFlags = startFUseStdHandles ; 896 897if (subprocess_option_no_window == (options & subprocess_option_no_window )) { 898flags |=createNoWindow ; 899 } 900 901memset (out_process ,0 ,sizeof (* out_process )); 902 903if (subprocess_option_inherit_environment != 904 (options & subprocess_option_inherit_environment )) { 905flags |=createUnicodeEnvironment ; 906 907if (SUBPROCESS_NULL == environment ) { 908used_environment = empty_environment ; 909 }else { 910// We always end with two null terminators. MultiByteToWideChar includes 911// each environment string's null terminator, so start with one extra. 912len = 1 ; 913 914for (i = 0 ;environment [i ];i ++ ) { 915wide_len = MultiByteToWideChar (codePageUtf8 ,mbErrInvalidChars , 916environment [i ],-1 ,SUBPROCESS_NULL ,0 ); 917if (0 == wide_len ) { 918result = subprocess_error_from_windows_error (GetLastError ()); 919if (subprocess_error_unknown == result ) { 920result = subprocess_error_spawn ; 921 } 922 gotocleanup ; 923 } 924 925len += SUBPROCESS_CAST (subprocess_size_t ,wide_len ); 926 } 927 928if (((SUBPROCESS_CAST (subprocess_size_t ,-1 )) / 929sizeof (subprocess_wchar_t ))< len ) { 930result = subprocess_error_no_memory ; 931 gotocleanup ; 932 } 933 934used_environment = SUBPROCESS_CAST ( 935subprocess_wchar_t * ,_alloca (len * sizeof (subprocess_wchar_t ))); 936if (!used_environment ) { 937result = subprocess_error_no_memory ; 938 gotocleanup ; 939 } 940 941// Re-use len for the insertion position. 942len = 0 ; 943 944for (i = 0 ;environment [i ];i ++ ) { 945wide_len = MultiByteToWideChar (codePageUtf8 ,mbErrInvalidChars , 946environment [i ],-1 ,SUBPROCESS_NULL ,0 ); 947if (0 == wide_len ) { 948result = subprocess_error_from_windows_error (GetLastError ()); 949if (subprocess_error_unknown == result ) { 950result = subprocess_error_spawn ; 951 } 952 gotocleanup ; 953 } 954 955if (0 == MultiByteToWideChar (codePageUtf8 ,mbErrInvalidChars , 956environment [i ],-1 , 957& used_environment [len ],wide_len )) { 958result = subprocess_error_from_windows_error (GetLastError ()); 959if (subprocess_error_unknown == result ) { 960result = subprocess_error_spawn ; 961 } 962 gotocleanup ; 963 } 964 965len += SUBPROCESS_CAST (subprocess_size_t ,wide_len ); 966 } 967 968// End with the second null terminator. 969used_environment [len ++ ]= 0 ; 970 } 971 }else { 972if (SUBPROCESS_NULL != environment ) { 973return subprocess_error_invalid_environment ; 974 } 975 } 976 977if (!CreatePipe (& rd ,& wr ,SUBPROCESS_PTR_CAST (LPSECURITY_ATTRIBUTES ,& saAttr ), 9780 )) { 979result = subprocess_error_pipe ; 980 gotocleanup ; 981 } 982 983if (!SetHandleInformation (wr ,handleFlagInherit ,0 )) { 984result = subprocess_error_pipe ; 985 gotocleanup ; 986 } 987 988fd = _open_osfhandle (SUBPROCESS_PTR_CAST (subprocess_intptr_t ,wr ),0 ); 989if (-1 == fd ) { 990result = subprocess_error_pipe ; 991 gotocleanup ; 992 } 993wr = SUBPROCESS_NULL ; 994 995out_process -> stdin_file = _fdopen (fd ,"wb" ); 996if (SUBPROCESS_NULL == out_process -> stdin_file ) { 997_close (fd ); 998 gotocleanup ; 999 } 1000 1001startInfo .hStdInput = rd ; 1002rd = SUBPROCESS_NULL ; 1003 1004if (options & subprocess_option_enable_async ) { 1005if (subprocess_create_named_pipe_helper (& rd ,& wr )) { 1006result = subprocess_error_pipe ; 1007 gotocleanup ; 1008 } 1009 }else { 1010if (!CreatePipe (& rd ,& wr , 1011SUBPROCESS_PTR_CAST (LPSECURITY_ATTRIBUTES ,& saAttr ),0 )) { 1012result = subprocess_error_pipe ; 1013 gotocleanup ; 1014 } 1015 } 1016 1017if (!SetHandleInformation (rd ,handleFlagInherit ,0 )) { 1018result = subprocess_error_pipe ; 1019 gotocleanup ; 1020 } 1021 1022fd = _open_osfhandle (SUBPROCESS_PTR_CAST (subprocess_intptr_t ,rd ),0 ); 1023if (-1 == fd ) { 1024result = subprocess_error_pipe ; 1025 gotocleanup ; 1026 } 1027rd = SUBPROCESS_NULL ; 1028 1029out_process -> stdout_file = _fdopen (fd ,"rb" ); 1030if (SUBPROCESS_NULL == out_process -> stdout_file ) { 1031_close (fd ); 1032 gotocleanup ; 1033 } 1034 1035startInfo .hStdOutput = wr ; 1036wr = SUBPROCESS_NULL ; 1037 1038if (subprocess_option_combined_stdout_stderr == 1039 (options & subprocess_option_combined_stdout_stderr )) { 1040out_process -> stderr_file = out_process -> stdout_file ; 1041startInfo .hStdError = startInfo .hStdOutput ; 1042 }else { 1043if (options & subprocess_option_enable_async ) { 1044if (subprocess_create_named_pipe_helper (& rd ,& wr )) { 1045result = subprocess_error_pipe ; 1046 gotocleanup ; 1047 } 1048 }else { 1049if (!CreatePipe (& rd ,& wr , 1050SUBPROCESS_PTR_CAST (LPSECURITY_ATTRIBUTES ,& saAttr ),0 )) { 1051result = subprocess_error_pipe ; 1052 gotocleanup ; 1053 } 1054 } 1055 1056if (!SetHandleInformation (rd ,handleFlagInherit ,0 )) { 1057result = subprocess_error_pipe ; 1058 gotocleanup ; 1059 } 1060 1061fd = _open_osfhandle (SUBPROCESS_PTR_CAST (subprocess_intptr_t ,rd ),0 ); 1062if (-1 == fd ) { 1063result = subprocess_error_pipe ; 1064 gotocleanup ; 1065 } 1066rd = SUBPROCESS_NULL ; 1067 1068out_process -> stderr_file = _fdopen (fd ,"rb" ); 1069if (SUBPROCESS_NULL == out_process -> stderr_file ) { 1070_close (fd ); 1071 gotocleanup ; 1072 } 1073 1074startInfo .hStdError = wr ; 1075wr = SUBPROCESS_NULL ; 1076 } 1077 1078if (options & subprocess_option_enable_async ) { 1079out_process -> hEventOutput = 1080CreateEventA (SUBPROCESS_PTR_CAST (LPSECURITY_ATTRIBUTES ,& saAttr ),1 ,1 , 1081SUBPROCESS_NULL ); 1082out_process -> hEventError = 1083CreateEventA (SUBPROCESS_PTR_CAST (LPSECURITY_ATTRIBUTES ,& saAttr ),1 ,1 , 1084SUBPROCESS_NULL ); 1085if (!out_process -> hEventOutput || !out_process -> hEventError ) { 1086result = subprocess_error_from_windows_error (GetLastError ()); 1087if (subprocess_error_unknown == result ) { 1088result = subprocess_error_no_memory ; 1089 } 1090 gotocleanup ; 1091 } 1092 }else { 1093out_process -> hEventOutput = SUBPROCESS_NULL ; 1094out_process -> hEventError = SUBPROCESS_NULL ; 1095 } 1096 1097// Combine commandLine together into a single string 1098len = 0 ; 1099for (i = 0 ;commandLine [i ];i ++ ) { 1100// for the trailing \0 1101len ++ ; 1102 1103// Quote the argument if it has a space in it 1104need_quoting = strpbrk (commandLine [i ],"\t\v " )!= SUBPROCESS_NULL || 1105commandLine [i ][0 ]== SUBPROCESS_NULL ; 1106if (need_quoting ) 1107len += 2 ; 1108 1109bs_run = 0 ; 1110for (j = 0 ;'\0' != commandLine [i ][j ];j ++ ) { 1111len ++ ; 1112 1113if ('\\' == commandLine [i ][j ]) { 1114bs_run ++ ; 1115 }else { 1116if ('"' == commandLine [i ][j ]) { 1117// Duplicate the preceding run and escape the quote. 1118len += bs_run + 1 ; 1119 } 1120bs_run = 0 ; 1121 } 1122 } 1123 1124if (need_quoting ) { 1125// Duplicate trailing slashes before the generated closing quote. 1126len += bs_run ; 1127 } 1128 } 1129 1130commandLineCombined = SUBPROCESS_CAST (char * ,_alloca (len )); 1131 1132if (!commandLineCombined ) { 1133result = subprocess_error_no_memory ; 1134 gotocleanup ; 1135 } 1136 1137// Gonna re-use len to store the write index into commandLineCombined 1138len = 0 ; 1139 1140for (i = 0 ;commandLine [i ];i ++ ) { 1141if (0 != i ) { 1142commandLineCombined [len ++ ]= ' ' ; 1143 } 1144 1145need_quoting = strpbrk (commandLine [i ],"\t\v " )!= SUBPROCESS_NULL || 1146commandLine [i ][0 ]== SUBPROCESS_NULL ; 1147if (need_quoting ) { 1148commandLineCombined [len ++ ]= '"' ; 1149 } 1150 1151for (j = 0 ;'\0' != commandLine [i ][j ];) { 1152bs_run = 0 ; 1153while ('\\' == commandLine [i ][j ]) { 1154bs_run ++ ; 1155j ++ ; 1156 } 1157 1158if ('"' == commandLine [i ][j ]) { 1159// 2n + 1 slashes preserve n slashes and escape the quote. 1160bs_run = (bs_run * 2 )+ 1 ; 1161 }else if ('\0' == commandLine [i ][j ]&& need_quoting ) { 1162// 2n slashes preserve n slashes before the closing quote. 1163bs_run *=2 ; 1164 } 1165 1166while (bs_run > 0 ) { 1167commandLineCombined [len ++ ]= '\\' ; 1168bs_run -- ; 1169 } 1170 1171if ('\0' != commandLine [i ][j ]) { 1172commandLineCombined [len ++ ]= commandLine [i ][j ++ ]; 1173 } 1174 } 1175if (need_quoting ) { 1176commandLineCombined [len ++ ]= '"' ; 1177 } 1178 } 1179 1180commandLineCombined [len ]= '\0' ; 1181 1182wide_len = MultiByteToWideChar (codePageUtf8 ,mbErrInvalidChars , 1183commandLineCombined ,-1 ,SUBPROCESS_NULL ,0 ); 1184if (0 == wide_len ) { 1185result = subprocess_error_from_windows_error (GetLastError ()); 1186if (subprocess_error_unknown == result ) { 1187result = subprocess_error_spawn ; 1188 } 1189 gotocleanup ; 1190 } 1191 1192commandLineCombinedWide = SUBPROCESS_CAST ( 1193subprocess_wchar_t * , 1194_alloca (SUBPROCESS_CAST (subprocess_size_t ,wide_len )* 1195sizeof (subprocess_wchar_t ))); 1196if (!commandLineCombinedWide ) { 1197result = subprocess_error_no_memory ; 1198 gotocleanup ; 1199 } 1200 1201if (0 == MultiByteToWideChar (codePageUtf8 ,mbErrInvalidChars , 1202commandLineCombined ,-1 , 1203commandLineCombinedWide ,wide_len )) { 1204result = subprocess_error_from_windows_error (GetLastError ()); 1205if (subprocess_error_unknown == result ) { 1206result = subprocess_error_spawn ; 1207 } 1208 gotocleanup ; 1209 } 1210 1211if (process_cwd ) { 1212wide_len = MultiByteToWideChar (codePageUtf8 ,mbErrInvalidChars ,process_cwd , 1213-1 ,SUBPROCESS_NULL ,0 ); 1214if (0 == wide_len ) { 1215result = subprocess_error_from_windows_error (GetLastError ()); 1216if (subprocess_error_unknown == result ) { 1217result = subprocess_error_spawn ; 1218 } 1219 gotocleanup ; 1220 } 1221 1222process_cwd_wide = SUBPROCESS_CAST ( 1223subprocess_wchar_t * , 1224_alloca (SUBPROCESS_CAST (subprocess_size_t ,wide_len )* 1225sizeof (subprocess_wchar_t ))); 1226if (!process_cwd_wide ) { 1227result = subprocess_error_no_memory ; 1228 gotocleanup ; 1229 } 1230 1231if (0 == MultiByteToWideChar (codePageUtf8 ,mbErrInvalidChars ,process_cwd , 1232-1 ,process_cwd_wide ,wide_len )) { 1233result = subprocess_error_from_windows_error (GetLastError ()); 1234if (subprocess_error_unknown == result ) { 1235result = subprocess_error_spawn ; 1236 } 1237 gotocleanup ; 1238 } 1239 } 1240 1241/* Restrict inheritance to this subprocess's standard streams. Without a 1242handle list, concurrent subprocess_create calls can inherit each other's 1243temporarily-inheritable child pipe handles. */ 1244inherited_handles [inherited_handle_count ++ ]= startInfo .hStdInput ; 1245inherited_handles [inherited_handle_count ++ ]= startInfo .hStdOutput ; 1246if (startInfo .hStdError != startInfo .hStdOutput ) { 1247inherited_handles [inherited_handle_count ++ ]= startInfo .hStdError ; 1248 } 1249 1250InitializeProcThreadAttributeList (SUBPROCESS_NULL ,1 ,0 , 1251& attribute_list_size ); 1252if (0 == attribute_list_size ) { 1253result = subprocess_error_spawn ; 1254 gotocleanup ; 1255 } 1256 1257attribute_list = SUBPROCESS_PTR_CAST (LPPROC_THREAD_ATTRIBUTE_LIST , 1258_alloca (attribute_list_size )); 1259if (!attribute_list || !InitializeProcThreadAttributeList ( 1260attribute_list ,1 ,0 ,& attribute_list_size )) { 1261result = subprocess_error_spawn ; 1262 gotocleanup ; 1263 } 1264attribute_list_initialized = 1 ; 1265 1266if (!UpdateProcThreadAttribute ( 1267attribute_list ,0 ,procThreadAttributeHandleList ,inherited_handles , 1268inherited_handle_count * sizeof (inherited_handles [0 ]), 1269SUBPROCESS_NULL ,SUBPROCESS_NULL )) { 1270result = subprocess_error_spawn ; 1271 gotocleanup ; 1272 } 1273 1274startInfoEx .startupInfo = startInfo ; 1275startInfoEx .startupInfo .cb = sizeof (startInfoEx ); 1276startInfoEx .attributeList = attribute_list ; 1277flags |=extendedStartupInfoPresent ; 1278 1279if (!CreateProcessW ( 1280SUBPROCESS_NULL , 1281commandLineCombinedWide ,// command line 1282SUBPROCESS_NULL ,// process security attributes 1283SUBPROCESS_NULL ,// primary thread security attributes 12841 ,// handles are inherited 1285flags ,// creation flags 1286used_environment ,// used environment 1287process_cwd_wide ,// use specified current directory 1288SUBPROCESS_PTR_CAST (LPSTARTUPINFOW , 1289& startInfoEx ),// STARTUPINFOEX pointer 1290SUBPROCESS_PTR_CAST (LPPROCESS_INFORMATION ,& processInfo ))) { 1291result = subprocess_error_from_windows_error (GetLastError ()); 1292if (subprocess_error_unknown == result ) { 1293result = subprocess_error_spawn ; 1294 } 1295 gotocleanup ; 1296 } 1297 1298DeleteProcThreadAttributeList (attribute_list ); 1299attribute_list_initialized = 0 ; 1300 1301out_process -> hProcess = processInfo .hProcess ; 1302processInfo .hProcess = SUBPROCESS_NULL ; 1303 1304out_process -> hStdInput = startInfo .hStdInput ; 1305startInfo .hStdInput = SUBPROCESS_NULL ; 1306 1307// We don't need the handle of the primary thread in the called process. 1308CloseHandle (processInfo .hThread ); 1309processInfo .hThread = SUBPROCESS_NULL ; 1310 1311if (SUBPROCESS_NULL != startInfo .hStdOutput ) { 1312CloseHandle (startInfo .hStdOutput ); 1313 1314if (startInfo .hStdError != startInfo .hStdOutput ) { 1315CloseHandle (startInfo .hStdError ); 1316 } 1317 1318startInfo .hStdOutput = SUBPROCESS_NULL ; 1319startInfo .hStdError = SUBPROCESS_NULL ; 1320 } 1321 1322out_process -> alive = 1 ; 1323out_process -> no_wait = async_no_wait ; 1324 1325return 0 ; 1326 1327cleanup : 1328last_error = GetLastError (); 1329 1330if (attribute_list_initialized ) { 1331DeleteProcThreadAttributeList (attribute_list ); 1332 } 1333 1334if (subprocess_error_unknown == result ) { 1335result = subprocess_error_from_windows_error (last_error ); 1336 } 1337 1338if (out_process -> stdin_file ) { 1339fclose (out_process -> stdin_file ); 1340out_process -> stdin_file = SUBPROCESS_NULL ; 1341 } 1342 1343if (out_process -> stdout_file ) { 1344fclose (out_process -> stdout_file ); 1345if (out_process -> stderr_file && 1346 (out_process -> stdout_file != out_process -> stderr_file )) { 1347fclose (out_process -> stderr_file ); 1348 } 1349out_process -> stdout_file = SUBPROCESS_NULL ; 1350out_process -> stderr_file = SUBPROCESS_NULL ; 1351 } 1352 1353subprocess_close_handle (& rd ); 1354subprocess_close_handle (& wr ); 1355subprocess_close_handle (& startInfo .hStdInput ); 1356 1357if (startInfo .hStdOutput == startInfo .hStdError ) { 1358subprocess_close_handle (& startInfo .hStdOutput ); 1359startInfo .hStdError = SUBPROCESS_NULL ; 1360 }else { 1361subprocess_close_handle (& startInfo .hStdOutput ); 1362subprocess_close_handle (& startInfo .hStdError ); 1363 } 1364 1365subprocess_close_handle (& out_process -> hEventOutput ); 1366subprocess_close_handle (& out_process -> hEventError ); 1367subprocess_close_handle (& processInfo .hThread ); 1368subprocess_close_handle (& processInfo .hProcess ); 1369 1370SetLastError (last_error ); 1371 1372return result ; 1373#else 1374int stdinfd [2 ]= {-1 ,-1 }; 1375int stdoutfd [2 ]= {-1 ,-1 }; 1376int stderrfd [2 ]= {-1 ,-1 }; 1377int fd ,fd_flags ; 1378int async_no_wait ; 1379int result = subprocess_error_unknown ; 1380int saved_errno = 0 ; 1381pid_t child = 0 ; 1382extern char ** environ ; 1383char * const empty_environment [1 ]= {SUBPROCESS_NULL }; 1384char * const * used_environment ; 1385#if SUBPROCESS_SPAWN_VIA_FORK 1386/* Pipe used to relay the child's exec() errno back to the parent. */ 1387int exec_errfd [2 ]= {-1 ,-1 }; 1388#else 1389int actions_created = 0 ; 1390int posix_error ; 1391posix_spawn_file_actions_t actions ; 1392#endif 1393 1394async_no_wait = subprocess_option_enable_async_no_wait == 1395 (options & subprocess_option_enable_async_no_wait ); 1396 1397if (async_no_wait && (subprocess_option_enable_async != 1398 (options & subprocess_option_enable_async ))) { 1399errno = EINVAL ; 1400return subprocess_error_invalid_options ; 1401 } 1402 1403if (subprocess_option_inherit_environment == 1404 (options & subprocess_option_inherit_environment )) { 1405if (SUBPROCESS_NULL != environment ) { 1406errno = EINVAL ; 1407return subprocess_error_invalid_environment ; 1408 } 1409 } 1410 1411memset (out_process ,0 ,sizeof (* out_process )); 1412 1413if (0 != subprocess_pipe_cloexec (stdinfd )) { 1414saved_errno = errno ; 1415result = subprocess_error_pipe ; 1416 gotocleanup ; 1417 } 1418 1419if (0 != subprocess_pipe_cloexec (stdoutfd )) { 1420saved_errno = errno ; 1421result = subprocess_error_pipe ; 1422 gotocleanup ; 1423 } 1424 1425if (subprocess_option_combined_stdout_stderr != 1426 (options & subprocess_option_combined_stdout_stderr )) { 1427if (0 != subprocess_pipe_cloexec (stderrfd )) { 1428saved_errno = errno ; 1429result = subprocess_error_pipe ; 1430 gotocleanup ; 1431 } 1432 } 1433 1434if (environment ) { 1435#ifdef __clang__ 1436#pragma clang diagnostic push 1437#pragma clang diagnostic ignored "-Wcast-qual" 1438#pragma clang diagnostic ignored "-Wold-style-cast" 1439#endif 1440used_environment = SUBPROCESS_CONST_CAST (char * const * ,environment ); 1441#ifdef __clang__ 1442#pragma clang diagnostic pop 1443#endif 1444 }else if (subprocess_option_inherit_environment == 1445 (options & subprocess_option_inherit_environment )) { 1446used_environment = environ ; 1447 }else { 1448used_environment = empty_environment ; 1449 } 1450 1451#if SUBPROCESS_SPAWN_VIA_FORK 1452/* fork()+exec() instead of posix_spawn, so the child can chdir() first. 1453exec_errfd[1] is close-on-exec: a successful exec closes it and the parent 1454reads EOF; a failed exec writes errno through it before _exit. */ 1455if (0 != pipe (exec_errfd )) { 1456saved_errno = errno ; 1457result = subprocess_error_pipe ; 1458 gotocleanup ; 1459 } 1460 1461if (-1 == fcntl (exec_errfd [1 ],F_SETFD ,FD_CLOEXEC )) { 1462saved_errno = errno ; 1463result = subprocess_error_spawn ; 1464 gotocleanup ; 1465 } 1466 1467child = fork (); 1468 1469if (child < 0 ) { 1470saved_errno = errno ; 1471result = subprocess_error_spawn ; 1472 gotocleanup ; 1473 } 1474 1475if (0 == child ) { 1476/* Child. Everything below must stay async-signal-safe: after fork() in a 1477threaded process only such functions may be called before exec. */ 1478int child_errno ; 1479 1480close (exec_errfd [0 ]); 1481 1482if ((-1 == dup2 (stdinfd [0 ],STDIN_FILENO ))|| 1483 (-1 == dup2 (stdoutfd [1 ],STDOUT_FILENO ))) { 1484 gotochild_failed ; 1485 } 1486 1487if (subprocess_option_combined_stdout_stderr == 1488 (options & subprocess_option_combined_stdout_stderr )) { 1489if (-1 == dup2 (STDOUT_FILENO ,STDERR_FILENO )) { 1490 gotochild_failed ; 1491 } 1492 }else { 1493if (-1 == dup2 (stderrfd [1 ],STDERR_FILENO )) { 1494 gotochild_failed ; 1495 } 1496 } 1497 1498/* The originals are only closed once they have been duplicated, so that a 1499pipe end that already sits on 0, 1 or 2 is not closed out from under us. */ 1500if (stdinfd [0 ]> STDERR_FILENO ) { 1501close (stdinfd [0 ]); 1502 } 1503if (stdinfd [1 ]> STDERR_FILENO ) { 1504close (stdinfd [1 ]); 1505 } 1506if (stdoutfd [0 ]> STDERR_FILENO ) { 1507close (stdoutfd [0 ]); 1508 } 1509if (stdoutfd [1 ]> STDERR_FILENO ) { 1510close (stdoutfd [1 ]); 1511 } 1512if (stderrfd [0 ]> STDERR_FILENO ) { 1513close (stderrfd [0 ]); 1514 } 1515if (stderrfd [1 ]> STDERR_FILENO ) { 1516close (stderrfd [1 ]); 1517 } 1518 1519if (process_cwd && (0 != chdir (process_cwd ))) { 1520 gotochild_failed ; 1521 } 1522 1523#ifdef __clang__ 1524#pragma clang diagnostic push 1525#pragma clang diagnostic ignored "-Wcast-qual" 1526#pragma clang diagnostic ignored "-Wold-style-cast" 1527#endif 1528if (subprocess_option_search_user_path == 1529 (options & subprocess_option_search_user_path )) { 1530execvpe (commandLine [0 ], 1531SUBPROCESS_CONST_CAST (char * const * ,commandLine ), 1532SUBPROCESS_CONST_CAST (char * const * ,used_environment )); 1533 }else { 1534execve (commandLine [0 ], 1535SUBPROCESS_CONST_CAST (char * const * ,commandLine ), 1536SUBPROCESS_CONST_CAST (char * const * ,used_environment )); 1537 } 1538#ifdef __clang__ 1539#pragma clang diagnostic pop 1540#endif 1541 1542child_failed : 1543child_errno = errno ; 1544/* Nothing useful can be done if this write fails; the parent then sees EOF 1545and reports success, exactly as posix_spawn would without exec reporting. */ 1546 (void )!write (exec_errfd [1 ],& child_errno ,sizeof (child_errno )); 1547/* 127 is what POSIX requires posix_spawn's child to exit with when exec 1548fails, so both implementations look the same to a caller. */ 1549_exit (127 ); 1550 } 1551 1552/* Parent. */ 1553close (exec_errfd [1 ]); 1554exec_errfd [1 ]= -1 ; 1555 1556 { 1557int child_errno = 0 ; 1558ssize_t bytes_read ; 1559 1560do { 1561bytes_read = read (exec_errfd [0 ],& child_errno ,sizeof (child_errno )); 1562 }while ((-1 == bytes_read )&& (EINTR == errno )); 1563 1564close (exec_errfd [0 ]); 1565exec_errfd [0 ]= -1 ; 1566 1567if (bytes_read == (ssize_t )sizeof (child_errno )) { 1568/* exec failed in the child. Reap it and surface the reason. */ 1569while ((-1 == waitpid (child ,SUBPROCESS_NULL ,0 ))&& (EINTR == errno )) { 1570 } 1571child = 0 ; 1572saved_errno = child_errno ; 1573result = subprocess_error_from_errno (child_errno ); 1574if (subprocess_error_unknown == result ) { 1575result = subprocess_error_spawn ; 1576 } 1577 gotocleanup ; 1578 } 1579 } 1580#else 1581posix_error = posix_spawn_file_actions_init (& actions ); 1582if (0 != posix_error ) { 1583saved_errno = posix_error ; 1584result = subprocess_error_from_errno (posix_error ); 1585if (subprocess_error_unknown == result ) { 1586result = subprocess_error_spawn ; 1587 } 1588 gotocleanup ; 1589 } 1590actions_created = 1 ; 1591 1592// Set working directory 1593if (process_cwd ) { 1594#if SUBPROCESS_ADDCHDIR_IS_POSIX 1595posix_error = posix_spawn_file_actions_addchdir (& actions ,process_cwd ); 1596#elif !SUBPROCESS_HAVE_CWD 1597posix_error = ENOSYS ; 1598#else 1599#if defined(__APPLE__ )&& defined(__clang__ ) 1600#pragma clang diagnostic push 1601#pragma clang diagnostic ignored "-Wdeprecated-declarations" 1602#endif 1603posix_error = posix_spawn_file_actions_addchdir_np (& actions ,process_cwd ); 1604#if defined(__APPLE__ )&& defined(__clang__ ) 1605#pragma clang diagnostic pop 1606#endif 1607#endif 1608if (0 != posix_error ) { 1609saved_errno = posix_error ; 1610result = subprocess_error_from_errno (posix_error ); 1611if (subprocess_error_unknown == result ) { 1612result = subprocess_error_spawn ; 1613 } 1614 gotocleanup ; 1615 } 1616 } 1617 1618// Close the stdin write end 1619posix_error = posix_spawn_file_actions_addclose (& actions ,stdinfd [1 ]); 1620if (0 != posix_error ) { 1621saved_errno = posix_error ; 1622result = subprocess_error_from_errno (posix_error ); 1623if (subprocess_error_unknown == result ) { 1624result = subprocess_error_spawn ; 1625 } 1626 gotocleanup ; 1627 } 1628 1629// Map the read end to stdin 1630posix_error = 1631posix_spawn_file_actions_adddup2 (& actions ,stdinfd [0 ],STDIN_FILENO ); 1632if (0 != posix_error ) { 1633saved_errno = posix_error ; 1634result = subprocess_error_from_errno (posix_error ); 1635if (subprocess_error_unknown == result ) { 1636result = subprocess_error_spawn ; 1637 } 1638 gotocleanup ; 1639 } 1640 1641// Close the stdout read end 1642posix_error = posix_spawn_file_actions_addclose (& actions ,stdoutfd [0 ]); 1643if (0 != posix_error ) { 1644saved_errno = posix_error ; 1645result = subprocess_error_from_errno (posix_error ); 1646if (subprocess_error_unknown == result ) { 1647result = subprocess_error_spawn ; 1648 } 1649 gotocleanup ; 1650 } 1651 1652// Map the write end to stdout 1653posix_error = 1654posix_spawn_file_actions_adddup2 (& actions ,stdoutfd [1 ],STDOUT_FILENO ); 1655if (0 != posix_error ) { 1656saved_errno = posix_error ; 1657result = subprocess_error_from_errno (posix_error ); 1658if (subprocess_error_unknown == result ) { 1659result = subprocess_error_spawn ; 1660 } 1661 gotocleanup ; 1662 } 1663 1664if (subprocess_option_combined_stdout_stderr == 1665 (options & subprocess_option_combined_stdout_stderr )) { 1666posix_error = posix_spawn_file_actions_adddup2 (& actions ,STDOUT_FILENO , 1667STDERR_FILENO ); 1668if (0 != posix_error ) { 1669saved_errno = posix_error ; 1670result = subprocess_error_from_errno (posix_error ); 1671if (subprocess_error_unknown == result ) { 1672result = subprocess_error_spawn ; 1673 } 1674 gotocleanup ; 1675 } 1676 }else { 1677// Close the stderr read end 1678posix_error = posix_spawn_file_actions_addclose (& actions ,stderrfd [0 ]); 1679if (0 != posix_error ) { 1680saved_errno = posix_error ; 1681result = subprocess_error_from_errno (posix_error ); 1682if (subprocess_error_unknown == result ) { 1683result = subprocess_error_spawn ; 1684 } 1685 gotocleanup ; 1686 } 1687// Map the write end to stdout 1688posix_error = posix_spawn_file_actions_adddup2 (& actions ,stderrfd [1 ], 1689STDERR_FILENO ); 1690if (0 != posix_error ) { 1691saved_errno = posix_error ; 1692result = subprocess_error_from_errno (posix_error ); 1693if (subprocess_error_unknown == result ) { 1694result = subprocess_error_spawn ; 1695 } 1696 gotocleanup ; 1697 } 1698 } 1699 1700#ifdef __clang__ 1701#pragma clang diagnostic push 1702#pragma clang diagnostic ignored "-Wcast-qual" 1703#pragma clang diagnostic ignored "-Wold-style-cast" 1704#endif 1705if (subprocess_option_search_user_path == 1706 (options & subprocess_option_search_user_path )) { 1707posix_error = posix_spawnp (& child ,commandLine [0 ],& actions , 1708SUBPROCESS_NULL , 1709SUBPROCESS_CONST_CAST (char * const * ,commandLine ), 1710used_environment ); 1711if (0 != posix_error ) { 1712saved_errno = posix_error ; 1713result = subprocess_error_from_errno (posix_error ); 1714if (subprocess_error_unknown == result ) { 1715result = subprocess_error_spawn ; 1716 } 1717 gotocleanup ; 1718 } 1719 }else { 1720#if !SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1721/* posix_spawn cannot tell us the exec failed, so check up front */ 1722if (0 != access (commandLine [0 ],X_OK )) { 1723saved_errno = errno ; 1724result = subprocess_error_from_errno (saved_errno ); 1725if (subprocess_error_unknown == result ) { 1726result = subprocess_error_spawn ; 1727 } 1728 gotocleanup ; 1729 } 1730#endif 1731posix_error = posix_spawn (& child ,commandLine [0 ],& actions , 1732SUBPROCESS_NULL , 1733SUBPROCESS_CONST_CAST (char * const * ,commandLine ), 1734used_environment ); 1735if (0 != posix_error ) { 1736saved_errno = posix_error ; 1737result = subprocess_error_from_errno (posix_error ); 1738if (subprocess_error_unknown == result ) { 1739result = subprocess_error_spawn ; 1740 } 1741 gotocleanup ; 1742 } 1743 } 1744#ifdef __clang__ 1745#pragma clang diagnostic pop 1746#endif 1747#endif /* SUBPROCESS_SPAWN_VIA_FORK */ 1748 1749// Close the stdin read end 1750close (stdinfd [0 ]); 1751stdinfd [0 ]= -1 ; 1752// Store the stdin write end 1753out_process -> stdin_file = fdopen (stdinfd [1 ],"wb" ); 1754if (SUBPROCESS_NULL == out_process -> stdin_file ) { 1755saved_errno = errno ; 1756result = subprocess_error_from_errno (saved_errno ); 1757 gotocleanup ; 1758 } 1759stdinfd [1 ]= -1 ; 1760 1761// Close the stdout write end 1762close (stdoutfd [1 ]); 1763stdoutfd [1 ]= -1 ; 1764// Store the stdout read end 1765out_process -> stdout_file = fdopen (stdoutfd [0 ],"rb" ); 1766if (SUBPROCESS_NULL == out_process -> stdout_file ) { 1767saved_errno = errno ; 1768result = subprocess_error_from_errno (saved_errno ); 1769 gotocleanup ; 1770 } 1771stdoutfd [0 ]= -1 ; 1772 1773// Set non blocking if we are async and asked not to wait. 1774if (async_no_wait ) { 1775fd = fileno (out_process -> stdout_file ); 1776fd_flags = fcntl (fd ,F_GETFL ,0 ); 1777fcntl (fd ,F_SETFL ,fd_flags |O_NONBLOCK ); 1778 } 1779 1780if (subprocess_option_combined_stdout_stderr == 1781 (options & subprocess_option_combined_stdout_stderr )) { 1782out_process -> stderr_file = out_process -> stdout_file ; 1783 }else { 1784// Close the stderr write end 1785close (stderrfd [1 ]); 1786stderrfd [1 ]= -1 ; 1787// Store the stderr read end 1788out_process -> stderr_file = fdopen (stderrfd [0 ],"rb" ); 1789if (SUBPROCESS_NULL == out_process -> stderr_file ) { 1790saved_errno = errno ; 1791result = subprocess_error_from_errno (saved_errno ); 1792 gotocleanup ; 1793 } 1794stderrfd [0 ]= -1 ; 1795 1796// Set non blocking if we are async and asked not to wait. 1797if (async_no_wait ) { 1798fd = fileno (out_process -> stderr_file ); 1799fd_flags = fcntl (fd ,F_GETFL ,0 ); 1800fcntl (fd ,F_SETFL ,fd_flags |O_NONBLOCK ); 1801 } 1802 } 1803 1804// Store the child's pid 1805out_process -> child = child ; 1806child = 0 ; 1807 1808out_process -> alive = 1 ; 1809out_process -> no_wait = async_no_wait ; 1810 1811result = 0 ; 1812 1813cleanup : 1814if ((0 != result )&& (0 == saved_errno )) { 1815saved_errno = errno ; 1816 } 1817 1818if ((subprocess_error_unknown == result )&& (0 != saved_errno )) { 1819result = subprocess_error_from_errno (saved_errno ); 1820 } 1821 1822#if SUBPROCESS_SPAWN_VIA_FORK 1823if (-1 != exec_errfd [0 ]) { 1824close (exec_errfd [0 ]); 1825exec_errfd [0 ]= -1 ; 1826 } 1827 1828if (-1 != exec_errfd [1 ]) { 1829close (exec_errfd [1 ]); 1830exec_errfd [1 ]= -1 ; 1831 } 1832#else 1833if (actions_created ) { 1834posix_spawn_file_actions_destroy (& actions ); 1835 } 1836#endif 1837 1838if (0 != result ) { 1839if (child ) { 1840kill (child ,9 ); 1841waitpid (child ,SUBPROCESS_NULL ,0 ); 1842 } 1843 1844if (out_process -> stdin_file ) { 1845fclose (out_process -> stdin_file ); 1846out_process -> stdin_file = SUBPROCESS_NULL ; 1847 } 1848 1849if (out_process -> stdout_file ) { 1850fclose (out_process -> stdout_file ); 1851if (out_process -> stderr_file && 1852 (out_process -> stdout_file != out_process -> stderr_file )) { 1853fclose (out_process -> stderr_file ); 1854 } 1855out_process -> stdout_file = SUBPROCESS_NULL ; 1856out_process -> stderr_file = SUBPROCESS_NULL ; 1857 } 1858 } 1859 1860if (-1 != stdinfd [0 ]) { 1861close (stdinfd [0 ]); 1862 } 1863if (-1 != stdinfd [1 ]) { 1864close (stdinfd [1 ]); 1865 } 1866if (-1 != stdoutfd [0 ]) { 1867close (stdoutfd [0 ]); 1868 } 1869if (-1 != stdoutfd [1 ]) { 1870close (stdoutfd [1 ]); 1871 } 1872if (-1 != stderrfd [0 ]) { 1873close (stderrfd [0 ]); 1874 } 1875if (-1 != stderrfd [1 ]) { 1876close (stderrfd [1 ]); 1877 } 1878 1879if ((0 != result )&& (0 != saved_errno )) { 1880errno = saved_errno ; 1881 } 1882 1883return result ; 1884#endif 1885} 1886 1887FILE * subprocess_stdin (const struct subprocess_s * const process ) { 1888return process -> stdin_file ; 1889} 1890 1891FILE * subprocess_stdout (const struct subprocess_s * const process ) { 1892return process -> stdout_file ; 1893} 1894 1895FILE * subprocess_stderr (const struct subprocess_s * const process ) { 1896if (process -> stdout_file != process -> stderr_file ) { 1897return process -> stderr_file ; 1898 }else { 1899return SUBPROCESS_NULL ; 1900 } 1901} 1902 1903int subprocess_join (struct subprocess_s * const process , 1904int * const out_return_code ) { 1905#if defined(_WIN32 ) 1906const unsigned long infinite = 0xFFFFFFFF ; 1907 1908if (process -> stdin_file ) { 1909fclose (process -> stdin_file ); 1910process -> stdin_file = SUBPROCESS_NULL ; 1911 } 1912 1913if (process -> hStdInput ) { 1914CloseHandle (process -> hStdInput ); 1915process -> hStdInput = SUBPROCESS_NULL ; 1916 } 1917 1918WaitForSingleObject (process -> hProcess ,infinite ); 1919 1920if (out_return_code ) { 1921if (!GetExitCodeProcess ( 1922process -> hProcess , 1923SUBPROCESS_PTR_CAST (unsigned long * ,out_return_code ))) { 1924return -1 ; 1925 } 1926 } 1927 1928process -> alive = 0 ; 1929 1930return 0 ; 1931#else 1932int status ; 1933 1934if (process -> stdin_file ) { 1935fclose (process -> stdin_file ); 1936process -> stdin_file = SUBPROCESS_NULL ; 1937 } 1938 1939if (process -> child ) { 1940if (process -> child != waitpid (process -> child ,& status ,0 )) { 1941return -1 ; 1942 } 1943 1944process -> child = 0 ; 1945 1946if (WIFEXITED (status )) { 1947process -> return_status = WEXITSTATUS (status ); 1948 }else { 1949process -> return_status = EXIT_FAILURE ; 1950 } 1951 1952process -> alive = 0 ; 1953 } 1954 1955if (out_return_code ) { 1956* out_return_code = process -> return_status ; 1957 } 1958 1959return 0 ; 1960#endif 1961} 1962 1963int subprocess_destroy (struct subprocess_s * const process ) { 1964if (process -> stdin_file ) { 1965fclose (process -> stdin_file ); 1966process -> stdin_file = SUBPROCESS_NULL ; 1967 } 1968 1969if (process -> stdout_file ) { 1970fclose (process -> stdout_file ); 1971 1972if (process -> stdout_file != process -> stderr_file ) { 1973fclose (process -> stderr_file ); 1974 } 1975 1976process -> stdout_file = SUBPROCESS_NULL ; 1977process -> stderr_file = SUBPROCESS_NULL ; 1978 } 1979 1980#if defined(_WIN32 ) 1981if (process -> hProcess ) { 1982CloseHandle (process -> hProcess ); 1983process -> hProcess = SUBPROCESS_NULL ; 1984 1985if (process -> hStdInput ) { 1986CloseHandle (process -> hStdInput ); 1987 } 1988 1989if (process -> hEventOutput ) { 1990CloseHandle (process -> hEventOutput ); 1991 } 1992 1993if (process -> hEventError ) { 1994CloseHandle (process -> hEventError ); 1995 } 1996 } 1997#endif 1998 1999return 0 ; 2000} 2001 2002int subprocess_terminate (struct subprocess_s * const process ) { 2003#if defined(_WIN32 ) 2004unsigned int killed_process_exit_code ; 2005int success_terminate ; 2006int windows_call_result ; 2007 2008killed_process_exit_code = 99 ; 2009windows_call_result = 2010TerminateProcess (process -> hProcess ,killed_process_exit_code ); 2011success_terminate = (windows_call_result == 0 ) ?1 :0 ; 2012return success_terminate ; 2013#else 2014int result ; 2015result = kill (process -> child ,9 ); 2016return result ; 2017#endif 2018} 2019 2020unsigned subprocess_read_stdout (struct subprocess_s * const process , 2021char * const buffer ,unsigned size ) { 2022#if defined(_WIN32 ) 2023void * handle ; 2024unsigned long bytes_available = 0 ; 2025unsigned long bytes_read = 0 ; 2026struct subprocess_overlapped_s overlapped = {0 ,0 , {{0 ,0 }},SUBPROCESS_NULL }; 2027overlapped .hEvent = process -> hEventOutput ; 2028 2029handle = SUBPROCESS_PTR_CAST (void * , 2030_get_osfhandle (_fileno (process -> stdout_file ))); 2031 2032if (process -> no_wait ) { 2033if (!PeekNamedPipe (handle ,SUBPROCESS_NULL ,0 ,SUBPROCESS_NULL , 2034& bytes_available ,SUBPROCESS_NULL )) { 2035return 0 ; 2036 } 2037 2038if (0 == bytes_available ) { 2039return 0 ; 2040 } 2041 } 2042 2043if (!ReadFile (handle ,buffer ,size ,& bytes_read , 2044SUBPROCESS_PTR_CAST (LPOVERLAPPED ,& overlapped ))) { 2045const unsigned long errorIoPending = 997 ; 2046unsigned long error = GetLastError (); 2047 2048// Means we've got an async read! 2049if (error == errorIoPending ) { 2050const uintptr_t statusPending = 0x00000103 ; 2051 2052const int wait = statusPending == overlapped .Internal ; 2053 2054if (!GetOverlappedResult (handle , 2055SUBPROCESS_PTR_CAST (LPOVERLAPPED ,& overlapped ), 2056& bytes_read ,wait )) { 2057const unsigned long errorHandleEOF = 38 ; 2058const unsigned long errorBrokenPipe = 109 ; 2059const unsigned long errorIoIncomplete = 996 ; 2060error = GetLastError (); 2061 2062if ((errorHandleEOF != error )&& (errorBrokenPipe != error )&& 2063 (errorIoIncomplete != error )) { 2064return 0 ; 2065 } 2066 } 2067 } 2068 } 2069 2070return SUBPROCESS_CAST (unsigned ,bytes_read ); 2071#else 2072const int fd = fileno (process -> stdout_file ); 2073const ssize_t bytes_read = read (fd ,buffer ,size ); 2074 2075if (bytes_read < 0 ) { 2076return 0 ; 2077 } 2078 2079return SUBPROCESS_CAST (unsigned ,bytes_read ); 2080#endif 2081} 2082 2083unsigned subprocess_read_stderr (struct subprocess_s * const process , 2084char * const buffer ,unsigned size ) { 2085#if defined(_WIN32 ) 2086void * handle ; 2087unsigned long bytes_available = 0 ; 2088unsigned long bytes_read = 0 ; 2089struct subprocess_overlapped_s overlapped = {0 ,0 , {{0 ,0 }},SUBPROCESS_NULL }; 2090overlapped .hEvent = process -> hEventError ; 2091 2092handle = SUBPROCESS_PTR_CAST (void * , 2093_get_osfhandle (_fileno (process -> stderr_file ))); 2094 2095if (process -> no_wait ) { 2096if (!PeekNamedPipe (handle ,SUBPROCESS_NULL ,0 ,SUBPROCESS_NULL , 2097& bytes_available ,SUBPROCESS_NULL )) { 2098return 0 ; 2099 } 2100 2101if (0 == bytes_available ) { 2102return 0 ; 2103 } 2104 } 2105 2106if (!ReadFile (handle ,buffer ,size ,& bytes_read , 2107SUBPROCESS_PTR_CAST (LPOVERLAPPED ,& overlapped ))) { 2108const unsigned long errorIoPending = 997 ; 2109unsigned long error = GetLastError (); 2110 2111// Means we've got an async read! 2112if (error == errorIoPending ) { 2113if (!GetOverlappedResult (handle , 2114SUBPROCESS_PTR_CAST (LPOVERLAPPED ,& overlapped ), 2115& bytes_read ,1 )) { 2116const unsigned long errorIoIncomplete = 996 ; 2117const unsigned long errorHandleEOF = 38 ; 2118error = GetLastError (); 2119 2120if ((error != errorIoIncomplete )&& (error != errorHandleEOF )) { 2121return 0 ; 2122 } 2123 } 2124 } 2125 } 2126 2127return SUBPROCESS_CAST (unsigned ,bytes_read ); 2128#else 2129const int fd = fileno (process -> stderr_file ); 2130const ssize_t bytes_read = read (fd ,buffer ,size ); 2131 2132if (bytes_read < 0 ) { 2133return 0 ; 2134 } 2135 2136return SUBPROCESS_CAST (unsigned ,bytes_read ); 2137#endif 2138} 2139 2140int subprocess_alive (struct subprocess_s * const process ) { 2141int is_alive = SUBPROCESS_CAST (int ,process -> alive ); 2142 2143if (!is_alive ) { 2144return 0 ; 2145 } 2146#if defined(_WIN32 ) 2147 { 2148const unsigned long zero = 0x0 ; 2149const unsigned long wait_object_0 = 0x00000000L ; 2150 2151is_alive = wait_object_0 != WaitForSingleObject (process -> hProcess ,zero ); 2152 } 2153#else 2154 { 2155int status ; 2156is_alive = 0 == waitpid (process -> child ,& status ,WNOHANG ); 2157 2158// If the process was successfully waited on we need to cleanup now. 2159if (!is_alive ) { 2160if (WIFEXITED (status )) { 2161process -> return_status = WEXITSTATUS (status ); 2162 }else { 2163process -> return_status = EXIT_FAILURE ; 2164 } 2165 2166// Since we've already successfully waited on the process, we need to wipe 2167// the child now. 2168process -> child = 0 ; 2169 2170if (subprocess_join (process ,SUBPROCESS_NULL )) { 2171return -1 ; 2172 } 2173 } 2174 } 2175#endif 2176 2177if (!is_alive ) { 2178process -> alive = 0 ; 2179 } 2180 2181return is_alive ; 2182} 2183 2184#if defined(__clang__ ) 2185#if __has_warning ("- Wunsafe - buffer - usage ") 2186#pragma clang diagnostic pop 2187#endif 2188#endif 2189 2190#if defined(__cplusplus ) 2191} // extern "C" 2192#endif 2193 2194#endif /* SHEREDOM_SUBPROCESS_H_INCLUDED */