aboutsummaryrefslogtreecommitdiff
path: root/lib/mlibc/options/posix/generic/spawn-stubs.cpp
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-03-07 17:28:00 -0500
committerIan Moffett <ian@osmora.org>2024-03-07 17:28:32 -0500
commitbd5969fc876a10b18613302db7087ef3c40f18e1 (patch)
tree7c2b8619afe902abf99570df2873fbdf40a4d1a1 /lib/mlibc/options/posix/generic/spawn-stubs.cpp
parenta95b38b1b92b172e6cc4e8e56a88a30cc65907b0 (diff)
lib: Add mlibc
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'lib/mlibc/options/posix/generic/spawn-stubs.cpp')
-rw-r--r--lib/mlibc/options/posix/generic/spawn-stubs.cpp376
1 files changed, 376 insertions, 0 deletions
diff --git a/lib/mlibc/options/posix/generic/spawn-stubs.cpp b/lib/mlibc/options/posix/generic/spawn-stubs.cpp
new file mode 100644
index 0000000..cf7edfc
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/spawn-stubs.cpp
@@ -0,0 +1,376 @@
+
+#include <spawn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+
+/*
+ * Musl places this in a seperate header called fdop.h
+ * This header isn't present in glibc, or on my host, so I
+ * include it's contents here
+ */
+
+#define FDOP_CLOSE 1
+#define FDOP_DUP2 2
+#define FDOP_OPEN 3
+#define FDOP_CHDIR 4
+#define FDOP_FCHDIR 5
+
+struct fdop {
+ struct fdop *next, *prev;
+ int cmd, fd, srcfd, oflag;
+ mode_t mode;
+ char path[];
+};
+
+/*
+ * This posix_spawn implementation is taken from musl
+ */
+
+static unsigned long handler_set[NSIG / (8 * sizeof(long))];
+
+static void __get_handler_set(sigset_t *set) {
+ memcpy(set, handler_set, sizeof handler_set);
+}
+
+struct args {
+ int p[2];
+ sigset_t oldmask;
+ const char *path;
+ const posix_spawn_file_actions_t *fa;
+ const posix_spawnattr_t *__restrict attr;
+ char *const *argv, *const *envp;
+};
+
+static int child(void *args_vp) {
+ int i, ret;
+ struct sigaction sa = {};
+ struct args *args = (struct args *)args_vp;
+ int p = args->p[1];
+ const posix_spawn_file_actions_t *fa = args->fa;
+ const posix_spawnattr_t *__restrict attr = args->attr;
+ sigset_t hset;
+ bool use_execvpe = false;
+
+ if(attr->__fn)
+ use_execvpe = true;
+
+ close(args->p[0]);
+
+ /* All signal dispositions must be either SIG_DFL or SIG_IGN
+ * before signals are unblocked. Otherwise a signal handler
+ * from the parent might get run in the child while sharing
+ * memory, with unpredictable and dangerous results. To
+ * reduce overhead, sigaction has tracked for us which signals
+ * potentially have a signal handler. */
+ __get_handler_set(&hset);
+ for(i = 1; i < NSIG; i++) {
+ if((attr->__flags & POSIX_SPAWN_SETSIGDEF) && sigismember(&attr->__def, i)) {
+ sa.sa_handler = SIG_DFL;
+ } else if(sigismember(&hset, i)) {
+ if (i - 32 < 3) {
+ sa.sa_handler = SIG_IGN;
+ } else {;
+ sigaction(i, 0, &sa);
+ if(sa.sa_handler == SIG_IGN)
+ continue;
+ sa.sa_handler = SIG_DFL;
+ }
+ } else {
+ continue;
+ }
+ sigaction(i, &sa, 0);
+ }
+
+ if(attr->__flags & POSIX_SPAWN_SETSID) {
+ if((ret = setsid()) < 0)
+ goto fail;
+ }
+
+ if(attr->__flags & POSIX_SPAWN_SETPGROUP) {
+ mlibc::infoLogger() << "mlibc: posix_spawn: ignoring SETPGROUP" << frg::endlog;
+ //if((ret = setpgid(0, attr->__pgrp)))
+ // goto fail;
+ }
+
+ if(attr->__flags & POSIX_SPAWN_RESETIDS) {
+ if((ret = setgid(getgid())) || (ret = setuid(getuid())) )
+ goto fail;
+ }
+
+ if(fa && fa->__actions) {
+ struct fdop *op;
+ int fd;
+ for(op = (struct fdop *)fa->__actions; op->next; op = op->next);
+ for(; op; op = op->prev) {
+ /* It's possible that a file operation would clobber
+ * the pipe fd used for synchronizing with the
+ * parent. To avoid that, we dup the pipe onto
+ * an unoccupied fd. */
+ if(op->fd == p) {
+ ret = dup(p);
+ if(ret < 0)
+ goto fail;
+ close(p);
+ p = ret;
+ }
+ switch(op->cmd) {
+ case FDOP_CLOSE:
+ close(op->fd);
+ break;
+ case FDOP_DUP2:
+ fd = op->srcfd;
+ if(fd == p) {
+ ret = -EBADF;
+ goto fail;
+ }
+ if(fd != op->fd) {
+ if((ret = dup2(fd, op->fd)) < 0)
+ goto fail;
+ } else {
+ ret = fcntl(fd, F_GETFD);
+ ret = fcntl(fd, F_SETFD, ret & ~FD_CLOEXEC);
+ if(ret < 0)
+ goto fail;
+ }
+ break;
+ case FDOP_OPEN:
+ fd = open(op->path, op->oflag, op->mode);
+ if((ret = fd) < 0)
+ goto fail;
+ if(fd != op->fd) {
+ if((ret = dup2(fd, op->fd)) < 0)
+ goto fail;
+ close(fd);
+ }
+ break;
+ case FDOP_CHDIR:
+ ret = chdir(op->path);
+ if(ret < 0)
+ goto fail;
+ break;
+ case FDOP_FCHDIR:
+ ret = fchdir(op->fd);
+ if(ret < 0)
+ goto fail;
+ break;
+ }
+ }
+ }
+
+ /* Close-on-exec flag may have been lost if we moved the pipe
+ * to a different fd. */
+ fcntl(p, F_SETFD, FD_CLOEXEC);
+
+ pthread_sigmask(SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK)
+ ? &attr->__mask : &args->oldmask, 0);
+
+ if(use_execvpe)
+ execvpe(args->path, args->argv, args->envp);
+ else
+ execve(args->path, args->argv, args->envp);
+ ret = -errno;
+
+fail:
+ /* Since sizeof errno < PIPE_BUF, the write is atomic. */
+ ret = -ret;
+ if(ret)
+ while(write(p, &ret, sizeof ret) < 0);
+ _exit(127);
+}
+
+int posix_spawn(pid_t *__restrict res, const char *__restrict path,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *__restrict attrs,
+ char *const argv[], char *const envp[]) {
+ pid_t pid;
+ int ec = 0, cs;
+ struct args args;
+ const posix_spawnattr_t empty_attr = {};
+ sigset_t full_sigset;
+ sigfillset(&full_sigset);
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+ args.path = path;
+ args.fa = file_actions;
+ args.attr = attrs ? attrs : &empty_attr;
+ args.argv = argv;
+ args.envp = envp;
+ pthread_sigmask(SIG_BLOCK, &full_sigset, &args.oldmask);
+
+ /* The lock guards both against seeing a SIGABRT disposition change
+ * by abort and against leaking the pipe fd to fork-without-exec. */
+ //LOCK(__abort_lock);
+
+ if(pipe2(args.p, O_CLOEXEC)) {
+ //UNLOCK(__abort_lock);
+ ec = errno;
+ goto fail;
+ }
+
+ /* Mlibc change: We use fork + execve, as clone is not implemented.
+ * This yields the same result in the end. */
+ //pid = clone(child, stack + sizeof stack, CLONE_VM | CLONE_VFORK | SIGCHLD, &args);
+ pid = fork();
+ if(!pid) {
+ child(&args);
+ }
+ close(args.p[1]);
+ //UNLOCK(__abort_lock);
+
+ if(pid > 0) {
+ if(read(args.p[0], &ec, sizeof ec) != sizeof ec)
+ ec = 0;
+ else
+ waitpid(pid, 0, 0);
+ } else {
+ ec = -pid;
+ }
+
+ close(args.p[0]);
+
+ if(!ec && res)
+ *res = pid;
+
+fail:
+ pthread_sigmask(SIG_SETMASK, &args.oldmask, 0);
+ pthread_setcancelstate(cs, 0);
+
+ return ec;
+}
+
+int posix_spawnattr_init(posix_spawnattr_t *attr) {
+ *attr = (posix_spawnattr_t){};
+ return 0;
+}
+
+int posix_spawnattr_destroy(posix_spawnattr_t *) {
+ return 0;
+}
+
+int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
+ const unsigned all_flags =
+ POSIX_SPAWN_RESETIDS |
+ POSIX_SPAWN_SETPGROUP |
+ POSIX_SPAWN_SETSIGDEF |
+ POSIX_SPAWN_SETSIGMASK |
+ POSIX_SPAWN_SETSCHEDPARAM |
+ POSIX_SPAWN_SETSCHEDULER |
+ POSIX_SPAWN_USEVFORK |
+ POSIX_SPAWN_SETSID;
+ if(flags & ~all_flags)
+ return EINVAL;
+ attr->__flags = flags;
+ return 0;
+}
+
+int posix_spawnattr_setsigdefault(posix_spawnattr_t *__restrict attr,
+ const sigset_t *__restrict sigdefault) {
+ attr->__def = *sigdefault;
+ return 0;
+}
+
+int posix_spawnattr_setschedparam(posix_spawnattr_t *__restrict,
+ const struct sched_param *__restrict) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int posix_spawnattr_setschedpolicy(posix_spawnattr_t *, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int posix_spawnattr_setsigmask(posix_spawnattr_t *__restrict attr,
+ const sigset_t *__restrict sigmask) {
+ attr->__mask = *sigmask;
+ return 0;
+}
+
+int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup) {
+ attr->__pgrp = pgroup;
+ return 0;
+}
+
+int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions) {
+ file_actions->__actions = 0;
+ return 0;
+}
+
+int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions) {
+ struct fdop *op = (struct fdop *)file_actions->__actions, *next;
+ while(op) {
+ next = op->next;
+ free(op);
+ op = next;
+ }
+ return 0;
+}
+
+int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions,
+ int fildes, int newfildes) {
+ struct fdop *op = (struct fdop *)malloc(sizeof *op);
+ if(!op)
+ return ENOMEM;
+ op->cmd = FDOP_DUP2;
+ op->srcfd = fildes;
+ op->fd = newfildes;
+ if((op->next = (struct fdop *)file_actions->__actions))
+ op->next->prev = op;
+ op->prev = 0;
+ file_actions->__actions = op;
+ return 0;
+}
+
+int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions,
+ int fildes) {
+ struct fdop *op = (struct fdop *)malloc(sizeof *op);
+ if(!op)
+ return ENOMEM;
+ op->cmd = FDOP_CLOSE;
+ op->fd = fildes;
+ if((op->next = (struct fdop *)file_actions->__actions))
+ op->next->prev = op;
+ op->prev = 0;
+ file_actions->__actions = op;
+ return 0;
+}
+
+int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *__restrict file_actions,
+ int fildes, const char *__restrict path, int oflag, mode_t mode) {
+ struct fdop *op = (struct fdop *)malloc(sizeof *op + strlen(path) + 1);
+ if(!op)
+ return ENOMEM;
+ op->cmd = FDOP_OPEN;
+ op->fd = fildes;
+ op->oflag = oflag;
+ op->mode = mode;
+ strcpy(op->path, path);
+ if((op->next = (struct fdop *)file_actions->__actions))
+ op->next->prev = op;
+ op->prev = 0;
+ file_actions->__actions = op;
+ return 0;
+}
+
+int posix_spawnp(pid_t *__restrict pid, const char *__restrict file,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *__restrict attrp,
+ char *const argv[], char *const envp[]) {
+ posix_spawnattr_t spawnp_attr = {};
+ if(attrp)
+ spawnp_attr = *attrp;
+ spawnp_attr.__fn = (void *)execvpe;
+ return posix_spawn(pid, file, file_actions, &spawnp_attr, argv, envp);
+}
+