process-1.6.13.2: cbits/posix/posix_spawn.c
#include "runProcess.h"
#include "common.h"
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#if !defined(HAVE_POSIX_SPAWNP)
ProcHandle
do_spawn_posix (char *const args[],
char *workingDirectory, char **environment,
struct std_handle *stdInHdl,
struct std_handle *stdOutHdl,
struct std_handle *stdErrHdl,
gid_t *childGroup, uid_t *childUser,
int flags,
char **failed_doing)
{
return -2;
}
#else
// Necessary for POSIX_SPAWN_SETSID under glibc.
#define _GNU_SOURCE
#include <spawn.h>
extern char **environ;
static int
setup_std_handle_spawn (int fd,
struct std_handle *hdl,
posix_spawn_file_actions_t *fa,
char **failed_doing)
{
switch (hdl->behavior) {
case STD_HANDLE_CLOSE:
if (posix_spawn_file_actions_addclose(fa, fd) != 0) {
*failed_doing = "posix_spawn_file_actions_addclose";
return -1;
}
return 0;
case STD_HANDLE_USE_FD:
// N.B. POSIX specifies that dup2(x,x) should be a no-op, but
// naturally Apple ignores this and rather fails in posix_spawn on Big
// Sur.
if (hdl->use_fd != fd) {
if (posix_spawn_file_actions_adddup2(fa, hdl->use_fd, fd) != 0) {
*failed_doing = "posix_spawn_file_actions_adddup2";
return -1;
}
}
return 0;
case STD_HANDLE_USE_PIPE:
if (hdl->use_pipe.child_end != fd) {
if (posix_spawn_file_actions_adddup2(fa, hdl->use_pipe.child_end, fd) != 0) {
*failed_doing = "posix_spawn_file_actions_adddup2(child_end)";
return -1;
}
if (posix_spawn_file_actions_addclose(fa, hdl->use_pipe.child_end) != 0) {
*failed_doing = "posix_spawn_file_actions_addclose(child_end)";
return -1;
}
}
if (posix_spawn_file_actions_addclose(fa, hdl->use_pipe.parent_end) != 0) {
*failed_doing = "posix_spawn_file_actions_addclose(parent_end)";
return -1;
}
return 0;
default:
// N.B. this should be unreachable
// but some compilers apparently can't
// see this.
*failed_doing = "posix_spawn_file_actions_addclose(invalid behavior)";
return -1;
}
}
/* Try spawning with posix_spawn. Returns -1 if posix_spawn (or the particular
* requested configuration) is not supported.
*/
ProcHandle
do_spawn_posix (char *const args[],
char *workingDirectory, char **environment,
struct std_handle *stdInHdl,
struct std_handle *stdOutHdl,
struct std_handle *stdErrHdl,
gid_t *childGroup, uid_t *childUser,
int flags,
char **failed_doing)
{
// First do catch some cases that posix_spawn simply can't handle...
if (childGroup || childUser) {
return -2;
}
if ((flags & RUN_PROCESS_IN_CLOSE_FDS) != 0) {
// TODO: can this be efficiently supported?
return -2;
}
// Now the main act...
pid_t pid;
posix_spawn_file_actions_t fa;
posix_spawnattr_t sa;
int r;
ProcHandle ret;
short spawn_flags = 0;
r = posix_spawn_file_actions_init(&fa);
if (r != 0) {
*failed_doing = "posix_spawn_file_actions_init";
return -1;
}
r = posix_spawnattr_init(&sa);
if (r != 0) {
posix_spawn_file_actions_destroy(&fa);
*failed_doing = "posix_spawnattr_init";
return -1;
}
if (workingDirectory) {
#if defined(HAVE_posix_spawn_file_actions_addchdir_np)
// N.B. this function is broken on macOS.
// See https://github.com/rust-lang/rust/pull/80537.
r = posix_spawn_file_actions_addchdir(&fa, workingDirectory);
if (r != 0) {
*failed_doing = "posix_spawn_file_actions_addchdir";
goto fail;
}
#else
goto not_supported;
#endif
}
if ((flags & RUN_PROCESS_NEW_SESSION) != 0) {
#if defined(HAVE_POSIX_SPAWN_SETSID)
// N.B. POSIX_SPAWN_SETSID is only supported on macOS 10.15 and later,
// but an proper error is not returned in earlier versions.
// See https://bugs.python.org/issue37586.
if (posix_spawnattr_setflags(&sa, POSIX_SPAWN_SETSID) != 0) {
*failed_doing = "posix_spawnattr_setflags(POSIX_SPAWN_SETSID)";
goto fail;
}
#elif defined(HAVE_POSIX_SPAWN_SETSID_NP)
if (posix_spawnattr_setflags(&sa, POSIX_SPAWN_SETSID_NP) != 0) {
*failed_doing = "posix_spawnattr_setflags(POSIX_SPAWN_SETSID_NP)";
goto fail;
}
#else
goto not_supported;
#endif
}
if ((flags & RUN_PROCESS_IN_NEW_GROUP) != 0) {
#if defined(HAVE_POSIX_SPAWN_SETPGROUP)
spawn_flags |= POSIX_SPAWN_SETPGROUP;
#else
goto not_supported;
#endif
}
if (setup_std_handle_spawn(STDIN_FILENO, stdInHdl, &fa, failed_doing) != 0) {
goto fail;
}
if (setup_std_handle_spawn(STDOUT_FILENO, stdOutHdl, &fa, failed_doing) != 0) {
goto fail;
}
if (setup_std_handle_spawn(STDERR_FILENO, stdErrHdl, &fa, failed_doing) != 0) {
goto fail;
}
sigset_t ss;
if ((flags & RESET_INT_QUIT_HANDLERS) != 0) {
if (sigemptyset(&ss) == -1) {
*failed_doing = "sigemptyset";
goto fail;
}
if (sigaddset(&ss, SIGINT) == -1) {
*failed_doing = "sigaddset(SIGINT)";
goto fail;
}
if (sigaddset(&ss, SIGQUIT) == -1) {
*failed_doing = "sigaddset(SIGQUIT)";
goto fail;
}
if (posix_spawnattr_setsigdefault(&sa, &ss) != 0) {
*failed_doing = "posix_spawnattr_setsigdefault";
goto fail;
}
spawn_flags |= POSIX_SPAWN_SETSIGDEF;
}
if (posix_spawnattr_setflags(&sa, spawn_flags) != 0) {
*failed_doing = "posix_spawnattr_setflags";
goto fail;
}
r = posix_spawnp(&pid, args[0], &fa, &sa, args, environment ? environment : environ);
if (r != 0) {
*failed_doing = "posix_spawnp";
goto fail;
} else {
ret = pid;
goto finish;
}
not_supported:
ret = -2;
goto finish;
fail:
ret = -1;
goto finish;
finish:
posix_spawn_file_actions_destroy(&fa);
posix_spawnattr_destroy(&sa);
return ret;
}
#endif