diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2021-06-13 15:53:06 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2021-06-13 16:10:13 -0400 |
commit | db67752672877ea0f5360699cb34ad31065ef39e (patch) | |
tree | 974bed22b2ed747a2c7b9cf5b1f57a370a646df3 | |
parent | ae15c5abe0bb2d8bd5fd3502721288bcb1a85d59 (diff) | |
download | bfs-db67752672877ea0f5360699cb34ad31065ef39e.tar.xz |
spawn: New function for resolving executables in the PATH
This fixes the BFS_SPAWN_USEPATH to use the parent's environment, not
the new child's environment, to resolve the executable.
-rw-r--r-- | spawn.c | 95 | ||||
-rw-r--r-- | spawn.h | 13 |
2 files changed, 91 insertions, 17 deletions
@@ -17,7 +17,9 @@ #include "spawn.h" #include "util.h" #include <errno.h> +#include <fcntl.h> #include <stdlib.h> +#include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> @@ -139,21 +141,9 @@ int bfs_spawn_addsetrlimit(struct bfs_spawn *ctx, int resource, const struct rli } } -/** Facade for execvpe() which is non-standard. */ -static int bfs_execvpe(const char *exe, char **argv, char **envp) { -#if __GLIBC__ || __linux__ || __NetBSD__ || __OpenBSD__ - return execvpe(exe, argv, envp); -#else - extern char **environ; - environ = envp; - return execvp(exe, argv); -#endif -} - /** 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]) { int error; - enum bfs_spawn_flags flags = ctx ? ctx->flags : 0; const struct bfs_spawn_action *actions = ctx ? ctx->actions : NULL; close(pipefd[0]); @@ -199,11 +189,7 @@ static void bfs_spawn_exec(const char *exe, const struct bfs_spawn *ctx, char ** } } - if (flags & BFS_SPAWN_USEPATH) { - bfs_execvpe(exe, argv, envp); - } else { - execve(exe, argv, envp); - } + execve(exe, argv, envp); fail: error = errno; @@ -222,6 +208,15 @@ pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char envp = environ; } + enum bfs_spawn_flags flags = ctx ? ctx->flags : 0; + char *resolved = NULL; + if (flags & BFS_SPAWN_USEPATH) { + exe = resolved = bfs_spawn_resolve(exe); + if (!resolved) { + return -1; + } + } + // Use a pipe to report errors from the child int pipefd[2]; if (pipe_cloexec(pipefd) != 0) { @@ -244,6 +239,7 @@ pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char // Parent close(pipefd[1]); + free(resolved); ssize_t nbytes = xread(pipefd[0], &error, sizeof(error)); close(pipefd[0]); @@ -256,3 +252,68 @@ pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char return pid; } + +char *bfs_spawn_resolve(const char *exe) { + if (strchr(exe, '/')) { + return strdup(exe); + } + + const char *path = getenv("PATH"); + + char *confpath = NULL; + if (!path) { + path = confpath = xconfstr(_CS_PATH); + if (!path) { + return NULL; + } + } + + size_t cap = 0; + char *ret = NULL; + while (true) { + const char *end = strchr(path, ':'); + size_t len = end ? (size_t)(end - path) : strlen(path); + + // POSIX 8.3: "A zero-length prefix is a legacy feature that + // indicates the current working directory." + if (len == 0) { + path = "."; + len = 1; + } + + size_t total = len + 1 + strlen(exe) + 1; + if (cap < total) { + char *grown = realloc(ret, total); + if (!grown) { + goto fail; + } + ret = grown; + cap = total; + } + + memcpy(ret, path, len); + if (ret[len - 1] != '/') { + ret[len++] = '/'; + } + strcpy(ret + len, exe); + + if (xfaccessat(AT_FDCWD, ret, X_OK) == 0) { + break; + } + + if (!end) { + errno = ENOENT; + goto fail; + } + + path = end + 1; + } + + free(confpath); + return ret; + +fail: + free(confpath); + free(ret); + return NULL; +} @@ -107,4 +107,17 @@ 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() + * would do. + * + * @param exe + * The name of the binary to execute. Bare names without a '/' will be + * searched on the provided PATH. + * @return + * The full path to the executable, which should be free()'d, or NULL on + * failure. + */ +char *bfs_spawn_resolve(const char *exe); + #endif // BFS_SPAWN_H |