summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--exec.c96
-rw-r--r--spawn.c171
-rw-r--r--spawn.h85
-rwxr-xr-xtests.sh9
-rw-r--r--tests/test_deep_stricter.out16
6 files changed, 296 insertions, 83 deletions
diff --git a/Makefile b/Makefile
index b18c61a..35a1496 100644
--- a/Makefile
+++ b/Makefile
@@ -51,7 +51,7 @@ ALL_LDFLAGS = $(ALL_CFLAGS) $(LDFLAGS)
all: bfs
-bfs: bftw.o color.o dstring.o eval.o exec.o main.o mtab.o opt.o parse.o printf.o stat.o typo.o util.o
+bfs: bftw.o color.o dstring.o eval.o exec.o main.o mtab.o opt.o parse.o printf.o spawn.o stat.o typo.o util.o
$(CC) $(ALL_LDFLAGS) $^ -o $@
sanitized: CFLAGS := -g $(WFLAGS) -fsanitize=address -fsanitize=undefined
diff --git a/exec.c b/exec.c
index 7d44d60..c2163e3 100644
--- a/exec.c
+++ b/exec.c
@@ -19,6 +19,7 @@
#include "cmdline.h"
#include "color.h"
#include "dstring.h"
+#include "spawn.h"
#include "util.h"
#include <assert.h>
#include <errno.h>
@@ -339,75 +340,56 @@ static int bfs_exec_spawn(const struct bfs_exec *execbuf) {
bfs_exec_debug(execbuf, "Executing '%s' ... [%zu arguments]\n", execbuf->argv[0], execbuf->argc - 1);
}
- // Use a pipe to report errors from the child
- int pipefd[2] = {-1, -1};
- if (pipe_cloexec(pipefd) != 0) {
- bfs_exec_debug(execbuf, "pipe() failed: %s\n", strerror(errno));
- }
-
- pid_t pid = fork();
+ pid_t pid = -1;
+ int error;
- if (pid < 0) {
- close(pipefd[1]);
- close(pipefd[0]);
+ struct bfs_spawn ctx;
+ if (bfs_spawn_init(&ctx) != 0) {
return -1;
- } else if (pid > 0) {
- // Parent
- close(pipefd[1]);
-
- int ret, error;
- ssize_t nbytes = read(pipefd[0], &error, sizeof(error));
- close(pipefd[0]);
- if (nbytes == sizeof(error)) {
- ret = -1;
- } else {
- ret = 0;
- error = 0;
- }
+ }
- int wstatus;
- if (waitpid(pid, &wstatus, 0) < 0) {
- return -1;
- }
+ if (bfs_spawn_setflags(&ctx, BFS_SPAWN_USEPATH) != 0) {
+ goto fail;
+ }
- if (WIFEXITED(wstatus)) {
- int status = WEXITSTATUS(wstatus);
- if (status != EXIT_SUCCESS) {
- bfs_exec_debug(execbuf, "Command '%s' failed with status %d\n", execbuf->argv[0], status);
- ret = -1;
- }
- } else if (WIFSIGNALED(wstatus)) {
- int sig = WTERMSIG(wstatus);
- bfs_exec_debug(execbuf, "Command '%s' terminated by signal %d\n", execbuf->argv[0], sig);
- ret = -1;
- } else {
- bfs_exec_debug(execbuf, "Command '%s' terminated abnormally\n", execbuf->argv[0]);
- ret = -1;
+ if (execbuf->wd_fd >= 0) {
+ if (bfs_spawn_addfchdir(&ctx, execbuf->wd_fd) != 0) {
+ goto fail;
}
+ }
+ pid = bfs_spawn(execbuf->argv[0], &ctx, execbuf->argv, environ);
+fail:
+ error = errno;
+ bfs_spawn_destroy(&ctx);
+ if (pid < 0) {
errno = error;
- return ret;
- } else {
- // Child
- close(pipefd[0]);
+ return -1;
+ }
- if (execbuf->wd_fd >= 0) {
- if (fchdir(execbuf->wd_fd) != 0) {
- goto child_err;
- }
- }
+ int wstatus;
+ if (waitpid(pid, &wstatus, 0) < 0) {
+ return -1;
+ }
- execvp(execbuf->argv[0], execbuf->argv);
+ int ret = -1;
- int error;
- child_err:
- error = errno;
- if (write(pipefd[1], &error, sizeof(error)) != sizeof(error)) {
- // Parent will still see that we exited unsuccessfully, but won't know why
+ if (WIFEXITED(wstatus)) {
+ int status = WEXITSTATUS(wstatus);
+ if (status == EXIT_SUCCESS) {
+ ret = 0;
+ } else {
+ bfs_exec_debug(execbuf, "Command '%s' failed with status %d\n", execbuf->argv[0], status);
}
- close(pipefd[1]);
- _Exit(EXIT_FAILURE);
+ } else if (WIFSIGNALED(wstatus)) {
+ int sig = WTERMSIG(wstatus);
+ bfs_exec_debug(execbuf, "Command '%s' terminated by signal %d\n", execbuf->argv[0], sig);
+ } else {
+ bfs_exec_debug(execbuf, "Command '%s' terminated abnormally\n", execbuf->argv[0]);
}
+
+ errno = 0;
+ return ret;
}
/** exec() a command for a single file. */
diff --git a/spawn.c b/spawn.c
new file mode 100644
index 0000000..ffc3d2e
--- /dev/null
+++ b/spawn.c
@@ -0,0 +1,171 @@
+/****************************************************************************
+ * bfs *
+ * Copyright (C) 2018 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted. *
+ * *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
+ ****************************************************************************/
+
+#include "spawn.h"
+#include "util.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/**
+ * Types of spawn actions.
+ */
+enum bfs_spawn_op {
+ BFS_SPAWN_FCHDIR,
+};
+
+/**
+ * A spawn action.
+ */
+struct bfs_spawn_action {
+ struct bfs_spawn_action *next;
+
+ enum bfs_spawn_op op;
+ int fd;
+};
+
+int bfs_spawn_init(struct bfs_spawn *ctx) {
+ ctx->flags = 0;
+ ctx->actions = NULL;
+ ctx->tail = &ctx->actions;
+ return 0;
+}
+
+int bfs_spawn_destroy(struct bfs_spawn *ctx) {
+ struct bfs_spawn_action *action = ctx->actions;
+ while (action) {
+ struct bfs_spawn_action *next = action->next;
+ free(action);
+ action = next;
+ }
+ return 0;
+}
+
+int bfs_spawn_setflags(struct bfs_spawn *ctx, enum bfs_spawn_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) {
+ struct bfs_spawn_action *action = malloc(sizeof(*action));
+ if (action) {
+ action->next = NULL;
+ action->op = op;
+ action->fd = -1;
+
+ *ctx->tail = action;
+ ctx->tail = &action->next;
+ }
+ return action;
+}
+
+int bfs_spawn_addfchdir(struct bfs_spawn *ctx, int fd) {
+ if (fd < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ struct bfs_spawn_action *action = bfs_spawn_add(ctx, BFS_SPAWN_FCHDIR);
+ if (action) {
+ action->fd = fd;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/** 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]);
+
+ for (const struct bfs_spawn_action *action = actions; action; action = action->next) {
+ switch (action->op) {
+ case BFS_SPAWN_FCHDIR:
+ if (fchdir(action->fd) != 0) {
+ goto fail;
+ }
+ break;
+ }
+ }
+
+ if (flags & BFS_SPAWN_USEPATH) {
+ bfs_execvpe(exe, argv, envp);
+ } else {
+ execve(exe, argv, envp);
+ }
+
+fail:
+ error = errno;
+ while (write(pipefd[1], &error, sizeof(error)) < sizeof(error));
+ close(pipefd[1]);
+ _Exit(127);
+}
+
+pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp) {
+ // Use a pipe to report errors from the child
+ int pipefd[2];
+ if (pipe_cloexec(pipefd) != 0) {
+ return -1;
+ }
+
+ int error;
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ error = errno;
+ close(pipefd[1]);
+ close(pipefd[0]);
+ errno = error;
+ return -1;
+ } else if (pid == 0) {
+ // Child
+ bfs_spawn_exec(exe, ctx, argv, envp, pipefd);
+ }
+
+ // Parent
+ close(pipefd[1]);
+
+ ssize_t nbytes = read(pipefd[0], &error, sizeof(error));
+ close(pipefd[0]);
+ if (nbytes == sizeof(error)) {
+ int wstatus;
+ waitpid(pid, &wstatus, 0);
+ errno = error;
+ return -1;
+ }
+
+ return pid;
+}
diff --git a/spawn.h b/spawn.h
new file mode 100644
index 0000000..ef510e1
--- /dev/null
+++ b/spawn.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+ * bfs *
+ * Copyright (C) 2018 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted. *
+ * *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
+ ****************************************************************************/
+
+#ifndef BFS_SPAWN_H
+#define BFS_SPAWN_H
+
+#include <stdbool.h>
+#include <signal.h>
+#include <sys/types.h>
+
+/**
+ * bfs_spawn() flags.
+ */
+enum bfs_spawn_flags {
+ /** Use the PATH variable to resolve the executable (like execvp()). */
+ BFS_SPAWN_USEPATH = 1 << 0,
+};
+
+/**
+ * bfs_spawn() attributes, controlling the context of the new process.
+ */
+struct bfs_spawn {
+ enum bfs_spawn_flags flags;
+ struct bfs_spawn_action *actions;
+ struct bfs_spawn_action **tail;
+};
+
+/**
+ * Create a new bfs_spawn() context.
+ *
+ * @return 0 on success, -1 on failure.
+ */
+int bfs_spawn_init(struct bfs_spawn *ctx);
+
+/**
+ * Destroy a bfs_spawn() context.
+ *
+ * @return 0 on success, -1 on failure.
+ */
+int bfs_spawn_destroy(struct bfs_spawn *ctx);
+
+/**
+ * Set the flags for a bfs_spawn() context.
+ *
+ * @return 0 on success, -1 on failure.
+ */
+int bfs_spawn_setflags(struct bfs_spawn *ctx, enum bfs_spawn_flags flags);
+
+/**
+ * Add an fchdir() action to a bfs_spawn() context.
+ *
+ * @return 0 on success, -1 on failure.
+ */
+int bfs_spawn_addfchdir(struct bfs_spawn *ctx, int fd);
+
+/**
+ * Spawn a new process.
+ *
+ * @param exe
+ * The executable to run.
+ * @param ctx
+ * The context for the new process.
+ * @param argv
+ * The arguments for the new process.
+ * @param envp
+ * The environment variables for the new process.
+ * @return
+ * The PID of the new process, or -1 on error.
+ */
+pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp);
+
+#endif // BFS_SPAWN_H
diff --git a/tests.sh b/tests.sh
index 1ad1568..c732979 100755
--- a/tests.sh
+++ b/tests.sh
@@ -438,7 +438,6 @@ bfs_tests=(
test_expr_path_flag
test_colors
test_deep_strict
- test_deep_stricter
)
BSD=yes
@@ -1523,14 +1522,6 @@ function test_deep_strict() {
bfs_diff deep -mindepth 18 -exec /bin/bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' /bin/bash '{}' \;
}
-function test_deep_stricter() {
- closefrom 4
-
- # So few fds that pipe() fails in the -exec implementation
- ulimit -n 6
- bfs_diff deep -mindepth 18 -exec /bin/bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' /bin/bash '{}' \;
-}
-
function test_exit() {
invoke_bfs basic -name foo -exit 42
if [ $? -ne 42 ]; then
diff --git a/tests/test_deep_stricter.out b/tests/test_deep_stricter.out
deleted file mode 100644
index c385fce..0000000
--- a/tests/test_deep_stricter.out
+++ /dev/null
@@ -1,16 +0,0 @@
-deep/0/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/1/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/2/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/3/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/4/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/5/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/6/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/7/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/8/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/9/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/A/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/B/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/C/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/D/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/E/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)
-deep/F/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE (4358)