gitout

A static git page generator
git clone https://git.bracken.jp/gitout.git
Log | Files | Refs | README | LICENSE

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 */