Merge pull request #201 from Dennisbonke/posix_spawn
options/posix: Implement posix_spawn
This commit is contained in:
commit
4ae723492f
|
@ -17,21 +17,24 @@ int sigfillset(sigset_t *sigset) {
|
|||
|
||||
// TODO: Return EINVAL instead of __ensure()ing.
|
||||
|
||||
int sigaddset(sigset_t *sigset, int signo) {
|
||||
int sigaddset(sigset_t *sigset, int sig) {
|
||||
int signo = sig - 1;
|
||||
// TODO: do not hard code CHAR_BITS
|
||||
__ensure((unsigned int)signo < sizeof(sigset_t) * 8);
|
||||
*sigset |= sigset_t(1) << signo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sigdelset(sigset_t *sigset, int signo) {
|
||||
int sigdelset(sigset_t *sigset, int sig) {
|
||||
int signo = sig - 1;
|
||||
// TODO: do not hard code CHAR_BITS
|
||||
__ensure((unsigned int)signo < sizeof(sigset_t) * 8);
|
||||
*sigset &= ~(sigset_t(1) << signo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sigismember(const sigset_t *set, int signo) {
|
||||
int sigismember(const sigset_t *set, int sig) {
|
||||
int signo = sig - 1;
|
||||
// TODO: do not hard code CHAR_BITS
|
||||
__ensure((unsigned int)signo < sizeof(sigset_t) * 8);
|
||||
return (*set) & (sigset_t(1) << signo);
|
||||
|
|
|
@ -1,14 +1,251 @@
|
|||
|
||||
#include <spawn.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <bits/ensure.h>
|
||||
#include <mlibc/debug.hpp>
|
||||
|
||||
int posix_spawn(pid_t *__restrict pid, const char *__restrict path,
|
||||
/*
|
||||
* 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 = {0};
|
||||
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[]) {
|
||||
__ensure(!"Not implemented");
|
||||
__builtin_unreachable();
|
||||
pid_t pid;
|
||||
int ec = 0, cs;
|
||||
struct args args;
|
||||
const posix_spawnattr_t empty_attr = {0};
|
||||
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) {
|
||||
|
|
|
@ -162,6 +162,11 @@ int execvp(const char *file, char *const argv[]) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
int execvpe(const char *path, char *const argv[], char *const envp[]) {
|
||||
__ensure(!"Not implemented");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
int faccessat(int dirfd, const char *pathname, int mode, int flags) {
|
||||
if(!mlibc::sys_faccessat) {
|
||||
MLIBC_MISSING_SYSDEP();
|
||||
|
|
|
@ -15,7 +15,7 @@ int sigemptyset(sigset_t *);
|
|||
int sigfillset(sigset_t *);
|
||||
int sigaddset(sigset_t *, int);
|
||||
int sigdelset(sigset_t *, int);
|
||||
int sigismember(const sigset_t *set, int signo);
|
||||
int sigismember(const sigset_t *set, int sig);
|
||||
|
||||
// functions to block / wait for signals
|
||||
int sigsuspend(const sigset_t *);
|
||||
|
|
|
@ -11,8 +11,20 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct { } posix_spawnattr_t;
|
||||
typedef struct { } posix_spawn_file_actions_t;
|
||||
typedef struct {
|
||||
int __flags;
|
||||
pid_t __pgrp;
|
||||
sigset_t __def, __mask;
|
||||
int __prio, __pol;
|
||||
void *__fn;
|
||||
char __pad[64 - sizeof(void *)];
|
||||
} posix_spawnattr_t;
|
||||
|
||||
typedef struct {
|
||||
int __pad0[2];
|
||||
void *__actions;
|
||||
int __pad[16];
|
||||
} posix_spawn_file_actions_t;
|
||||
|
||||
// MISSIG: sigset_t
|
||||
|
||||
|
|
|
@ -132,6 +132,7 @@ int execlp(const char *, const char *, ...);
|
|||
int execv(const char *, char *const []);
|
||||
int execve(const char *path, char *const argv[], char *const envp[]);
|
||||
int execvp(const char *, char *const[]);
|
||||
int execvpe(const char *path, char *const argv[], char *const envp[]);
|
||||
int faccessat(int, const char *, int, int);
|
||||
int fchdir(int fd);
|
||||
int fchown(int fd, uid_t uid, gid_t gid);
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
extern "C" {
|
||||
using sc_word_t = long;
|
||||
|
||||
static sc_word_t do_asm_syscall0(int sc) {
|
||||
sc_word_t ret;
|
||||
asm volatile ("syscall" : "=a"(ret)
|
||||
: "a"(sc)
|
||||
: "rcx", "r11", "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static sc_word_t do_asm_syscall1(int sc,
|
||||
sc_word_t arg1) {
|
||||
sc_word_t ret;
|
||||
|
@ -71,6 +79,9 @@ extern "C" {
|
|||
|
||||
namespace mlibc {
|
||||
// C++ wrappers for the extern "C" functions.
|
||||
inline sc_word_t do_nargs_syscall(int sc) {
|
||||
return do_asm_syscall0(sc);
|
||||
}
|
||||
inline sc_word_t do_nargs_syscall(int sc, sc_word_t arg1) {
|
||||
return do_asm_syscall1(sc, arg1);
|
||||
}
|
||||
|
|
|
@ -18,19 +18,25 @@
|
|||
#define NR_lseek 8
|
||||
#define NR_mmap 9
|
||||
#define NR_sigaction 13
|
||||
#define NR_rt_sigprocmask 14
|
||||
#define NR_ioctl 16
|
||||
#define NR_pipe 22
|
||||
#define NR_select 23
|
||||
#define NR_socket 41
|
||||
#define NR_connect 42
|
||||
#define NR_sendmsg 46
|
||||
#define NR_recvmsg 47
|
||||
#define NR_fork 57
|
||||
#define NR_execve 59
|
||||
#define NR_exit 60
|
||||
#define NR_wait4 61
|
||||
#define NR_fcntl 72
|
||||
#define NR_unlink 87
|
||||
#define NR_arch_prctl 158
|
||||
#define NR_sys_futex 202
|
||||
#define NR_clock_gettime 228
|
||||
#define NR_pselect6 270
|
||||
#define NR_pipe2 293
|
||||
|
||||
#define ARCH_SET_FS 0x1002
|
||||
|
||||
|
@ -231,6 +237,50 @@ int sys_pselect(int nfds, fd_set *readfds, fd_set *writefds,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sys_pipe(int *fds, int flags) {
|
||||
if(flags) {
|
||||
auto ret = do_syscall(NR_pipe2, fds, flags);
|
||||
if (int e = sc_error(ret); e)
|
||||
return e;
|
||||
return 0;
|
||||
} else {
|
||||
auto ret = do_syscall(NR_pipe, fds);
|
||||
if (int e = sc_error(ret); e)
|
||||
return e;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int sys_fork(pid_t *child) {
|
||||
auto ret = do_syscall(NR_fork);
|
||||
if (int e = sc_error(ret); e)
|
||||
return e;
|
||||
*child = sc_int_result<int>(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_waitpid(pid_t pid, int *status, int flags, pid_t *ret_pid) {
|
||||
auto ret = do_syscall(NR_wait4, pid, status, flags, 0);
|
||||
if (int e = sc_error(ret); e)
|
||||
return e;
|
||||
*ret_pid = sc_int_result<int>(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_execve(const char *path, char *const argv[], char *const envp[]) {
|
||||
auto ret = do_syscall(NR_execve, path, argv, envp);
|
||||
if (int e = sc_error(ret); e)
|
||||
return e;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_sigprocmask(int how, const sigset_t *set, sigset_t *old) {
|
||||
auto ret = do_syscall(NR_rt_sigprocmask, how, set, old, NSIG / 8);
|
||||
if (int e = sc_error(ret); e)
|
||||
return e;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // __MLIBC_POSIX_OPTION
|
||||
|
||||
void sys_exit(int status) {
|
||||
|
|
|
@ -10,7 +10,8 @@ posix_test_cases = [
|
|||
'inet_pton',
|
||||
'pthread_rwlock',
|
||||
'getaddrinfo',
|
||||
'dprintf'
|
||||
'dprintf',
|
||||
'posix_spawn'
|
||||
]
|
||||
|
||||
glibc_test_cases = [
|
||||
|
|
39
tests/posix/posix_spawn.c
Normal file
39
tests/posix/posix_spawn.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
extern char **environ;
|
||||
|
||||
void run_cmd(char *cmd)
|
||||
{
|
||||
pid_t pid;
|
||||
char *argv[] = {"sh", "-c", cmd, NULL};
|
||||
int status;
|
||||
printf("Run command: %s\n", cmd);
|
||||
status = posix_spawn(&pid, "/bin/sh", NULL, NULL, argv, environ);
|
||||
if(status == 0) {
|
||||
printf("Child pid: %i\n", pid);
|
||||
if(waitpid(pid, &status, 0) != -1) {
|
||||
printf("Child exited with status %i\n", status);
|
||||
printf("Child exit status: %i\n", WEXITSTATUS(status));
|
||||
assert(WEXITSTATUS(status) == 0);
|
||||
} else {
|
||||
perror("waitpid");
|
||||
assert(0 == 1);
|
||||
}
|
||||
} else {
|
||||
printf("posix_spawn: %s\n", strerror(status));
|
||||
assert(0 == 1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
run_cmd("/usr/bin/true");
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue