From 95fbde17a66377b6fbe7ff1f014301dbbf09270d Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 2 Nov 2023 11:54:03 -0400 Subject: xspawn: Wrap the real posix_spawn() if possible Fixes #47. --- src/exec.c | 2 +- src/xspawn.c | 167 +++++++++++++++++++++++++++++++++++++++++++---------------- src/xspawn.h | 15 +++++- 3 files changed, 136 insertions(+), 48 deletions(-) diff --git a/src/exec.c b/src/exec.c index a3297e8..e3f7b97 100644 --- a/src/exec.c +++ b/src/exec.c @@ -363,7 +363,7 @@ static int bfs_exec_spawn(const struct bfs_exec *execbuf) { return -1; } - if (bfs_spawn_setflags(&spawn, BFS_SPAWN_USEPATH) != 0) { + if (bfs_spawn_setflags(&spawn, BFS_SPAWN_USE_PATH) != 0) { goto fail; } diff --git a/src/xspawn.c b/src/xspawn.c index 64759e0..ac9e401 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -8,6 +8,7 @@ #include "list.h" #include #include +#include #include #include #include @@ -43,12 +44,27 @@ struct bfs_spawn_action { }; int bfs_spawn_init(struct bfs_spawn *ctx) { - ctx->flags = 0; + ctx->flags = BFS_SPAWN_USE_POSIX; SLIST_INIT(ctx); + + errno = posix_spawnattr_init(&ctx->attr); + if (errno != 0) { + return -1; + } + + errno = posix_spawn_file_actions_init(&ctx->actions); + if (errno != 0) { + posix_spawnattr_destroy(&ctx->attr); + return -1; + } + return 0; } int bfs_spawn_destroy(struct bfs_spawn *ctx) { + posix_spawn_file_actions_destroy(&ctx->actions); + posix_spawnattr_destroy(&ctx->attr); + for_slist (struct bfs_spawn_action, action, ctx) { free(action); } @@ -57,12 +73,12 @@ int bfs_spawn_destroy(struct bfs_spawn *ctx) { } int bfs_spawn_setflags(struct bfs_spawn *ctx, enum bfs_spawn_flags flags) { - ctx->flags = flags; + ctx->flags |= flags; return 0; } -/** Add a spawn action to the chain. */ -static struct bfs_spawn_action *bfs_spawn_add(struct bfs_spawn *ctx, enum bfs_spawn_op op) { +/** Allocate a spawn action. */ +static struct bfs_spawn_action *bfs_spawn_action(enum bfs_spawn_op op) { struct bfs_spawn_action *action = ALLOC(struct bfs_spawn_action); if (!action) { return NULL; @@ -72,70 +88,122 @@ static struct bfs_spawn_action *bfs_spawn_add(struct bfs_spawn *ctx, enum bfs_sp action->op = op; action->in_fd = -1; action->out_fd = -1; - - SLIST_APPEND(ctx, action); return action; } int bfs_spawn_addclose(struct bfs_spawn *ctx, int fd) { - if (fd < 0) { - errno = EBADF; + struct bfs_spawn_action *action = bfs_spawn_action(BFS_SPAWN_CLOSE); + if (!action) { return -1; } - struct bfs_spawn_action *action = bfs_spawn_add(ctx, BFS_SPAWN_CLOSE); - if (action) { - action->out_fd = fd; - return 0; - } else { - return -1; + if (ctx->flags & BFS_SPAWN_USE_POSIX) { + errno = posix_spawn_file_actions_addclose(&ctx->actions, fd); + if (errno != 0) { + free(action); + return -1; + } } + + action->out_fd = fd; + SLIST_APPEND(ctx, action); + return 0; } int bfs_spawn_adddup2(struct bfs_spawn *ctx, int oldfd, int newfd) { - if (oldfd < 0 || newfd < 0) { - errno = EBADF; + struct bfs_spawn_action *action = bfs_spawn_action(BFS_SPAWN_DUP2); + if (!action) { return -1; } - struct bfs_spawn_action *action = bfs_spawn_add(ctx, BFS_SPAWN_DUP2); - if (action) { - action->in_fd = oldfd; - action->out_fd = newfd; - return 0; - } else { - return -1; + if (ctx->flags & BFS_SPAWN_USE_POSIX) { + errno = posix_spawn_file_actions_adddup2(&ctx->actions, oldfd, newfd); + if (errno != 0) { + free(action); + return -1; + } } + + action->in_fd = oldfd; + action->out_fd = newfd; + SLIST_APPEND(ctx, action); + return 0; } int bfs_spawn_addfchdir(struct bfs_spawn *ctx, int fd) { - if (fd < 0) { - errno = EBADF; + struct bfs_spawn_action *action = bfs_spawn_action(BFS_SPAWN_FCHDIR); + if (!action) { return -1; } - struct bfs_spawn_action *action = bfs_spawn_add(ctx, BFS_SPAWN_FCHDIR); - if (action) { - action->in_fd = fd; - return 0; - } else { - return -1; +#ifndef BFS_HAS_POSIX_SPAWN_FCHDIR +# define BFS_HAS_POSIX_SPAWN_FCHDIR __NetBSD__ +#endif + +#ifndef BFS_HAS_POSIX_SPAWN_FCHDIR_NP +# if __GLIBC__ +# define BFS_HAS_POSIX_SPAWN_FCHDIR_NP __GLIBC_PREREQ(2, 29) +# elif __ANDROID__ +# define BFS_HAS_POSIX_SPAWN_FCHDIR_NP (__ANDROID_API__ >= 34) +# else +# define BFS_HAS_POSIX_SPAWN_FCHDIR_NP (__linux__ || __FreeBSD__ || __APPLE__) +# endif +#endif + +#if BFS_HAS_POSIX_SPAWN_FCHDIR || BFS_HAS_POSIX_SPAWN_FCHDIR_NP + if (ctx->flags & BFS_SPAWN_USE_POSIX) { +# if BFS_HAS_POSIX_SPAWN_FCHDIR + errno = posix_spawn_file_actions_addfchdir(&ctx->actions, fd); +# else + errno = posix_spawn_file_actions_addfchdir_np(&ctx->actions, fd); +# endif + if (errno != 0) { + free(action); + return -1; + } } +#else + ctx->flags &= ~BFS_SPAWN_USE_POSIX; +#endif + + action->in_fd = fd; + SLIST_APPEND(ctx, action); + return 0; } int bfs_spawn_addsetrlimit(struct bfs_spawn *ctx, int resource, const struct rlimit *rl) { - struct bfs_spawn_action *action = bfs_spawn_add(ctx, BFS_SPAWN_SETRLIMIT); - if (action) { - action->resource = resource; - action->rlimit = *rl; - return 0; - } else { + struct bfs_spawn_action *action = bfs_spawn_action(BFS_SPAWN_SETRLIMIT); + if (!action) { return -1; } + + ctx->flags &= ~BFS_SPAWN_USE_POSIX; + + action->resource = resource; + action->rlimit = *rl; + SLIST_APPEND(ctx, action); + return 0; +} + +/** bfs_spawn() implementation using posix_spawn(). */ +static pid_t bfs_posix_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp) { + pid_t ret; + + if (ctx->flags & BFS_SPAWN_USE_PATH) { + errno = posix_spawnp(&ret, exe, &ctx->actions, &ctx->attr, argv, envp); + } else { + errno = posix_spawn(&ret, exe, &ctx->actions, &ctx->attr, argv, envp); + } + + if (errno != 0) { + ret = -1; + } + + return ret; } /** Actually exec() the new process. */ -static void bfs_spawn_exec(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp, int pipefd[2]) { +static noreturn void bfs_spawn_exec(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp, int pipefd[2]) { xclose(pipefd[0]); for_slist (const struct bfs_spawn_action, action, ctx) { @@ -193,14 +261,10 @@ fail: _Exit(127); } -pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp) { - extern char **environ; - if (!envp) { - envp = environ; - } - +/** bfs_spawn() implementation using fork()/exec(). */ +static pid_t bfs_fork_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp) { char *resolved = NULL; - if (ctx->flags & BFS_SPAWN_USEPATH) { + if (ctx->flags & BFS_SPAWN_USE_PATH) { exe = resolved = bfs_spawn_resolve(exe); if (!resolved) { return -1; @@ -242,6 +306,19 @@ pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char return pid; } +pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp) { + extern char **environ; + if (!envp) { + envp = environ; + } + + if (ctx->flags & BFS_SPAWN_USE_POSIX) { + return bfs_posix_spawn(exe, ctx, argv, envp); + } else { + return bfs_fork_spawn(exe, ctx, argv, envp); + } +} + char *bfs_spawn_resolve(const char *exe) { if (strchr(exe, '/')) { return strdup(exe); diff --git a/src/xspawn.h b/src/xspawn.h index d9b4a2e..2a3d736 100644 --- a/src/xspawn.h +++ b/src/xspawn.h @@ -8,6 +8,8 @@ #ifndef BFS_XSPAWN_H #define BFS_XSPAWN_H +#include "config.h" +#include #include #include @@ -16,16 +18,25 @@ */ enum bfs_spawn_flags { /** Use the PATH variable to resolve the executable (like execvp()). */ - BFS_SPAWN_USEPATH = 1 << 0, + BFS_SPAWN_USE_PATH = 1 << 0, + /** Whether posix_spawn() can be used. */ + BFS_SPAWN_USE_POSIX = 1 << 1, }; /** * bfs_spawn() attributes, controlling the context of the new process. */ struct bfs_spawn { + /** Spawn flags. */ enum bfs_spawn_flags flags; + + /** Linked list of actions. */ struct bfs_spawn_action *head; struct bfs_spawn_action **tail; + + /** pthread_spawn() context, for when we can use it. */ + posix_spawnattr_t attr; + posix_spawn_file_actions_t actions; }; /** @@ -95,7 +106,7 @@ int bfs_spawn_addsetrlimit(struct bfs_spawn *ctx, int resource, const struct rli pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp); /** - * Look up an executable in the current PATH, as BFS_SPAWN_USEPATH or execvp() + * Look up an executable in the current PATH, as BFS_SPAWN_USE_PATH or execvp() * would do. * * @param exe -- cgit v1.2.3