| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180 |
- /*
- The latest version of this library is available on GitHub;
- https://github.com/sheredom/subprocess.h
- */
- /*
- This is free and unencumbered software released into the public domain.
- Anyone is free to copy, modify, publish, use, compile, sell, or
- distribute this software, either in source code form or as a compiled
- binary, for any purpose, commercial or non-commercial, and by any
- means.
- In jurisdictions that recognize copyright laws, the author or authors
- of this software dedicate any and all copyright interest in the
- software to the public domain. We make this dedication for the benefit
- of the public at large and to the detriment of our heirs and
- successors. We intend this dedication to be an overt act of
- relinquishment in perpetuity of all present and future rights to this
- software under copyright law.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- For more information, please refer to <http://unlicense.org/>
- */
- #ifndef SHEREDOM_SUBPROCESS_H_INCLUDED
- #define SHEREDOM_SUBPROCESS_H_INCLUDED
- #if defined(_MSC_VER)
- #pragma warning(push, 1)
- /* disable warning: '__cplusplus' is not defined as a preprocessor macro,
- * replacing with '0' for '#if/#elif' */
- #pragma warning(disable : 4668)
- #endif
- #include <stdio.h>
- #include <string.h>
- #if defined(_MSC_VER)
- #pragma warning(pop)
- #endif
- #if defined(_MSC_VER)
- #define subprocess_pure
- #define subprocess_weak __inline
- #define subprocess_tls __declspec(thread)
- #elif defined(__MINGW32__)
- #define subprocess_pure __attribute__((pure))
- #define subprocess_weak static __attribute__((used))
- #define subprocess_tls __thread
- #elif defined(__clang__) || defined(__GNUC__)
- #define subprocess_pure __attribute__((pure))
- #define subprocess_weak __attribute__((weak))
- #define subprocess_tls __thread
- #else
- #error Non clang, non gcc, non MSVC compiler found!
- #endif
- struct subprocess_s;
- enum subprocess_option_e {
- // stdout and stderr are the same FILE.
- subprocess_option_combined_stdout_stderr = 0x1,
- // The child process should inherit the environment variables of the parent.
- subprocess_option_inherit_environment = 0x2,
- // Enable asynchronous reading of stdout/stderr before it has completed.
- subprocess_option_enable_async = 0x4,
- // Enable the child process to be spawned with no window visible if supported
- // by the platform.
- subprocess_option_no_window = 0x8,
- // Search for program names in the PATH variable. Always enabled on Windows.
- // Note: this will **not** search for paths in any provided custom environment
- // and instead uses the PATH of the spawning process.
- subprocess_option_search_user_path = 0x10
- };
- #if defined(__cplusplus)
- extern "C" {
- #endif
- /// @brief Create a process.
- /// @param command_line An array of strings for the command line to execute for
- /// this process. The last element must be NULL to signify the end of the array.
- /// The memory backing this parameter only needs to persist until this function
- /// returns.
- /// @param options A bit field of subprocess_option_e's to pass.
- /// @param out_process The newly created process.
- /// @return On success zero is returned.
- subprocess_weak int subprocess_create(const char *const command_line[],
- int options,
- struct subprocess_s *const out_process);
- /// @brief Create a process (extended create).
- /// @param command_line An array of strings for the command line to execute for
- /// this process. The last element must be NULL to signify the end of the array.
- /// The memory backing this parameter only needs to persist until this function
- /// returns.
- /// @param options A bit field of subprocess_option_e's to pass.
- /// @param environment An optional array of strings for the environment to use
- /// for a child process (each element of the form FOO=BAR). The last element
- /// must be NULL to signify the end of the array.
- /// @param out_process The newly created process.
- /// @return On success zero is returned.
- ///
- /// If `options` contains `subprocess_option_inherit_environment`, then
- /// `environment` must be NULL.
- subprocess_weak int
- subprocess_create_ex(const char *const command_line[], int options,
- const char *const environment[],
- struct subprocess_s *const out_process);
- /// @brief Get the standard input file for a process.
- /// @param process The process to query.
- /// @return The file for standard input of the process.
- ///
- /// The file returned can be written to by the parent process to feed data to
- /// the standard input of the process.
- subprocess_pure subprocess_weak FILE *
- subprocess_stdin(const struct subprocess_s *const process);
- /// @brief Get the standard output file for a process.
- /// @param process The process to query.
- /// @return The file for standard output of the process.
- ///
- /// The file returned can be read from by the parent process to read data from
- /// the standard output of the child process.
- subprocess_pure subprocess_weak FILE *
- subprocess_stdout(const struct subprocess_s *const process);
- /// @brief Get the standard error file for a process.
- /// @param process The process to query.
- /// @return The file for standard error of the process.
- ///
- /// The file returned can be read from by the parent process to read data from
- /// the standard error of the child process.
- ///
- /// If the process was created with the subprocess_option_combined_stdout_stderr
- /// option bit set, this function will return NULL, and the subprocess_stdout
- /// function should be used for both the standard output and error combined.
- subprocess_pure subprocess_weak FILE *
- subprocess_stderr(const struct subprocess_s *const process);
- /// @brief Wait for a process to finish execution.
- /// @param process The process to wait for.
- /// @param out_return_code The return code of the returned process (can be
- /// NULL).
- /// @return On success zero is returned.
- ///
- /// Joining a process will close the stdin pipe to the process.
- subprocess_weak int subprocess_join(struct subprocess_s *const process,
- int *const out_return_code);
- /// @brief Destroy a previously created process.
- /// @param process The process to destroy.
- /// @return On success zero is returned.
- ///
- /// If the process to be destroyed had not finished execution, it may out live
- /// the parent process.
- subprocess_weak int subprocess_destroy(struct subprocess_s *const process);
- /// @brief Terminate a previously created process.
- /// @param process The process to terminate.
- /// @return On success zero is returned.
- ///
- /// If the process to be destroyed had not finished execution, it will be
- /// terminated (i.e killed).
- subprocess_weak int subprocess_terminate(struct subprocess_s *const process);
- /// @brief Read the standard output from the child process.
- /// @param process The process to read from.
- /// @param buffer The buffer to read into.
- /// @param size The maximum number of bytes to read.
- /// @return The number of bytes actually read into buffer. Can only be 0 if the
- /// process has complete.
- ///
- /// The only safe way to read from the standard output of a process during it's
- /// execution is to use the `subprocess_option_enable_async` option in
- /// conjuction with this method.
- subprocess_weak unsigned
- subprocess_read_stdout(struct subprocess_s *const process, char *const buffer,
- unsigned size);
- /// @brief Read the standard error from the child process.
- /// @param process The process to read from.
- /// @param buffer The buffer to read into.
- /// @param size The maximum number of bytes to read.
- /// @return The number of bytes actually read into buffer. Can only be 0 if the
- /// process has complete.
- ///
- /// The only safe way to read from the standard error of a process during it's
- /// execution is to use the `subprocess_option_enable_async` option in
- /// conjuction with this method.
- subprocess_weak unsigned
- subprocess_read_stderr(struct subprocess_s *const process, char *const buffer,
- unsigned size);
- /// @brief Returns if the subprocess is currently still alive and executing.
- /// @param process The process to check.
- /// @return If the process is still alive non-zero is returned.
- subprocess_weak int subprocess_alive(struct subprocess_s *const process);
- #if defined(__cplusplus)
- #define SUBPROCESS_CAST(type, x) static_cast<type>(x)
- #define SUBPROCESS_PTR_CAST(type, x) reinterpret_cast<type>(x)
- #define SUBPROCESS_CONST_CAST(type, x) const_cast<type>(x)
- #define SUBPROCESS_NULL NULL
- #else
- #define SUBPROCESS_CAST(type, x) ((type)(x))
- #define SUBPROCESS_PTR_CAST(type, x) ((type)(x))
- #define SUBPROCESS_CONST_CAST(type, x) ((type)(x))
- #define SUBPROCESS_NULL 0
- #endif
- #if !defined(_WIN32)
- #include <signal.h>
- #include <spawn.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #endif
- #if defined(_WIN32)
- #if (_MSC_VER < 1920)
- #ifdef _WIN64
- typedef __int64 subprocess_intptr_t;
- typedef unsigned __int64 subprocess_size_t;
- #else
- typedef int subprocess_intptr_t;
- typedef unsigned int subprocess_size_t;
- #endif
- #else
- #include <inttypes.h>
- typedef intptr_t subprocess_intptr_t;
- typedef size_t subprocess_size_t;
- #endif
- #ifdef __clang__
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wreserved-identifier"
- #endif
- typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
- typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
- typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
- typedef struct _OVERLAPPED *LPOVERLAPPED;
- #ifdef __clang__
- #pragma clang diagnostic pop
- #endif
- #ifdef _MSC_VER
- #pragma warning(push, 1)
- #endif
- #ifdef __MINGW32__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wpedantic"
- #endif
- struct subprocess_subprocess_information_s {
- void *hProcess;
- void *hThread;
- unsigned long dwProcessId;
- unsigned long dwThreadId;
- };
- struct subprocess_security_attributes_s {
- unsigned long nLength;
- void *lpSecurityDescriptor;
- int bInheritHandle;
- };
- struct subprocess_startup_info_s {
- unsigned long cb;
- char *lpReserved;
- char *lpDesktop;
- char *lpTitle;
- unsigned long dwX;
- unsigned long dwY;
- unsigned long dwXSize;
- unsigned long dwYSize;
- unsigned long dwXCountChars;
- unsigned long dwYCountChars;
- unsigned long dwFillAttribute;
- unsigned long dwFlags;
- unsigned short wShowWindow;
- unsigned short cbReserved2;
- unsigned char *lpReserved2;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
- };
- struct subprocess_overlapped_s {
- uintptr_t Internal;
- uintptr_t InternalHigh;
- union {
- struct {
- unsigned long Offset;
- unsigned long OffsetHigh;
- } DUMMYSTRUCTNAME;
- void *Pointer;
- } DUMMYUNIONNAME;
- void *hEvent;
- };
- #ifdef __MINGW32__
- #pragma GCC diagnostic pop
- #endif
- #ifdef _MSC_VER
- #pragma warning(pop)
- #endif
- __declspec(dllimport) unsigned long __stdcall GetLastError(void);
- __declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
- unsigned long);
- __declspec(dllimport) int __stdcall CreatePipe(void **, void **,
- LPSECURITY_ATTRIBUTES,
- unsigned long);
- __declspec(dllimport) void *__stdcall CreateNamedPipeA(
- const char *, unsigned long, unsigned long, unsigned long, unsigned long,
- unsigned long, unsigned long, LPSECURITY_ATTRIBUTES);
- __declspec(dllimport) int __stdcall ReadFile(void *, void *, unsigned long,
- unsigned long *, LPOVERLAPPED);
- __declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(void);
- __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void);
- __declspec(dllimport) void *__stdcall CreateFileA(const char *, unsigned long,
- unsigned long,
- LPSECURITY_ATTRIBUTES,
- unsigned long, unsigned long,
- void *);
- __declspec(dllimport) void *__stdcall CreateEventA(LPSECURITY_ATTRIBUTES, int,
- int, const char *);
- __declspec(dllimport) int __stdcall CreateProcessA(
- const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
- unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
- __declspec(dllimport) int __stdcall CloseHandle(void *);
- __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
- void *, unsigned long);
- __declspec(dllimport) int __stdcall GetExitCodeProcess(
- void *, unsigned long *lpExitCode);
- __declspec(dllimport) int __stdcall TerminateProcess(void *, unsigned int);
- __declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects(
- unsigned long, void *const *, int, unsigned long);
- __declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED,
- unsigned long *, int);
- #if defined(_DLL)
- #define SUBPROCESS_DLLIMPORT __declspec(dllimport)
- #else
- #define SUBPROCESS_DLLIMPORT
- #endif
- #ifdef __clang__
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wreserved-identifier"
- #endif
- SUBPROCESS_DLLIMPORT int __cdecl _fileno(FILE *);
- SUBPROCESS_DLLIMPORT int __cdecl _open_osfhandle(subprocess_intptr_t, int);
- SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl _get_osfhandle(int);
- #ifndef __MINGW32__
- void *__cdecl _alloca(subprocess_size_t);
- #else
- #include <malloc.h>
- #endif
- #ifdef __clang__
- #pragma clang diagnostic pop
- #endif
- #else
- typedef size_t subprocess_size_t;
- #endif
- #ifdef __clang__
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wpadded"
- #endif
- struct subprocess_s {
- FILE *stdin_file;
- FILE *stdout_file;
- FILE *stderr_file;
- #if defined(_WIN32)
- void *hProcess;
- void *hStdInput;
- void *hEventOutput;
- void *hEventError;
- #else
- pid_t child;
- int return_status;
- #endif
- subprocess_size_t alive;
- };
- #ifdef __clang__
- #pragma clang diagnostic pop
- #endif
- #if defined(_WIN32)
- subprocess_weak int subprocess_create_named_pipe_helper(void **rd, void **wr);
- int subprocess_create_named_pipe_helper(void **rd, void **wr) {
- const unsigned long pipeAccessInbound = 0x00000001;
- const unsigned long fileFlagOverlapped = 0x40000000;
- const unsigned long pipeTypeByte = 0x00000000;
- const unsigned long pipeWait = 0x00000000;
- const unsigned long genericWrite = 0x40000000;
- const unsigned long openExisting = 3;
- const unsigned long fileAttributeNormal = 0x00000080;
- const void *const invalidHandleValue =
- SUBPROCESS_PTR_CAST(void *, ~(SUBPROCESS_CAST(subprocess_intptr_t, 0)));
- struct subprocess_security_attributes_s saAttr = {sizeof(saAttr),
- SUBPROCESS_NULL, 1};
- char name[256] = {0};
- static subprocess_tls long index = 0;
- const long unique = index++;
- #if defined(_MSC_VER) && _MSC_VER < 1900
- #pragma warning(push, 1)
- #pragma warning(disable : 4996)
- _snprintf(name, sizeof(name) - 1,
- "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
- GetCurrentProcessId(), GetCurrentThreadId(), unique);
- #pragma warning(pop)
- #else
- snprintf(name, sizeof(name) - 1,
- "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
- GetCurrentProcessId(), GetCurrentThreadId(), unique);
- #endif
- *rd =
- CreateNamedPipeA(name, pipeAccessInbound | fileFlagOverlapped,
- pipeTypeByte | pipeWait, 1, 4096, 4096, SUBPROCESS_NULL,
- SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr));
- if (invalidHandleValue == *rd) {
- return -1;
- }
- *wr = CreateFileA(name, genericWrite, SUBPROCESS_NULL,
- SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr),
- openExisting, fileAttributeNormal, SUBPROCESS_NULL);
- if (invalidHandleValue == *wr) {
- return -1;
- }
- return 0;
- }
- #endif
- int subprocess_create(const char *const commandLine[], int options,
- struct subprocess_s *const out_process) {
- return subprocess_create_ex(commandLine, options, SUBPROCESS_NULL,
- out_process);
- }
- int subprocess_create_ex(const char *const commandLine[], int options,
- const char *const environment[],
- struct subprocess_s *const out_process) {
- #if defined(_WIN32)
- int fd;
- void *rd, *wr;
- char *commandLineCombined;
- subprocess_size_t len;
- int i, j;
- int need_quoting;
- unsigned long flags = 0;
- const unsigned long startFUseStdHandles = 0x00000100;
- const unsigned long handleFlagInherit = 0x00000001;
- const unsigned long createNoWindow = 0x08000000;
- struct subprocess_subprocess_information_s processInfo;
- struct subprocess_security_attributes_s saAttr = {sizeof(saAttr),
- SUBPROCESS_NULL, 1};
- char *used_environment = SUBPROCESS_NULL;
- struct subprocess_startup_info_s startInfo = {0,
- SUBPROCESS_NULL,
- SUBPROCESS_NULL,
- SUBPROCESS_NULL,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- SUBPROCESS_NULL,
- SUBPROCESS_NULL,
- SUBPROCESS_NULL,
- SUBPROCESS_NULL};
- startInfo.cb = sizeof(startInfo);
- startInfo.dwFlags = startFUseStdHandles;
- if (subprocess_option_no_window == (options & subprocess_option_no_window)) {
- flags |= createNoWindow;
- }
- if (subprocess_option_inherit_environment !=
- (options & subprocess_option_inherit_environment)) {
- if (SUBPROCESS_NULL == environment) {
- used_environment = SUBPROCESS_CONST_CAST(char *, "\0\0");
- } else {
- // We always end with two null terminators.
- len = 2;
- for (i = 0; environment[i]; i++) {
- for (j = 0; '\0' != environment[i][j]; j++) {
- len++;
- }
- // For the null terminator too.
- len++;
- }
- used_environment = SUBPROCESS_CAST(char *, _alloca(len));
- // Re-use len for the insertion position
- len = 0;
- for (i = 0; environment[i]; i++) {
- for (j = 0; '\0' != environment[i][j]; j++) {
- used_environment[len++] = environment[i][j];
- }
- used_environment[len++] = '\0';
- }
- // End with the two null terminators.
- used_environment[len++] = '\0';
- used_environment[len++] = '\0';
- }
- } else {
- if (SUBPROCESS_NULL != environment) {
- return -1;
- }
- }
- if (!CreatePipe(&rd, &wr, SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr),
- 0)) {
- return -1;
- }
- if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
- return -1;
- }
- fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, wr), 0);
- if (-1 != fd) {
- out_process->stdin_file = _fdopen(fd, "wb");
- if (SUBPROCESS_NULL == out_process->stdin_file) {
- return -1;
- }
- }
- startInfo.hStdInput = rd;
- if (options & subprocess_option_enable_async) {
- if (subprocess_create_named_pipe_helper(&rd, &wr)) {
- return -1;
- }
- } else {
- if (!CreatePipe(&rd, &wr,
- SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) {
- return -1;
- }
- }
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
- fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0);
- if (-1 != fd) {
- out_process->stdout_file = _fdopen(fd, "rb");
- if (SUBPROCESS_NULL == out_process->stdout_file) {
- return -1;
- }
- }
- startInfo.hStdOutput = wr;
- if (subprocess_option_combined_stdout_stderr ==
- (options & subprocess_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- startInfo.hStdError = startInfo.hStdOutput;
- } else {
- if (options & subprocess_option_enable_async) {
- if (subprocess_create_named_pipe_helper(&rd, &wr)) {
- return -1;
- }
- } else {
- if (!CreatePipe(&rd, &wr,
- SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) {
- return -1;
- }
- }
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
- fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0);
- if (-1 != fd) {
- out_process->stderr_file = _fdopen(fd, "rb");
- if (SUBPROCESS_NULL == out_process->stderr_file) {
- return -1;
- }
- }
- startInfo.hStdError = wr;
- }
- if (options & subprocess_option_enable_async) {
- out_process->hEventOutput =
- CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1,
- SUBPROCESS_NULL);
- out_process->hEventError =
- CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1,
- SUBPROCESS_NULL);
- } else {
- out_process->hEventOutput = SUBPROCESS_NULL;
- out_process->hEventError = SUBPROCESS_NULL;
- }
- // Combine commandLine together into a single string
- len = 0;
- for (i = 0; commandLine[i]; i++) {
- // for the trailing \0
- len++;
- // Quote the argument if it has a space in it
- if (strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL)
- len += 2;
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- switch (commandLine[i][j]) {
- default:
- break;
- case '\\':
- if (commandLine[i][j + 1] == '"') {
- len++;
- }
- break;
- case '"':
- len++;
- break;
- }
- len++;
- }
- }
- commandLineCombined = SUBPROCESS_CAST(char *, _alloca(len));
- if (!commandLineCombined) {
- return -1;
- }
- // Gonna re-use len to store the write index into commandLineCombined
- len = 0;
- for (i = 0; commandLine[i]; i++) {
- if (0 != i) {
- commandLineCombined[len++] = ' ';
- }
- need_quoting = strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL;
- if (need_quoting) {
- commandLineCombined[len++] = '"';
- }
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- switch (commandLine[i][j]) {
- default:
- break;
- case '\\':
- if (commandLine[i][j + 1] == '"') {
- commandLineCombined[len++] = '\\';
- }
- break;
- case '"':
- commandLineCombined[len++] = '\\';
- break;
- }
- commandLineCombined[len++] = commandLine[i][j];
- }
- if (need_quoting) {
- commandLineCombined[len++] = '"';
- }
- }
- commandLineCombined[len] = '\0';
- if (!CreateProcessA(
- SUBPROCESS_NULL,
- commandLineCombined, // command line
- SUBPROCESS_NULL, // process security attributes
- SUBPROCESS_NULL, // primary thread security attributes
- 1, // handles are inherited
- flags, // creation flags
- used_environment, // used environment
- SUBPROCESS_NULL, // use parent's current directory
- SUBPROCESS_PTR_CAST(LPSTARTUPINFOA,
- &startInfo), // STARTUPINFO pointer
- SUBPROCESS_PTR_CAST(LPPROCESS_INFORMATION, &processInfo))) {
- return -1;
- }
- out_process->hProcess = processInfo.hProcess;
- out_process->hStdInput = startInfo.hStdInput;
- // We don't need the handle of the primary thread in the called process.
- CloseHandle(processInfo.hThread);
- if (SUBPROCESS_NULL != startInfo.hStdOutput) {
- CloseHandle(startInfo.hStdOutput);
- if (startInfo.hStdError != startInfo.hStdOutput) {
- CloseHandle(startInfo.hStdError);
- }
- }
- out_process->alive = 1;
- return 0;
- #else
- int stdinfd[2];
- int stdoutfd[2];
- int stderrfd[2];
- pid_t child;
- extern char **environ;
- char *const empty_environment[1] = {SUBPROCESS_NULL};
- posix_spawn_file_actions_t actions;
- char *const *used_environment;
- if (subprocess_option_inherit_environment ==
- (options & subprocess_option_inherit_environment)) {
- if (SUBPROCESS_NULL != environment) {
- return -1;
- }
- }
- if (0 != pipe(stdinfd)) {
- return -1;
- }
- if (0 != pipe(stdoutfd)) {
- return -1;
- }
- if (subprocess_option_combined_stdout_stderr !=
- (options & subprocess_option_combined_stdout_stderr)) {
- if (0 != pipe(stderrfd)) {
- return -1;
- }
- }
- if (environment) {
- #ifdef __clang__
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wcast-qual"
- #pragma clang diagnostic ignored "-Wold-style-cast"
- #endif
- used_environment = (char *const *)environment;
- #ifdef __clang__
- #pragma clang diagnostic pop
- #endif
- } else if (subprocess_option_inherit_environment ==
- (options & subprocess_option_inherit_environment)) {
- used_environment = environ;
- } else {
- used_environment = empty_environment;
- }
- if (0 != posix_spawn_file_actions_init(&actions)) {
- return -1;
- }
- // Close the stdin write end
- if (0 != posix_spawn_file_actions_addclose(&actions, stdinfd[1])) {
- posix_spawn_file_actions_destroy(&actions);
- return -1;
- }
- // Map the read end to stdin
- if (0 !=
- posix_spawn_file_actions_adddup2(&actions, stdinfd[0], STDIN_FILENO)) {
- posix_spawn_file_actions_destroy(&actions);
- return -1;
- }
- // Close the stdout read end
- if (0 != posix_spawn_file_actions_addclose(&actions, stdoutfd[0])) {
- posix_spawn_file_actions_destroy(&actions);
- return -1;
- }
- // Map the write end to stdout
- if (0 !=
- posix_spawn_file_actions_adddup2(&actions, stdoutfd[1], STDOUT_FILENO)) {
- posix_spawn_file_actions_destroy(&actions);
- return -1;
- }
- if (subprocess_option_combined_stdout_stderr ==
- (options & subprocess_option_combined_stdout_stderr)) {
- if (0 != posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO,
- STDERR_FILENO)) {
- posix_spawn_file_actions_destroy(&actions);
- return -1;
- }
- } else {
- // Close the stderr read end
- if (0 != posix_spawn_file_actions_addclose(&actions, stderrfd[0])) {
- posix_spawn_file_actions_destroy(&actions);
- return -1;
- }
- // Map the write end to stdout
- if (0 != posix_spawn_file_actions_adddup2(&actions, stderrfd[1],
- STDERR_FILENO)) {
- posix_spawn_file_actions_destroy(&actions);
- return -1;
- }
- }
- #ifdef __clang__
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wcast-qual"
- #pragma clang diagnostic ignored "-Wold-style-cast"
- #endif
- if (subprocess_option_search_user_path ==
- (options & subprocess_option_search_user_path)) {
- if (0 != posix_spawnp(&child, commandLine[0], &actions, SUBPROCESS_NULL,
- (char *const *)commandLine, used_environment)) {
- posix_spawn_file_actions_destroy(&actions);
- return -1;
- }
- } else {
- if (0 != posix_spawn(&child, commandLine[0], &actions, SUBPROCESS_NULL,
- (char *const *)commandLine, used_environment)) {
- posix_spawn_file_actions_destroy(&actions);
- return -1;
- }
- }
- #ifdef __clang__
- #pragma clang diagnostic pop
- #endif
- // Close the stdin read end
- close(stdinfd[0]);
- // Store the stdin write end
- out_process->stdin_file = fdopen(stdinfd[1], "wb");
- // Close the stdout write end
- close(stdoutfd[1]);
- // Store the stdout read end
- out_process->stdout_file = fdopen(stdoutfd[0], "rb");
- if (subprocess_option_combined_stdout_stderr ==
- (options & subprocess_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- } else {
- // Close the stderr write end
- close(stderrfd[1]);
- // Store the stderr read end
- out_process->stderr_file = fdopen(stderrfd[0], "rb");
- }
- // Store the child's pid
- out_process->child = child;
- out_process->alive = 1;
- posix_spawn_file_actions_destroy(&actions);
- return 0;
- #endif
- }
- FILE *subprocess_stdin(const struct subprocess_s *const process) {
- return process->stdin_file;
- }
- FILE *subprocess_stdout(const struct subprocess_s *const process) {
- return process->stdout_file;
- }
- FILE *subprocess_stderr(const struct subprocess_s *const process) {
- if (process->stdout_file != process->stderr_file) {
- return process->stderr_file;
- } else {
- return SUBPROCESS_NULL;
- }
- }
- int subprocess_join(struct subprocess_s *const process,
- int *const out_return_code) {
- #if defined(_WIN32)
- const unsigned long infinite = 0xFFFFFFFF;
- if (process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = SUBPROCESS_NULL;
- }
- if (process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = SUBPROCESS_NULL;
- }
- WaitForSingleObject(process->hProcess, infinite);
- if (out_return_code) {
- if (!GetExitCodeProcess(
- process->hProcess,
- SUBPROCESS_PTR_CAST(unsigned long *, out_return_code))) {
- return -1;
- }
- }
- process->alive = 0;
- return 0;
- #else
- int status;
- if (process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = SUBPROCESS_NULL;
- }
- if (process->child) {
- if (process->child != waitpid(process->child, &status, 0)) {
- return -1;
- }
- process->child = 0;
- if (WIFEXITED(status)) {
- process->return_status = WEXITSTATUS(status);
- } else {
- process->return_status = EXIT_FAILURE;
- }
- process->alive = 0;
- }
- if (out_return_code) {
- *out_return_code = process->return_status;
- }
- return 0;
- #endif
- }
- int subprocess_destroy(struct subprocess_s *const process) {
- if (process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = SUBPROCESS_NULL;
- }
- if (process->stdout_file) {
- fclose(process->stdout_file);
- if (process->stdout_file != process->stderr_file) {
- fclose(process->stderr_file);
- }
- process->stdout_file = SUBPROCESS_NULL;
- process->stderr_file = SUBPROCESS_NULL;
- }
- #if defined(_WIN32)
- if (process->hProcess) {
- CloseHandle(process->hProcess);
- process->hProcess = SUBPROCESS_NULL;
- if (process->hStdInput) {
- CloseHandle(process->hStdInput);
- }
- if (process->hEventOutput) {
- CloseHandle(process->hEventOutput);
- }
- if (process->hEventError) {
- CloseHandle(process->hEventError);
- }
- }
- #endif
- return 0;
- }
- int subprocess_terminate(struct subprocess_s *const process) {
- #if defined(_WIN32)
- unsigned int killed_process_exit_code;
- int success_terminate;
- int windows_call_result;
- killed_process_exit_code = 99;
- windows_call_result =
- TerminateProcess(process->hProcess, killed_process_exit_code);
- success_terminate = (windows_call_result == 0) ? 1 : 0;
- return success_terminate;
- #else
- int result;
- result = kill(process->child, 9);
- return result;
- #endif
- }
- unsigned subprocess_read_stdout(struct subprocess_s *const process,
- char *const buffer, unsigned size) {
- #if defined(_WIN32)
- void *handle;
- unsigned long bytes_read = 0;
- struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL};
- overlapped.hEvent = process->hEventOutput;
- handle = SUBPROCESS_PTR_CAST(void *,
- _get_osfhandle(_fileno(process->stdout_file)));
- if (!ReadFile(handle, buffer, size, &bytes_read,
- SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) {
- const unsigned long errorIoPending = 997;
- unsigned long error = GetLastError();
- // Means we've got an async read!
- if (error == errorIoPending) {
- if (!GetOverlappedResult(handle,
- SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped),
- &bytes_read, 1)) {
- const unsigned long errorIoIncomplete = 996;
- const unsigned long errorHandleEOF = 38;
- error = GetLastError();
- if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
- return 0;
- }
- }
- }
- }
- return SUBPROCESS_CAST(unsigned, bytes_read);
- #else
- const int fd = fileno(process->stdout_file);
- const ssize_t bytes_read = read(fd, buffer, size);
- if (bytes_read < 0) {
- return 0;
- }
- return SUBPROCESS_CAST(unsigned, bytes_read);
- #endif
- }
- unsigned subprocess_read_stderr(struct subprocess_s *const process,
- char *const buffer, unsigned size) {
- #if defined(_WIN32)
- void *handle;
- unsigned long bytes_read = 0;
- struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL};
- overlapped.hEvent = process->hEventError;
- handle = SUBPROCESS_PTR_CAST(void *,
- _get_osfhandle(_fileno(process->stderr_file)));
- if (!ReadFile(handle, buffer, size, &bytes_read,
- SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) {
- const unsigned long errorIoPending = 997;
- unsigned long error = GetLastError();
- // Means we've got an async read!
- if (error == errorIoPending) {
- if (!GetOverlappedResult(handle,
- SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped),
- &bytes_read, 1)) {
- const unsigned long errorIoIncomplete = 996;
- const unsigned long errorHandleEOF = 38;
- error = GetLastError();
- if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
- return 0;
- }
- }
- }
- }
- return SUBPROCESS_CAST(unsigned, bytes_read);
- #else
- const int fd = fileno(process->stderr_file);
- const ssize_t bytes_read = read(fd, buffer, size);
- if (bytes_read < 0) {
- return 0;
- }
- return SUBPROCESS_CAST(unsigned, bytes_read);
- #endif
- }
- int subprocess_alive(struct subprocess_s *const process) {
- int is_alive = SUBPROCESS_CAST(int, process->alive);
- if (!is_alive) {
- return 0;
- }
- #if defined(_WIN32)
- {
- const unsigned long zero = 0x0;
- const unsigned long wait_object_0 = 0x00000000L;
- is_alive = wait_object_0 != WaitForSingleObject(process->hProcess, zero);
- }
- #else
- {
- int status;
- is_alive = 0 == waitpid(process->child, &status, WNOHANG);
- // If the process was successfully waited on we need to cleanup now.
- if (!is_alive) {
- if (WIFEXITED(status)) {
- process->return_status = WEXITSTATUS(status);
- } else {
- process->return_status = EXIT_FAILURE;
- }
- // Since we've already successfully waited on the process, we need to wipe
- // the child now.
- process->child = 0;
- if (subprocess_join(process, SUBPROCESS_NULL)) {
- return -1;
- }
- }
- }
- #endif
- if (!is_alive) {
- process->alive = 0;
- }
- return is_alive;
- }
- #if defined(__cplusplus)
- } // extern "C"
- #endif
- #endif /* SHEREDOM_SUBPROCESS_H_INCLUDED */
|