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