summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2018-09-18 21:29:59 -0400
committerTavian Barnes <tavianator@tavianator.com>2018-09-18 22:04:07 -0400
commitabf4394ae34bf25a17efb9fa560791005a6b217b (patch)
tree8717654c659deead1c8c162407cf2633ccb14ea5
parent727562e99082e9ed808b5ff6e02b65b9cf5b65c3 (diff)
downloadbfs-abf4394ae34bf25a17efb9fa560791005a6b217b.tar.xz
spawn: New posix_spawn()-like API for exec
-rw-r--r--Makefile4
-rw-r--r--exec.c98
-rw-r--r--spawn.c158
-rw-r--r--spawn.h43
-rwxr-xr-xtests.sh9
-rw-r--r--tests/test_deep_stricter.out16
6 files changed, 243 insertions, 85 deletions
diff --git a/Makefile b/Makefile
index cb6a4a0..fcab96f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
############################################################################
# bfs #
-# Copyright (C) 2015-2017 Tavian Barnes <tavianator@tavianator.com> #
+# Copyright (C) 2015-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. #
@@ -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 3cb05f4..c2163e3 100644
--- a/exec.c
+++ b/exec.c
@@ -1,6 +1,6 @@
/****************************************************************************
* bfs *
- * Copyright (C) 2017 Tavian Barnes <tavianator@tavianator.com> *
+ * Copyright (C) 2017-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. *
@@ -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..82bce11
--- /dev/null
+++ b/spawn.c
@@ -0,0 +1,158 @@
+/****************************************************************************
+ * 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;
+}
+
+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;
+ }
+}
+
+static void bfs_spawn_exec(const char *file, 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) {
+ execvpe(file, argv, envp);
+ } else {
+ execve(file, 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 *file, 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(file, 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..6a8a15c
--- /dev/null
+++ b/spawn.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+ * 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>
+
+enum bfs_spawn_flags {
+ BFS_SPAWN_USEPATH = 1 << 0,
+};
+
+struct bfs_spawn {
+ enum bfs_spawn_flags flags;
+ struct bfs_spawn_action *actions;
+ struct bfs_spawn_action **tail;
+};
+
+int bfs_spawn_init(struct bfs_spawn *ctx);
+int bfs_spawn_destroy(struct bfs_spawn *ctx);
+
+int bfs_spawn_setflags(struct bfs_spawn *ctx, enum bfs_spawn_flags flags);
+
+int bfs_spawn_addfchdir(struct bfs_spawn *ctx, int fd);
+
+pid_t bfs_spawn(const char *file, 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)