diff options
Diffstat (limited to 'lib/mlibc/options/posix/generic/spawn-stubs.cpp')
-rw-r--r-- | lib/mlibc/options/posix/generic/spawn-stubs.cpp | 376 |
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); +} + |