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 compiler found!
  71#endif
  72
  73struct subprocess_s;
  74
  75enum 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)
  96extern "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.
 107subprocess_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.
 125subprocess_weak int
 126subprocess_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.
 136subprocess_pure subprocess_weak FILE *
 137subprocess_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.
 145subprocess_pure subprocess_weak FILE *
 146subprocess_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.
 158subprocess_pure subprocess_weak FILE *
 159subprocess_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.
 168subprocess_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.
 177subprocess_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).
 185subprocess_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/// conjunction with this method.
 197subprocess_weak unsigned
 198subprocess_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/// conjunction with this method.
 211subprocess_weak unsigned
 212subprocess_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.
 218subprocess_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
 245typedef __int64 subprocess_intptr_t;
 246typedef unsigned __int64 subprocess_size_t;
 247#else
 248typedef int subprocess_intptr_t;
 249typedef unsigned int subprocess_size_t;
 250#endif
 251#else
 252#include <inttypes.h>
 253
 254typedef intptr_t subprocess_intptr_t;
 255typedef 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
 263typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
 264typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
 265typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
 266typedef 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
 280struct subprocess_subprocess_information_s {
 281  void *hProcess;
 282  void *hThread;
 283  unsigned long dwProcessId;
 284  unsigned long dwThreadId;
 285};
 286
 287struct subprocess_security_attributes_s {
 288  unsigned long nLength;
 289  void *lpSecurityDescriptor;
 290  int bInheritHandle;
 291};
 292
 293struct 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
 314struct 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
 380SUBPROCESS_DLLIMPORT int __cdecl _fileno(FILE *);
 381SUBPROCESS_DLLIMPORT int __cdecl _open_osfhandle(subprocess_intptr_t, int);
 382SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl _get_osfhandle(int);
 383
 384#ifndef __MINGW32__
 385void *__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
 395typedef size_t subprocess_size_t;
 396#endif
 397
 398#ifdef __clang__
 399#pragma clang diagnostic push
 400#pragma clang diagnostic ignored "-Wpadded"
 401#endif
 402struct 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(__clang__)
 424#if __has_warning("-Wunsafe-buffer-usage")
 425#pragma clang diagnostic push
 426#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
 427#endif
 428#endif
 429
 430#if defined(_WIN32)
 431subprocess_weak int subprocess_create_named_pipe_helper(void **rd, void **wr);
 432int subprocess_create_named_pipe_helper(void **rd, void **wr) {
 433  const unsigned long pipeAccessInbound = 0x00000001;
 434  const unsigned long fileFlagOverlapped = 0x40000000;
 435  const unsigned long pipeTypeByte = 0x00000000;
 436  const unsigned long pipeWait = 0x00000000;
 437  const unsigned long genericWrite = 0x40000000;
 438  const unsigned long openExisting = 3;
 439  const unsigned long fileAttributeNormal = 0x00000080;
 440  const void *const invalidHandleValue =
 441      SUBPROCESS_PTR_CAST(void *, ~(SUBPROCESS_CAST(subprocess_intptr_t, 0)));
 442  struct subprocess_security_attributes_s saAttr = {sizeof(saAttr),
 443                                                    SUBPROCESS_NULL, 1};
 444  char name[256] = {0};
 445  static subprocess_tls long index = 0;
 446  const long unique = index++;
 447
 448#if defined(_MSC_VER) && _MSC_VER < 1900
 449#pragma warning(push, 1)
 450#pragma warning(disable : 4996)
 451  _snprintf(name, sizeof(name) - 1,
 452            "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
 453            GetCurrentProcessId(), GetCurrentThreadId(), unique);
 454#pragma warning(pop)
 455#else
 456  snprintf(name, sizeof(name) - 1,
 457           "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
 458           GetCurrentProcessId(), GetCurrentThreadId(), unique);
 459#endif
 460
 461  *rd =
 462      CreateNamedPipeA(name, pipeAccessInbound | fileFlagOverlapped,
 463                       pipeTypeByte | pipeWait, 1, 4096, 4096, SUBPROCESS_NULL,
 464                       SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr));
 465
 466  if (invalidHandleValue == *rd) {
 467    return -1;
 468  }
 469
 470  *wr = CreateFileA(name, genericWrite, SUBPROCESS_NULL,
 471                    SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr),
 472                    openExisting, fileAttributeNormal, SUBPROCESS_NULL);
 473
 474  if (invalidHandleValue == *wr) {
 475    return -1;
 476  }
 477
 478  return 0;
 479}
 480#endif
 481
 482int subprocess_create(const char *const commandLine[], int options,
 483                      struct subprocess_s *const out_process) {
 484  return subprocess_create_ex(commandLine, options, SUBPROCESS_NULL,
 485                              out_process);
 486}
 487
 488int subprocess_create_ex(const char *const commandLine[], int options,
 489                         const char *const environment[],
 490                         struct subprocess_s *const out_process) {
 491#if defined(_WIN32)
 492  int fd;
 493  void *rd, *wr;
 494  char *commandLineCombined;
 495  subprocess_size_t len;
 496  int i, j;
 497  int need_quoting;
 498  unsigned long flags = 0;
 499  const unsigned long startFUseStdHandles = 0x00000100;
 500  const unsigned long handleFlagInherit = 0x00000001;
 501  const unsigned long createNoWindow = 0x08000000;
 502  struct subprocess_subprocess_information_s processInfo;
 503  struct subprocess_security_attributes_s saAttr = {sizeof(saAttr),
 504                                                    SUBPROCESS_NULL, 1};
 505  char *used_environment = SUBPROCESS_NULL;
 506  struct subprocess_startup_info_s startInfo = {0,
 507                                                SUBPROCESS_NULL,
 508                                                SUBPROCESS_NULL,
 509                                                SUBPROCESS_NULL,
 510                                                0,
 511                                                0,
 512                                                0,
 513                                                0,
 514                                                0,
 515                                                0,
 516                                                0,
 517                                                0,
 518                                                0,
 519                                                0,
 520                                                SUBPROCESS_NULL,
 521                                                SUBPROCESS_NULL,
 522                                                SUBPROCESS_NULL,
 523                                                SUBPROCESS_NULL};
 524
 525  startInfo.cb = sizeof(startInfo);
 526  startInfo.dwFlags = startFUseStdHandles;
 527
 528  if (subprocess_option_no_window == (options & subprocess_option_no_window)) {
 529    flags |= createNoWindow;
 530  }
 531
 532  if (subprocess_option_inherit_environment !=
 533      (options & subprocess_option_inherit_environment)) {
 534    if (SUBPROCESS_NULL == environment) {
 535      used_environment = SUBPROCESS_CONST_CAST(char *, "\0\0");
 536    } else {
 537      // We always end with two null terminators.
 538      len = 2;
 539
 540      for (i = 0; environment[i]; i++) {
 541        for (j = 0; '\0' != environment[i][j]; j++) {
 542          len++;
 543        }
 544
 545        // For the null terminator too.
 546        len++;
 547      }
 548
 549      used_environment = SUBPROCESS_CAST(char *, _alloca(len));
 550
 551      // Re-use len for the insertion position
 552      len = 0;
 553
 554      for (i = 0; environment[i]; i++) {
 555        for (j = 0; '\0' != environment[i][j]; j++) {
 556          used_environment[len++] = environment[i][j];
 557        }
 558
 559        used_environment[len++] = '\0';
 560      }
 561
 562      // End with the two null terminators.
 563      used_environment[len++] = '\0';
 564      used_environment[len++] = '\0';
 565    }
 566  } else {
 567    if (SUBPROCESS_NULL != environment) {
 568      return -1;
 569    }
 570  }
 571
 572  if (!CreatePipe(&rd, &wr, SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr),
 573                  0)) {
 574    return -1;
 575  }
 576
 577  if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
 578    return -1;
 579  }
 580
 581  fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, wr), 0);
 582
 583  if (-1 != fd) {
 584    out_process->stdin_file = _fdopen(fd, "wb");
 585
 586    if (SUBPROCESS_NULL == out_process->stdin_file) {
 587      return -1;
 588    }
 589  }
 590
 591  startInfo.hStdInput = rd;
 592
 593  if (options & subprocess_option_enable_async) {
 594    if (subprocess_create_named_pipe_helper(&rd, &wr)) {
 595      return -1;
 596    }
 597  } else {
 598    if (!CreatePipe(&rd, &wr,
 599                    SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) {
 600      return -1;
 601    }
 602  }
 603
 604  if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
 605    return -1;
 606  }
 607
 608  fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0);
 609
 610  if (-1 != fd) {
 611    out_process->stdout_file = _fdopen(fd, "rb");
 612
 613    if (SUBPROCESS_NULL == out_process->stdout_file) {
 614      return -1;
 615    }
 616  }
 617
 618  startInfo.hStdOutput = wr;
 619
 620  if (subprocess_option_combined_stdout_stderr ==
 621      (options & subprocess_option_combined_stdout_stderr)) {
 622    out_process->stderr_file = out_process->stdout_file;
 623    startInfo.hStdError = startInfo.hStdOutput;
 624  } else {
 625    if (options & subprocess_option_enable_async) {
 626      if (subprocess_create_named_pipe_helper(&rd, &wr)) {
 627        return -1;
 628      }
 629    } else {
 630      if (!CreatePipe(&rd, &wr,
 631                      SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) {
 632        return -1;
 633      }
 634    }
 635
 636    if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
 637      return -1;
 638    }
 639
 640    fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0);
 641
 642    if (-1 != fd) {
 643      out_process->stderr_file = _fdopen(fd, "rb");
 644
 645      if (SUBPROCESS_NULL == out_process->stderr_file) {
 646        return -1;
 647      }
 648    }
 649
 650    startInfo.hStdError = wr;
 651  }
 652
 653  if (options & subprocess_option_enable_async) {
 654    out_process->hEventOutput =
 655        CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1,
 656                     SUBPROCESS_NULL);
 657    out_process->hEventError =
 658        CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1,
 659                     SUBPROCESS_NULL);
 660  } else {
 661    out_process->hEventOutput = SUBPROCESS_NULL;
 662    out_process->hEventError = SUBPROCESS_NULL;
 663  }
 664
 665  // Combine commandLine together into a single string
 666  len = 0;
 667  for (i = 0; commandLine[i]; i++) {
 668    // for the trailing \0
 669    len++;
 670
 671    // Quote the argument if it has a space in it
 672    if (strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL ||
 673        commandLine[i][0] == SUBPROCESS_NULL)
 674      len += 2;
 675
 676    for (j = 0; '\0' != commandLine[i][j]; j++) {
 677      switch (commandLine[i][j]) {
 678      default:
 679        break;
 680      case '\\':
 681        if (commandLine[i][j + 1] == '"') {
 682          len++;
 683        }
 684
 685        break;
 686      case '"':
 687        len++;
 688        break;
 689      }
 690      len++;
 691    }
 692  }
 693
 694  commandLineCombined = SUBPROCESS_CAST(char *, _alloca(len));
 695
 696  if (!commandLineCombined) {
 697    return -1;
 698  }
 699
 700  // Gonna re-use len to store the write index into commandLineCombined
 701  len = 0;
 702
 703  for (i = 0; commandLine[i]; i++) {
 704    if (0 != i) {
 705      commandLineCombined[len++] = ' ';
 706    }
 707
 708    need_quoting = strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL ||
 709                   commandLine[i][0] == SUBPROCESS_NULL;
 710    if (need_quoting) {
 711      commandLineCombined[len++] = '"';
 712    }
 713
 714    for (j = 0; '\0' != commandLine[i][j]; j++) {
 715      switch (commandLine[i][j]) {
 716      default:
 717        break;
 718      case '\\':
 719        if (commandLine[i][j + 1] == '"') {
 720          commandLineCombined[len++] = '\\';
 721        }
 722
 723        break;
 724      case '"':
 725        commandLineCombined[len++] = '\\';
 726        break;
 727      }
 728
 729      commandLineCombined[len++] = commandLine[i][j];
 730    }
 731    if (need_quoting) {
 732      commandLineCombined[len++] = '"';
 733    }
 734  }
 735
 736  commandLineCombined[len] = '\0';
 737
 738  if (!CreateProcessA(
 739          SUBPROCESS_NULL,
 740          commandLineCombined, // command line
 741          SUBPROCESS_NULL,     // process security attributes
 742          SUBPROCESS_NULL,     // primary thread security attributes
 743          1,                   // handles are inherited
 744          flags,               // creation flags
 745          used_environment,    // used environment
 746          SUBPROCESS_NULL,     // use parent's current directory
 747          SUBPROCESS_PTR_CAST(LPSTARTUPINFOA,
 748                              &startInfo), // STARTUPINFO pointer
 749          SUBPROCESS_PTR_CAST(LPPROCESS_INFORMATION, &processInfo))) {
 750    return -1;
 751  }
 752
 753  out_process->hProcess = processInfo.hProcess;
 754
 755  out_process->hStdInput = startInfo.hStdInput;
 756
 757  // We don't need the handle of the primary thread in the called process.
 758  CloseHandle(processInfo.hThread);
 759
 760  if (SUBPROCESS_NULL != startInfo.hStdOutput) {
 761    CloseHandle(startInfo.hStdOutput);
 762
 763    if (startInfo.hStdError != startInfo.hStdOutput) {
 764      CloseHandle(startInfo.hStdError);
 765    }
 766  }
 767
 768  out_process->alive = 1;
 769
 770  return 0;
 771#else
 772  int stdinfd[2];
 773  int stdoutfd[2];
 774  int stderrfd[2];
 775  pid_t child;
 776  extern char **environ;
 777  char *const empty_environment[1] = {SUBPROCESS_NULL};
 778  posix_spawn_file_actions_t actions;
 779  char *const *used_environment;
 780
 781  if (subprocess_option_inherit_environment ==
 782      (options & subprocess_option_inherit_environment)) {
 783    if (SUBPROCESS_NULL != environment) {
 784      return -1;
 785    }
 786  }
 787
 788  if (0 != pipe(stdinfd)) {
 789    return -1;
 790  }
 791
 792  if (0 != pipe(stdoutfd)) {
 793    return -1;
 794  }
 795
 796  if (subprocess_option_combined_stdout_stderr !=
 797      (options & subprocess_option_combined_stdout_stderr)) {
 798    if (0 != pipe(stderrfd)) {
 799      return -1;
 800    }
 801  }
 802
 803  if (environment) {
 804#ifdef __clang__
 805#pragma clang diagnostic push
 806#pragma clang diagnostic ignored "-Wcast-qual"
 807#pragma clang diagnostic ignored "-Wold-style-cast"
 808#endif
 809    used_environment = SUBPROCESS_CONST_CAST(char *const *, environment);
 810#ifdef __clang__
 811#pragma clang diagnostic pop
 812#endif
 813  } else if (subprocess_option_inherit_environment ==
 814             (options & subprocess_option_inherit_environment)) {
 815    used_environment = environ;
 816  } else {
 817    used_environment = empty_environment;
 818  }
 819
 820  if (0 != posix_spawn_file_actions_init(&actions)) {
 821    return -1;
 822  }
 823
 824  // Close the stdin write end
 825  if (0 != posix_spawn_file_actions_addclose(&actions, stdinfd[1])) {
 826    posix_spawn_file_actions_destroy(&actions);
 827    return -1;
 828  }
 829
 830  // Map the read end to stdin
 831  if (0 !=
 832      posix_spawn_file_actions_adddup2(&actions, stdinfd[0], STDIN_FILENO)) {
 833    posix_spawn_file_actions_destroy(&actions);
 834    return -1;
 835  }
 836
 837  // Close the stdout read end
 838  if (0 != posix_spawn_file_actions_addclose(&actions, stdoutfd[0])) {
 839    posix_spawn_file_actions_destroy(&actions);
 840    return -1;
 841  }
 842
 843  // Map the write end to stdout
 844  if (0 !=
 845      posix_spawn_file_actions_adddup2(&actions, stdoutfd[1], STDOUT_FILENO)) {
 846    posix_spawn_file_actions_destroy(&actions);
 847    return -1;
 848  }
 849
 850  if (subprocess_option_combined_stdout_stderr ==
 851      (options & subprocess_option_combined_stdout_stderr)) {
 852    if (0 != posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO,
 853                                              STDERR_FILENO)) {
 854      posix_spawn_file_actions_destroy(&actions);
 855      return -1;
 856    }
 857  } else {
 858    // Close the stderr read end
 859    if (0 != posix_spawn_file_actions_addclose(&actions, stderrfd[0])) {
 860      posix_spawn_file_actions_destroy(&actions);
 861      return -1;
 862    }
 863    // Map the write end to stdout
 864    if (0 != posix_spawn_file_actions_adddup2(&actions, stderrfd[1],
 865                                              STDERR_FILENO)) {
 866      posix_spawn_file_actions_destroy(&actions);
 867      return -1;
 868    }
 869  }
 870
 871#ifdef __clang__
 872#pragma clang diagnostic push
 873#pragma clang diagnostic ignored "-Wcast-qual"
 874#pragma clang diagnostic ignored "-Wold-style-cast"
 875#endif
 876  if (subprocess_option_search_user_path ==
 877      (options & subprocess_option_search_user_path)) {
 878    if (0 != posix_spawnp(&child, commandLine[0], &actions, SUBPROCESS_NULL,
 879                          SUBPROCESS_CONST_CAST(char *const *, commandLine),
 880                          used_environment)) {
 881      posix_spawn_file_actions_destroy(&actions);
 882      return -1;
 883    }
 884  } else {
 885    if (0 != posix_spawn(&child, commandLine[0], &actions, SUBPROCESS_NULL,
 886                         SUBPROCESS_CONST_CAST(char *const *, commandLine),
 887                         used_environment)) {
 888      posix_spawn_file_actions_destroy(&actions);
 889      return -1;
 890    }
 891  }
 892#ifdef __clang__
 893#pragma clang diagnostic pop
 894#endif
 895
 896  // Close the stdin read end
 897  close(stdinfd[0]);
 898  // Store the stdin write end
 899  out_process->stdin_file = fdopen(stdinfd[1], "wb");
 900
 901  // Close the stdout write end
 902  close(stdoutfd[1]);
 903  // Store the stdout read end
 904  out_process->stdout_file = fdopen(stdoutfd[0], "rb");
 905
 906  if (subprocess_option_combined_stdout_stderr ==
 907      (options & subprocess_option_combined_stdout_stderr)) {
 908    out_process->stderr_file = out_process->stdout_file;
 909  } else {
 910    // Close the stderr write end
 911    close(stderrfd[1]);
 912    // Store the stderr read end
 913    out_process->stderr_file = fdopen(stderrfd[0], "rb");
 914  }
 915
 916  // Store the child's pid
 917  out_process->child = child;
 918
 919  out_process->alive = 1;
 920
 921  posix_spawn_file_actions_destroy(&actions);
 922  return 0;
 923#endif
 924}
 925
 926FILE *subprocess_stdin(const struct subprocess_s *const process) {
 927  return process->stdin_file;
 928}
 929
 930FILE *subprocess_stdout(const struct subprocess_s *const process) {
 931  return process->stdout_file;
 932}
 933
 934FILE *subprocess_stderr(const struct subprocess_s *const process) {
 935  if (process->stdout_file != process->stderr_file) {
 936    return process->stderr_file;
 937  } else {
 938    return SUBPROCESS_NULL;
 939  }
 940}
 941
 942int subprocess_join(struct subprocess_s *const process,
 943                    int *const out_return_code) {
 944#if defined(_WIN32)
 945  const unsigned long infinite = 0xFFFFFFFF;
 946
 947  if (process->stdin_file) {
 948    fclose(process->stdin_file);
 949    process->stdin_file = SUBPROCESS_NULL;
 950  }
 951
 952  if (process->hStdInput) {
 953    CloseHandle(process->hStdInput);
 954    process->hStdInput = SUBPROCESS_NULL;
 955  }
 956
 957  WaitForSingleObject(process->hProcess, infinite);
 958
 959  if (out_return_code) {
 960    if (!GetExitCodeProcess(
 961            process->hProcess,
 962            SUBPROCESS_PTR_CAST(unsigned long *, out_return_code))) {
 963      return -1;
 964    }
 965  }
 966
 967  process->alive = 0;
 968
 969  return 0;
 970#else
 971  int status;
 972
 973  if (process->stdin_file) {
 974    fclose(process->stdin_file);
 975    process->stdin_file = SUBPROCESS_NULL;
 976  }
 977
 978  if (process->child) {
 979    if (process->child != waitpid(process->child, &status, 0)) {
 980      return -1;
 981    }
 982
 983    process->child = 0;
 984
 985    if (WIFEXITED(status)) {
 986      process->return_status = WEXITSTATUS(status);
 987    } else {
 988      process->return_status = EXIT_FAILURE;
 989    }
 990
 991    process->alive = 0;
 992  }
 993
 994  if (out_return_code) {
 995    *out_return_code = process->return_status;
 996  }
 997
 998  return 0;
 999#endif
1000}
1001
1002int subprocess_destroy(struct subprocess_s *const process) {
1003  if (process->stdin_file) {
1004    fclose(process->stdin_file);
1005    process->stdin_file = SUBPROCESS_NULL;
1006  }
1007
1008  if (process->stdout_file) {
1009    fclose(process->stdout_file);
1010
1011    if (process->stdout_file != process->stderr_file) {
1012      fclose(process->stderr_file);
1013    }
1014
1015    process->stdout_file = SUBPROCESS_NULL;
1016    process->stderr_file = SUBPROCESS_NULL;
1017  }
1018
1019#if defined(_WIN32)
1020  if (process->hProcess) {
1021    CloseHandle(process->hProcess);
1022    process->hProcess = SUBPROCESS_NULL;
1023
1024    if (process->hStdInput) {
1025      CloseHandle(process->hStdInput);
1026    }
1027
1028    if (process->hEventOutput) {
1029      CloseHandle(process->hEventOutput);
1030    }
1031
1032    if (process->hEventError) {
1033      CloseHandle(process->hEventError);
1034    }
1035  }
1036#endif
1037
1038  return 0;
1039}
1040
1041int subprocess_terminate(struct subprocess_s *const process) {
1042#if defined(_WIN32)
1043  unsigned int killed_process_exit_code;
1044  int success_terminate;
1045  int windows_call_result;
1046
1047  killed_process_exit_code = 99;
1048  windows_call_result =
1049      TerminateProcess(process->hProcess, killed_process_exit_code);
1050  success_terminate = (windows_call_result == 0) ? 1 : 0;
1051  return success_terminate;
1052#else
1053  int result;
1054  result = kill(process->child, 9);
1055  return result;
1056#endif
1057}
1058
1059unsigned subprocess_read_stdout(struct subprocess_s *const process,
1060                                char *const buffer, unsigned size) {
1061#if defined(_WIN32)
1062  void *handle;
1063  unsigned long bytes_read = 0;
1064  struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL};
1065  overlapped.hEvent = process->hEventOutput;
1066
1067  handle = SUBPROCESS_PTR_CAST(void *,
1068                               _get_osfhandle(_fileno(process->stdout_file)));
1069
1070  if (!ReadFile(handle, buffer, size, &bytes_read,
1071                SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) {
1072    const unsigned long errorIoPending = 997;
1073    unsigned long error = GetLastError();
1074
1075    // Means we've got an async read!
1076    if (error == errorIoPending) {
1077      if (!GetOverlappedResult(handle,
1078                               SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped),
1079                               &bytes_read, 1)) {
1080        const unsigned long errorIoIncomplete = 996;
1081        const unsigned long errorHandleEOF = 38;
1082        error = GetLastError();
1083
1084        if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
1085          return 0;
1086        }
1087      }
1088    }
1089  }
1090
1091  return SUBPROCESS_CAST(unsigned, bytes_read);
1092#else
1093  const int fd = fileno(process->stdout_file);
1094  const ssize_t bytes_read = read(fd, buffer, size);
1095
1096  if (bytes_read < 0) {
1097    return 0;
1098  }
1099
1100  return SUBPROCESS_CAST(unsigned, bytes_read);
1101#endif
1102}
1103
1104unsigned subprocess_read_stderr(struct subprocess_s *const process,
1105                                char *const buffer, unsigned size) {
1106#if defined(_WIN32)
1107  void *handle;
1108  unsigned long bytes_read = 0;
1109  struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL};
1110  overlapped.hEvent = process->hEventError;
1111
1112  handle = SUBPROCESS_PTR_CAST(void *,
1113                               _get_osfhandle(_fileno(process->stderr_file)));
1114
1115  if (!ReadFile(handle, buffer, size, &bytes_read,
1116                SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) {
1117    const unsigned long errorIoPending = 997;
1118    unsigned long error = GetLastError();
1119
1120    // Means we've got an async read!
1121    if (error == errorIoPending) {
1122      if (!GetOverlappedResult(handle,
1123                               SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped),
1124                               &bytes_read, 1)) {
1125        const unsigned long errorIoIncomplete = 996;
1126        const unsigned long errorHandleEOF = 38;
1127        error = GetLastError();
1128
1129        if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
1130          return 0;
1131        }
1132      }
1133    }
1134  }
1135
1136  return SUBPROCESS_CAST(unsigned, bytes_read);
1137#else
1138  const int fd = fileno(process->stderr_file);
1139  const ssize_t bytes_read = read(fd, buffer, size);
1140
1141  if (bytes_read < 0) {
1142    return 0;
1143  }
1144
1145  return SUBPROCESS_CAST(unsigned, bytes_read);
1146#endif
1147}
1148
1149int subprocess_alive(struct subprocess_s *const process) {
1150  int is_alive = SUBPROCESS_CAST(int, process->alive);
1151
1152  if (!is_alive) {
1153    return 0;
1154  }
1155#if defined(_WIN32)
1156  {
1157    const unsigned long zero = 0x0;
1158    const unsigned long wait_object_0 = 0x00000000L;
1159
1160    is_alive = wait_object_0 != WaitForSingleObject(process->hProcess, zero);
1161  }
1162#else
1163  {
1164    int status;
1165    is_alive = 0 == waitpid(process->child, &status, WNOHANG);
1166
1167    // If the process was successfully waited on we need to cleanup now.
1168    if (!is_alive) {
1169      if (WIFEXITED(status)) {
1170        process->return_status = WEXITSTATUS(status);
1171      } else {
1172        process->return_status = EXIT_FAILURE;
1173      }
1174
1175      // Since we've already successfully waited on the process, we need to wipe
1176      // the child now.
1177      process->child = 0;
1178
1179      if (subprocess_join(process, SUBPROCESS_NULL)) {
1180        return -1;
1181      }
1182    }
1183  }
1184#endif
1185
1186  if (!is_alive) {
1187    process->alive = 0;
1188  }
1189
1190  return is_alive;
1191}
1192
1193#if defined(__clang__)
1194#if __has_warning("-Wunsafe-buffer-usage")
1195#pragma clang diagnostic pop
1196#endif
1197#endif
1198
1199#if defined(__cplusplus)
1200} // extern "C"
1201#endif
1202
1203#endif /* SHEREDOM_SUBPROCESS_H_INCLUDED */