vendor : update subprocess.h (#27409)

This commit is contained in:
Alessandro de Oliveira Faria (A.K.A.CABELO)
2026-08-23 10:38:29 +03:00
committed by GitHub
parent 29ea9412a6
commit 6657ded4fa
2 changed files with 364 additions and 13 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ vendor = {
f"https://raw.githubusercontent.com/yhirose/cpp-httplib/{HTTPLIB_VERSION}/split.py": "split.py",
f"https://raw.githubusercontent.com/yhirose/cpp-httplib/{HTTPLIB_VERSION}/LICENSE": "vendor/cpp-httplib/LICENSE",
"https://raw.githubusercontent.com/sheredom/subprocess.h/9ce0d701b6fb10f8f8c4445edd31e7c60a1237e3/subprocess.h": "vendor/sheredom/subprocess.h",
"https://raw.githubusercontent.com/sheredom/subprocess.h/0dccaa9aa176dd6d7ef8afeca3c18d6e80a32795/subprocess.h": "vendor/sheredom/subprocess.h",
f"https://raw.githubusercontent.com/Cyan4973/xxHash/{XXHASH_COMMIT}/xxhash.c": "vendor/hash/xxhash/xxhash.c",
f"https://raw.githubusercontent.com/Cyan4973/xxHash/{XXHASH_COMMIT}/xxhash.h": "vendor/hash/xxhash/xxhash.h",
+363 -12
View File
@@ -275,13 +275,46 @@ subprocess_weak int subprocess_alive(struct subprocess_s *const process);
#include <unistd.h>
#endif
#if defined(__NetBSD__)
#include <sys/param.h>
#endif
/* Which spelling of the chdir file action the platform provides, if any.
POSIX 2024 standardised posix_spawn_file_actions_addchdir; implementations
that shipped it earlier called it ..._np. macOS 26 and NetBSD 10 use the
standard name, glibc 2.29+, macOS 10.15+ and FreeBSD 13.1+ use the _np name,
and AIX, NetBSD 9 and older, and OpenBSD provide neither. */
#if !defined(SUBPROCESS_ADDCHDIR_IS_POSIX)
#if (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000) || \
(defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)
#define SUBPROCESS_ADDCHDIR_IS_POSIX 1
#else
#define SUBPROCESS_ADDCHDIR_IS_POSIX 0
#endif
#endif
/* Whether to launch the child with fork()+exec() instead of posix_spawn(),
for platforms with no posix_spawn_file_actions_addchdir under either
spelling: the child chdir()s before exec, and a close-on-exec pipe carries
exec's errno back. Define this yourself to force either implementation. */
#if !defined(SUBPROCESS_SPAWN_VIA_FORK)
#if defined(_AIX) || defined(__OpenBSD__) || \
(defined(__NetBSD__) && (__NetBSD_Version__ < 1000000000))
#define SUBPROCESS_SPAWN_VIA_FORK 1
#else
#define SUBPROCESS_SPAWN_VIA_FORK 0
#endif
#endif
/* Whether subprocess_create_ex can honour process_cwd. glibc only gained
posix_spawn_file_actions_addchdir_np in 2.29, and macOS in 10.15; the SDKs
mark it unavailable on iOS, tvOS and watchOS, where the undefined version
macro folds to 0 and so answers correctly. Define this yourself to override
the detection, for instance on musl older than 1.1.24. */
#if !defined(SUBPROCESS_HAVE_CWD)
#if defined(__GLIBC__)
#if SUBPROCESS_SPAWN_VIA_FORK
#define SUBPROCESS_HAVE_CWD 1
#elif defined(__GLIBC__)
#if __GLIBC_PREREQ(2, 29)
#define SUBPROCESS_HAVE_CWD 1
#else
@@ -294,10 +327,13 @@ subprocess_weak int subprocess_alive(struct subprocess_s *const process);
#endif
#endif
/* Whether posix_spawn reports a failed exec back to the caller. glibc only
started doing so in 2.24; before that the child silently exits with 127. */
/* Whether a failed exec is reported back to the caller. The fork() path always
reports it through its error pipe. glibc's posix_spawn only started doing so
in 2.24; before that the child silently exits with 127. */
#if !defined(SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS)
#if defined(__GLIBC__)
#if SUBPROCESS_SPAWN_VIA_FORK
#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1
#elif defined(__GLIBC__)
#if __GLIBC_PREREQ(2, 24)
#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1
#else
@@ -342,6 +378,14 @@ typedef intptr_t subprocess_intptr_t;
typedef size_t subprocess_size_t;
#endif
/* SIZE_T is ULONG_PTR, which is not size_t: on Win32 both are 32 bits wide but
unsigned long and unsigned int are still distinct types. */
#ifdef _WIN64
typedef subprocess_size_t subprocess_ulongptr_t;
#else
typedef unsigned long subprocess_ulongptr_t;
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-identifier"
@@ -351,6 +395,7 @@ typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
typedef struct _STARTUPINFOW *LPSTARTUPINFOW;
typedef struct _OVERLAPPED *LPOVERLAPPED;
typedef struct _PROC_THREAD_ATTRIBUTE_LIST *LPPROC_THREAD_ATTRIBUTE_LIST;
#ifdef __clang__
#pragma clang diagnostic pop
@@ -402,6 +447,11 @@ struct subprocess_startup_info_s {
void *hStdError;
};
struct subprocess_startup_info_ex_s {
struct subprocess_startup_info_s startupInfo;
void *attributeList;
};
struct subprocess_overlapped_s {
uintptr_t Internal;
uintptr_t InternalHigh;
@@ -451,6 +501,14 @@ __declspec(dllimport) int __stdcall CreateProcessW(
const subprocess_wchar_t *, subprocess_wchar_t *, LPSECURITY_ATTRIBUTES,
LPSECURITY_ATTRIBUTES, int, unsigned long, void *,
const subprocess_wchar_t *, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
__declspec(dllimport) int __stdcall
InitializeProcThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST, unsigned long,
unsigned long, subprocess_ulongptr_t *);
__declspec(dllimport) int __stdcall UpdateProcThreadAttribute(
LPPROC_THREAD_ATTRIBUTE_LIST, unsigned long, subprocess_ulongptr_t, void *,
subprocess_ulongptr_t, void *, subprocess_ulongptr_t *);
__declspec(dllimport) void __stdcall
DeleteProcThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST);
__declspec(dllimport) int __stdcall MultiByteToWideChar(
unsigned int, unsigned long, const char *, int, subprocess_wchar_t *, int);
__declspec(dllimport) int __stdcall CloseHandle(void *);
@@ -667,12 +725,104 @@ int subprocess_create_named_pipe_helper(void **rd, void **wr) {
}
#endif
#if !defined(_WIN32)
/* Move a pipe end off 0, 1 or 2. Duplicating a descriptor onto itself is a
no-op, so a pipe end already sitting on a standard descriptor would keep its
FD_CLOEXEC and be closed by exec, leaving the child without that stream. */
static int subprocess_fds_above_std(int fds[2]) {
int fd_flags;
int index;
int moved;
int saved_errno;
for (index = 0; index < 2; index++) {
if (fds[index] > STDERR_FILENO) {
continue;
}
moved = fcntl(fds[index], F_DUPFD, STDERR_FILENO + 1);
if (-1 != moved) {
fd_flags = fcntl(moved, F_GETFD, 0);
if ((-1 == fd_flags) ||
(-1 == fcntl(moved, F_SETFD, fd_flags | FD_CLOEXEC))) {
saved_errno = errno;
close(moved);
errno = saved_errno;
moved = -1;
}
}
if (-1 == moved) {
saved_errno = errno;
close(fds[0]);
close(fds[1]);
fds[0] = -1;
fds[1] = -1;
errno = saved_errno;
return -1;
}
close(fds[index]);
fds[index] = moved;
}
return 0;
}
/* Create pipes with close-on-exec set so later subprocesses do not inherit
descriptors belonging to subprocesses which are already running. */
static int subprocess_pipe_cloexec(int fds[2]) {
int fd_flags;
int index;
int saved_errno;
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
defined(__OpenBSD__) || defined(__DragonFly__) || \
(defined(__sun) && defined(__SVR4))
if (0 == pipe2(fds, O_CLOEXEC)) {
return subprocess_fds_above_std(fds);
}
/* Older kernels can lack pipe2 even when the C library declares it. */
if (ENOSYS != errno) {
return -1;
}
#endif
if (0 != pipe(fds)) {
return -1;
}
for (index = 0; index < 2; index++) {
fd_flags = fcntl(fds[index], F_GETFD, 0);
if ((-1 == fd_flags) ||
(-1 == fcntl(fds[index], F_SETFD, fd_flags | FD_CLOEXEC))) {
saved_errno = errno;
close(fds[0]);
close(fds[1]);
fds[0] = -1;
fds[1] = -1;
errno = saved_errno;
return -1;
}
}
return subprocess_fds_above_std(fds);
}
#endif
int subprocess_create(const char *const commandLine[], int options,
struct subprocess_s *const out_process) {
return subprocess_create_ex(commandLine, options, SUBPROCESS_NULL,
SUBPROCESS_NULL, out_process);
}
#if SUBPROCESS_SPAWN_VIA_FORK
/* Not every platform declares execvpe: AIX exports it from libc without ever
naming it in a header, and glibc hides it behind _GNU_SOURCE. */
extern int execvpe(const char *, char *const *, char *const *);
#endif
int subprocess_create_ex(const char *const commandLine[], int options,
const char *const environment[],
const char *const process_cwd,
@@ -692,6 +842,7 @@ int subprocess_create_ex(const char *const commandLine[], int options,
subprocess_size_t bs_run;
unsigned long flags = 0;
unsigned long last_error = 0;
int attribute_list_initialized = 0;
int result = subprocess_error_unknown;
const unsigned int codePageUtf8 = 65001;
const unsigned long mbErrInvalidChars = 0x00000008;
@@ -699,6 +850,8 @@ int subprocess_create_ex(const char *const commandLine[], int options,
const unsigned long handleFlagInherit = 0x00000001;
const unsigned long createNoWindow = 0x08000000;
const unsigned long createUnicodeEnvironment = 0x00000400;
const unsigned long extendedStartupInfoPresent = 0x00080000;
const subprocess_size_t procThreadAttributeHandleList = 0x00020002;
struct subprocess_subprocess_information_s processInfo = {SUBPROCESS_NULL,
SUBPROCESS_NULL, 0,
0};
@@ -706,6 +859,11 @@ int subprocess_create_ex(const char *const commandLine[], int options,
SUBPROCESS_NULL, 1};
subprocess_wchar_t empty_environment[2] = {0, 0};
subprocess_wchar_t *used_environment = SUBPROCESS_NULL;
subprocess_ulongptr_t attribute_list_size = 0;
subprocess_size_t inherited_handle_count = 0;
LPPROC_THREAD_ATTRIBUTE_LIST attribute_list = SUBPROCESS_NULL;
void *inherited_handles[3];
struct subprocess_startup_info_ex_s startInfoEx;
struct subprocess_startup_info_s startInfo = {0,
SUBPROCESS_NULL,
SUBPROCESS_NULL,
@@ -1080,6 +1238,44 @@ int subprocess_create_ex(const char *const commandLine[], int options,
}
}
/* Restrict inheritance to this subprocess's standard streams. Without a
handle list, concurrent subprocess_create calls can inherit each other's
temporarily-inheritable child pipe handles. */
inherited_handles[inherited_handle_count++] = startInfo.hStdInput;
inherited_handles[inherited_handle_count++] = startInfo.hStdOutput;
if (startInfo.hStdError != startInfo.hStdOutput) {
inherited_handles[inherited_handle_count++] = startInfo.hStdError;
}
InitializeProcThreadAttributeList(SUBPROCESS_NULL, 1, 0,
&attribute_list_size);
if (0 == attribute_list_size) {
result = subprocess_error_spawn;
goto cleanup;
}
attribute_list = SUBPROCESS_PTR_CAST(LPPROC_THREAD_ATTRIBUTE_LIST,
_alloca(attribute_list_size));
if (!attribute_list || !InitializeProcThreadAttributeList(
attribute_list, 1, 0, &attribute_list_size)) {
result = subprocess_error_spawn;
goto cleanup;
}
attribute_list_initialized = 1;
if (!UpdateProcThreadAttribute(
attribute_list, 0, procThreadAttributeHandleList, inherited_handles,
inherited_handle_count * sizeof(inherited_handles[0]),
SUBPROCESS_NULL, SUBPROCESS_NULL)) {
result = subprocess_error_spawn;
goto cleanup;
}
startInfoEx.startupInfo = startInfo;
startInfoEx.startupInfo.cb = sizeof(startInfoEx);
startInfoEx.attributeList = attribute_list;
flags |= extendedStartupInfoPresent;
if (!CreateProcessW(
SUBPROCESS_NULL,
commandLineCombinedWide, // command line
@@ -1090,7 +1286,7 @@ int subprocess_create_ex(const char *const commandLine[], int options,
used_environment, // used environment
process_cwd_wide, // use specified current directory
SUBPROCESS_PTR_CAST(LPSTARTUPINFOW,
&startInfo), // STARTUPINFO pointer
&startInfoEx), // STARTUPINFOEX pointer
SUBPROCESS_PTR_CAST(LPPROCESS_INFORMATION, &processInfo))) {
result = subprocess_error_from_windows_error(GetLastError());
if (subprocess_error_unknown == result) {
@@ -1099,6 +1295,9 @@ int subprocess_create_ex(const char *const commandLine[], int options,
goto cleanup;
}
DeleteProcThreadAttributeList(attribute_list);
attribute_list_initialized = 0;
out_process->hProcess = processInfo.hProcess;
processInfo.hProcess = SUBPROCESS_NULL;
@@ -1128,6 +1327,10 @@ int subprocess_create_ex(const char *const commandLine[], int options,
cleanup:
last_error = GetLastError();
if (attribute_list_initialized) {
DeleteProcThreadAttributeList(attribute_list);
}
if (subprocess_error_unknown == result) {
result = subprocess_error_from_windows_error(last_error);
}
@@ -1173,15 +1376,20 @@ cleanup:
int stderrfd[2] = {-1, -1};
int fd, fd_flags;
int async_no_wait;
int actions_created = 0;
int result = subprocess_error_unknown;
int saved_errno = 0;
int posix_error;
pid_t child = 0;
extern char **environ;
char *const empty_environment[1] = {SUBPROCESS_NULL};
posix_spawn_file_actions_t actions;
char *const *used_environment;
#if SUBPROCESS_SPAWN_VIA_FORK
/* Pipe used to relay the child's exec() errno back to the parent. */
int exec_errfd[2] = {-1, -1};
#else
int actions_created = 0;
int posix_error;
posix_spawn_file_actions_t actions;
#endif
async_no_wait = subprocess_option_enable_async_no_wait ==
(options & subprocess_option_enable_async_no_wait);
@@ -1202,13 +1410,13 @@ cleanup:
memset(out_process, 0, sizeof(*out_process));
if (0 != pipe(stdinfd)) {
if (0 != subprocess_pipe_cloexec(stdinfd)) {
saved_errno = errno;
result = subprocess_error_pipe;
goto cleanup;
}
if (0 != pipe(stdoutfd)) {
if (0 != subprocess_pipe_cloexec(stdoutfd)) {
saved_errno = errno;
result = subprocess_error_pipe;
goto cleanup;
@@ -1216,7 +1424,7 @@ cleanup:
if (subprocess_option_combined_stdout_stderr !=
(options & subprocess_option_combined_stdout_stderr)) {
if (0 != pipe(stderrfd)) {
if (0 != subprocess_pipe_cloexec(stderrfd)) {
saved_errno = errno;
result = subprocess_error_pipe;
goto cleanup;
@@ -1240,6 +1448,136 @@ cleanup:
used_environment = empty_environment;
}
#if SUBPROCESS_SPAWN_VIA_FORK
/* fork()+exec() instead of posix_spawn, so the child can chdir() first.
exec_errfd[1] is close-on-exec: a successful exec closes it and the parent
reads EOF; a failed exec writes errno through it before _exit. */
if (0 != pipe(exec_errfd)) {
saved_errno = errno;
result = subprocess_error_pipe;
goto cleanup;
}
if (-1 == fcntl(exec_errfd[1], F_SETFD, FD_CLOEXEC)) {
saved_errno = errno;
result = subprocess_error_spawn;
goto cleanup;
}
child = fork();
if (child < 0) {
saved_errno = errno;
result = subprocess_error_spawn;
goto cleanup;
}
if (0 == child) {
/* Child. Everything below must stay async-signal-safe: after fork() in a
threaded process only such functions may be called before exec. */
int child_errno;
close(exec_errfd[0]);
if ((-1 == dup2(stdinfd[0], STDIN_FILENO)) ||
(-1 == dup2(stdoutfd[1], STDOUT_FILENO))) {
goto child_failed;
}
if (subprocess_option_combined_stdout_stderr ==
(options & subprocess_option_combined_stdout_stderr)) {
if (-1 == dup2(STDOUT_FILENO, STDERR_FILENO)) {
goto child_failed;
}
} else {
if (-1 == dup2(stderrfd[1], STDERR_FILENO)) {
goto child_failed;
}
}
/* The originals are only closed once they have been duplicated, so that a
pipe end that already sits on 0, 1 or 2 is not closed out from under us. */
if (stdinfd[0] > STDERR_FILENO) {
close(stdinfd[0]);
}
if (stdinfd[1] > STDERR_FILENO) {
close(stdinfd[1]);
}
if (stdoutfd[0] > STDERR_FILENO) {
close(stdoutfd[0]);
}
if (stdoutfd[1] > STDERR_FILENO) {
close(stdoutfd[1]);
}
if (stderrfd[0] > STDERR_FILENO) {
close(stderrfd[0]);
}
if (stderrfd[1] > STDERR_FILENO) {
close(stderrfd[1]);
}
if (process_cwd && (0 != chdir(process_cwd))) {
goto child_failed;
}
#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)) {
execvpe(commandLine[0],
SUBPROCESS_CONST_CAST(char *const *, commandLine),
SUBPROCESS_CONST_CAST(char *const *, used_environment));
} else {
execve(commandLine[0],
SUBPROCESS_CONST_CAST(char *const *, commandLine),
SUBPROCESS_CONST_CAST(char *const *, used_environment));
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
child_failed:
child_errno = errno;
/* Nothing useful can be done if this write fails; the parent then sees EOF
and reports success, exactly as posix_spawn would without exec reporting. */
(void)!write(exec_errfd[1], &child_errno, sizeof(child_errno));
/* 127 is what POSIX requires posix_spawn's child to exit with when exec
fails, so both implementations look the same to a caller. */
_exit(127);
}
/* Parent. */
close(exec_errfd[1]);
exec_errfd[1] = -1;
{
int child_errno = 0;
ssize_t bytes_read;
do {
bytes_read = read(exec_errfd[0], &child_errno, sizeof(child_errno));
} while ((-1 == bytes_read) && (EINTR == errno));
close(exec_errfd[0]);
exec_errfd[0] = -1;
if (bytes_read == (ssize_t)sizeof(child_errno)) {
/* exec failed in the child. Reap it and surface the reason. */
while ((-1 == waitpid(child, SUBPROCESS_NULL, 0)) && (EINTR == errno)) {
}
child = 0;
saved_errno = child_errno;
result = subprocess_error_from_errno(child_errno);
if (subprocess_error_unknown == result) {
result = subprocess_error_spawn;
}
goto cleanup;
}
}
#else
posix_error = posix_spawn_file_actions_init(&actions);
if (0 != posix_error) {
saved_errno = posix_error;
@@ -1253,7 +1591,7 @@ cleanup:
// Set working directory
if (process_cwd) {
#if defined(__NetBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000)
#if SUBPROCESS_ADDCHDIR_IS_POSIX
posix_error = posix_spawn_file_actions_addchdir(&actions, process_cwd);
#elif !SUBPROCESS_HAVE_CWD
posix_error = ENOSYS;
@@ -1406,6 +1744,7 @@ cleanup:
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif /* SUBPROCESS_SPAWN_VIA_FORK */
// Close the stdin read end
close(stdinfd[0]);
@@ -1480,9 +1819,21 @@ cleanup:
result = subprocess_error_from_errno(saved_errno);
}
#if SUBPROCESS_SPAWN_VIA_FORK
if (-1 != exec_errfd[0]) {
close(exec_errfd[0]);
exec_errfd[0] = -1;
}
if (-1 != exec_errfd[1]) {
close(exec_errfd[1]);
exec_errfd[1] = -1;
}
#else
if (actions_created) {
posix_spawn_file_actions_destroy(&actions);
}
#endif
if (0 != result) {
if (child) {