summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2024-03-27 12:41:33 -0400
committerTavian Barnes <tavianator@tavianator.com>2024-03-27 13:13:23 -0400
commit0ad5c7edda2de25e47077d138d7c371f94590a22 (patch)
treefe4d72db7eeb97ea666ae4b30d9e9808946fbcbe
parent42972dfac6cd8faef6b1e2ddc6236d554daf433f (diff)
downloadbfs-0ad5c7edda2de25e47077d138d7c371f94590a22.tar.xz
xspawn: Don't use posix_spawnp() on macOS
On macOS, posix_spawnp() resolves the executable against the $PATH *before* the file_actions are applied, contrary to the upcoming POSIX wording [1] for posix_spawn_file_actions_addfchdir(). [1]: https://www.austingroupbugs.net/view.php?id=1208#c4830
-rw-r--r--src/xspawn.c34
1 files changed, 33 insertions, 1 deletions
diff --git a/src/xspawn.c b/src/xspawn.c
index dc2bf67..f67adec 100644
--- a/src/xspawn.c
+++ b/src/xspawn.c
@@ -202,6 +202,18 @@ int bfs_spawn_adddup2(struct bfs_spawn *ctx, int oldfd, int newfd) {
return 0;
}
+/**
+ * https://www.austingroupbugs.net/view.php?id=1208#c4830 says:
+ *
+ * ... a search of the directories passed as the environment variable
+ * PATH ..., using the working directory of the child process after all
+ * file_actions have been performed.
+ *
+ * but macOS resolves the PATH *before* file_actions (because there
+ * posix_spawn() is its own syscall).
+ */
+#define BFS_POSIX_SPAWNP_AFTER_FCHDIR !__APPLE__
+
int bfs_spawn_addfchdir(struct bfs_spawn *ctx, int fd) {
struct bfs_spawn_action *action = bfs_spawn_action(BFS_SPAWN_FCHDIR);
if (!action) {
@@ -452,12 +464,15 @@ static int bfs_resolve_early(struct bfs_resolver *res, const char *exe, const st
}
bool can_finish = bfs_can_resolve_early(res, ctx);
+
+#if BFS_POSIX_SPAWNP_AFTER_FCHDIR
bool use_posix = ctx && (ctx->flags & BFS_SPAWN_USE_POSIX);
if (!can_finish && use_posix) {
// posix_spawnp() will do the resolution, so don't bother
// allocating a buffer
return 0;
}
+#endif
res->len = bfs_resolve_capacity(res);
res->buf = malloc(res->len);
@@ -477,6 +492,7 @@ fail:
}
#if _POSIX_SPAWN > 0
+
/** bfs_spawn() implementation using posix_spawn(). */
static pid_t bfs_posix_spawn(struct bfs_resolver *res, const struct bfs_spawn *ctx, char **argv, char **envp) {
pid_t ret;
@@ -493,8 +509,24 @@ static pid_t bfs_posix_spawn(struct bfs_resolver *res, const struct bfs_spawn *c
return ret;
}
+
+/** Check if we can use posix_spawn(). */
+static bool bfs_use_posix_spawn(const struct bfs_resolver *res, const struct bfs_spawn *ctx) {
+ if (!(ctx->flags & BFS_SPAWN_USE_POSIX)) {
+ return false;
+ }
+
+#if !BFS_POSIX_SPAWNP_AFTER_FCHDIR
+ if (!res->done) {
+ return false;
+ }
#endif
+ return true;
+}
+
+#endif // _POSIX_SPAWN > 0
+
/** Actually exec() the new process. */
static noreturn void bfs_spawn_exec(struct bfs_resolver *res, const struct bfs_spawn *ctx, char **argv, char **envp, int pipefd[2]) {
xclose(pipefd[0]);
@@ -607,7 +639,7 @@ static pid_t bfs_fork_spawn(struct bfs_resolver *res, const struct bfs_spawn *ct
/** Call the right bfs_spawn() implementation. */
static pid_t bfs_spawn_impl(struct bfs_resolver *res, const struct bfs_spawn *ctx, char **argv, char **envp) {
#if _POSIX_SPAWN > 0
- if (ctx->flags & BFS_SPAWN_USE_POSIX) {
+ if (bfs_use_posix_spawn(res, ctx)) {
return bfs_posix_spawn(res, ctx, argv, envp);
}
#endif