From ccd42320c45658052d620804bb8e05e5bc327706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Wed, 6 Sep 2017 06:05:26 +0200 Subject: [PATCH] libroot: add posix_spawn(). --- headers/posix/spawn.h | 83 +++ src/system/libroot/posix/Jamfile | 1 + src/system/libroot/posix/spawn.cpp | 495 ++++++++++++++++++ src/tests/system/libroot/posix/Jamfile | 1 + .../system/libroot/posix/posix_spawn_test.cpp | 50 ++ 5 files changed, 630 insertions(+) create mode 100644 headers/posix/spawn.h create mode 100644 src/system/libroot/posix/spawn.cpp create mode 100644 src/tests/system/libroot/posix/posix_spawn_test.cpp diff --git a/headers/posix/spawn.h b/headers/posix/spawn.h new file mode 100644 index 0000000000..4726b53a0e --- /dev/null +++ b/headers/posix/spawn.h @@ -0,0 +1,83 @@ +/* + * Copyright 2017 Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the Haiku License. + */ +#ifndef _SPAWN_H_ +#define _SPAWN_H_ + + +#include +#include +#include +#include + + +/* + * Flags for spawn attributes. + */ +#define POSIX_SPAWN_RESETIDS 0x01 +#define POSIX_SPAWN_SETPGROUP 0x02 +#if 0 /* Unimplemented flags: */ +#define POSIX_SPAWN_SETSCHEDPARAM 0x04 +#define POSIX_SPAWN_SETSCHEDULER 0x08 +#endif /* 0 */ +#define POSIX_SPAWN_SETSIGDEF 0x10 +#define POSIX_SPAWN_SETSIGMASK 0x20 + + +typedef struct _posix_spawnattr *posix_spawnattr_t; +typedef struct _posix_spawn_file_actions *posix_spawn_file_actions_t; + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern int posix_spawn(pid_t *pid, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]); +extern int posix_spawnp(pid_t *pid, const char *file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, char *const argv[], + char *const envp[]); + +/* file actions functions */ +extern int posix_spawn_file_actions_init( + posix_spawn_file_actions_t *file_actions); +extern int posix_spawn_file_actions_destroy( + posix_spawn_file_actions_t *file_actions); +extern int posix_spawn_file_actions_addopen( + posix_spawn_file_actions_t *file_actions, + int fildes, const char *path, int oflag, mode_t mode); +extern int posix_spawn_file_actions_addclose( + posix_spawn_file_actions_t *file_actions, int fildes); +extern int posix_spawn_file_actions_adddup2( + posix_spawn_file_actions_t *file_actions, int fildes, int newfildes); + +/* spawn attribute functions */ +extern int posix_spawnattr_destroy(posix_spawnattr_t *attr); +extern int posix_spawnattr_init(posix_spawnattr_t *attr); +extern int posix_spawnattr_getflags(const posix_spawnattr_t *attr, + short *_flags); +extern int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags); +extern int posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, + pid_t *_pgroup); +extern int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup); +extern int posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr, + sigset_t *sigdefault); +extern int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr, + const sigset_t *sigdefault); +extern int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr, + sigset_t *_sigmask); +extern int posix_spawnattr_setsigmask(posix_spawnattr_t *attr, + const sigset_t *sigmask); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _SPAWN_H_ */ + diff --git a/src/system/libroot/posix/Jamfile b/src/system/libroot/posix/Jamfile index ac6f34c3d3..2079067016 100644 --- a/src/system/libroot/posix/Jamfile +++ b/src/system/libroot/posix/Jamfile @@ -34,6 +34,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { $(PWD_BACKEND) scheduler.cpp semaphore.cpp + spawn.cpp syslog.cpp termios.c utime.c diff --git a/src/system/libroot/posix/spawn.cpp b/src/system/libroot/posix/spawn.cpp new file mode 100644 index 0000000000..171e531f84 --- /dev/null +++ b/src/system/libroot/posix/spawn.cpp @@ -0,0 +1,495 @@ +/* + * Copyright 2017, Jérôme Duval, jerome.Duval@gmail.com + * Distributed under the terms of the MIT license. + */ + + +#include + +#include +#include +#include +#include +#include +#include + +#include + + +enum action_type { + file_action_open, + file_action_close, + file_action_dup2 +}; + +struct _file_action { + enum action_type type; + int fd; + union { + struct { + char* path; + int oflag; + mode_t mode; + } open_action; + struct { + int srcfd; + } dup2_action; + } action; +}; + +struct _posix_spawnattr { + short flags; + pid_t pgroup; + sigset_t sigdefault; + sigset_t sigmask; +}; + +struct _posix_spawn_file_actions { + int size; + int count; + _file_action *actions; +}; + + +static int +posix_spawn_file_actions_extend(struct _posix_spawn_file_actions *actions) +{ + int newsize = actions->size + 4; + void *newactions = realloc(actions->actions, + newsize * sizeof(struct _file_action)); + if (newactions == NULL) + return ENOMEM; + actions->actions = (struct _file_action*)newactions; + actions->size = newsize; + return 0; +} + + +int +posix_spawn_file_actions_init(posix_spawn_file_actions_t *_file_actions) +{ + posix_spawn_file_actions_t actions = (posix_spawn_file_actions_t)malloc( + sizeof(struct _posix_spawn_file_actions)); + + if (actions == NULL) + return ENOMEM; + + memset(actions, 0, sizeof(*actions)); + *_file_actions = actions; + + return 0; +} + + +int +posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *_actions) +{ + struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL; + + if (actions == NULL) + return EINVAL; + + free(actions); + + return 0; +} + + +int +posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *_actions, + int fildes, const char *path, int oflag, mode_t mode) +{ + struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL; + + if (actions == NULL) + return EINVAL; + + if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX)) + return EBADF; + + char* npath = strdup(path); + if (npath == NULL) + return ENOMEM; + if (actions->count == actions->size + && posix_spawn_file_actions_extend(actions) != 0) { + free(npath); + return ENOMEM; + } + + struct _file_action *action = &actions->actions[actions->count]; + action->type = file_action_open; + action->fd = fildes; + action->action.open_action.path = npath; + action->action.open_action.oflag = oflag; + action->action.open_action.mode = mode; + actions->count++; + return 0; +} + + +int +posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *_actions, + int fildes) +{ + struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL; + + if (actions == NULL) + return EINVAL; + + if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX)) + return EBADF; + + if (actions->count == actions->size + && posix_spawn_file_actions_extend(actions) != 0) { + return ENOMEM; + } + + struct _file_action *action = &actions->actions[actions->count]; + action->type = file_action_close; + action->fd = fildes; + actions->count++; + return 0; +} + + +int +posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *_actions, + int fildes, int newfildes) +{ + struct _posix_spawn_file_actions* actions = _actions != NULL ? *_actions : NULL; + + if (actions == NULL) + return EINVAL; + + if (fildes < 0 || fildes >= sysconf(_SC_OPEN_MAX)) + return EBADF; + + if (actions->count == actions->size + && posix_spawn_file_actions_extend(actions) != 0) { + return ENOMEM; + } + + struct _file_action *action = &actions->actions[actions->count]; + action->type = file_action_dup2; + action->fd = newfildes; + action->action.dup2_action.srcfd = fildes; + actions->count++; + return 0; +} + + +int +posix_spawnattr_init(posix_spawnattr_t *_attr) +{ + posix_spawnattr_t attr = (posix_spawnattr_t)malloc( + sizeof(struct _posix_spawnattr)); + + if (attr == NULL) + return ENOMEM; + + memset(attr, 0, sizeof(*attr)); + *_attr = attr; + + return 0; +} + + +int +posix_spawnattr_destroy(posix_spawnattr_t *_attr) +{ + struct _posix_spawnattr* attr = _attr != NULL ? *_attr : NULL; + + if (attr == NULL) + return EINVAL; + + free(attr); + + return 0; +} + + +int +posix_spawnattr_getflags(const posix_spawnattr_t *_attr, short *flags) +{ + struct _posix_spawnattr *attr; + + if (_attr == NULL || (attr = *_attr) == NULL || flags == NULL) + return EINVAL; + + *flags = attr->flags; + + return 0; +} + + +int +posix_spawnattr_setflags(posix_spawnattr_t *_attr, short flags) +{ + struct _posix_spawnattr *attr; + + if (_attr == NULL || (attr = *_attr) == NULL) + return EINVAL; + + if ((flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP + | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK)) != 0) { + return EINVAL; + } + + attr->flags = flags; + + return 0; +} + + +int +posix_spawnattr_getpgroup(const posix_spawnattr_t *_attr, pid_t *pgroup) +{ + struct _posix_spawnattr *attr; + + if (_attr == NULL || (attr = *_attr) == NULL || pgroup == NULL) + return EINVAL; + + *pgroup = attr->pgroup; + + return 0; +} + + +int +posix_spawnattr_setpgroup(posix_spawnattr_t *_attr, pid_t pgroup) +{ + struct _posix_spawnattr *attr; + + if (_attr == NULL || (attr = *_attr) == NULL) + return EINVAL; + + attr->pgroup = pgroup; + + return 0; +} + + +int +posix_spawnattr_getsigdefault(const posix_spawnattr_t *_attr, sigset_t *sigdefault) +{ + struct _posix_spawnattr *attr; + + if (_attr == NULL || (attr = *_attr) == NULL || sigdefault == NULL) + return EINVAL; + + memcpy(sigdefault, &attr->sigdefault, sizeof(sigset_t)); + + return 0; +} + + +int +posix_spawnattr_setsigdefault(posix_spawnattr_t *_attr, + const sigset_t *sigdefault) +{ + struct _posix_spawnattr *attr; + + if (_attr == NULL || (attr = *_attr) == NULL || sigdefault == NULL) + return EINVAL; + + memcpy(&attr->sigdefault, sigdefault, sizeof(sigset_t)); + + return 0; +} + + +int +posix_spawnattr_getsigmask(const posix_spawnattr_t *_attr, sigset_t *sigmask) +{ + struct _posix_spawnattr *attr; + + if (_attr == NULL || (attr = *_attr) == NULL || sigmask == NULL) + return EINVAL; + + memcpy(sigmask, &attr->sigmask, sizeof(sigset_t)); + + return 0; +} + + +int +posix_spawnattr_setsigmask(posix_spawnattr_t *_attr, const sigset_t *sigmask) +{ + struct _posix_spawnattr *attr; + + if (_attr == NULL || (attr = *_attr) == NULL || sigmask == NULL) + return EINVAL; + + memcpy(&attr->sigmask, sigmask, sizeof(sigset_t)); + + return 0; +} + + +static int +process_spawnattr(const posix_spawnattr_t *_attr) +{ + if (_attr == NULL) + return 0; + + struct _posix_spawnattr *attr = *_attr; + if (attr == NULL) + return EINVAL; + + if ((attr->flags & POSIX_SPAWN_SETSIGMASK) != 0) + sigprocmask(SIG_SETMASK, &attr->sigmask, NULL); + + if ((attr->flags & POSIX_SPAWN_SETSIGDEF) != 0) { + struct sigaction action; + action.sa_handler = SIG_DFL; + action.sa_flags = 0; + action.sa_userdata = NULL; + sigemptyset(&action.sa_mask); + for (int i = 1; i <= NSIG; i++) { + if (sigismember(&attr->sigdefault, i) == 1 + && sigaction(i, &action, NULL) != 0) { + return errno; + } + } + } + + if ((attr->flags & POSIX_SPAWN_RESETIDS) != 0) { + if (setegid(getgid()) != 0) + return errno; + if (seteuid(getuid()) != 0) + return errno; + } + + if ((attr->flags & POSIX_SPAWN_SETPGROUP) != 0) { + if (setpgid(0, attr->pgroup) != 0) + return errno; + } + + return 0; +} + + +static int +process_file_actions(const posix_spawn_file_actions_t *_actions, int *errfd) +{ + if (_actions == NULL) + return 0; + + struct _posix_spawn_file_actions* actions = *_actions; + if (actions == NULL) + return EINVAL; + + for (int i = 0; i < actions->count; i++) { + struct _file_action *action = &actions->actions[i]; + + if (action->fd == *errfd) { + int newfd = dup(action->fd); + if (newfd == -1) + return errno; + close(action->fd); + *errfd = newfd; + } + + if (action->type == file_action_close) { + if (close(action->fd) != 0) + return errno; + } else if (action->type == file_action_open) { + int fd = open(action->action.open_action.path, + action->action.open_action.oflag, + action->action.open_action.mode); + if (fd == -1) + return errno; + if (fd != action->fd) { + if (dup2(action->fd, fd) != 0) + return errno; + if (close(fd) != 0) + return errno; + } + } else if (action->type == file_action_dup2) { + if (dup2(action->action.dup2_action.srcfd, action->fd) != 0) + return errno; + } + } + + return 0; +} + + +static int +do_posix_spawn(pid_t *_pid, const char *path, + const posix_spawn_file_actions_t *actions, + const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], + bool envpath) +{ + int err = 0; + int fds[2]; + pid_t pid; + + if (pipe(fds) != 0) + return errno; + if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) != 0 + || fcntl(fds[1], F_SETFD, FD_CLOEXEC) != 0) { + goto fail; + } + pid = vfork(); + if (pid == -1) + goto fail; + + if (pid != 0) { + // parent + if (_pid != NULL) + *_pid = pid; + close(fds[1]); + read(fds[0], &err, sizeof(err)); + if (err != 0) + waitpid(pid, NULL, WNOHANG); + return err; + } + + // child + close(fds[0]); + + err = process_spawnattr(attrp); + if (err == 0) + err = process_file_actions(actions, &fds[1]); + if (err != 0) + goto fail_child; + + if (envpath) + execvpe(path, argv, envp != NULL ? envp : environ); + else + execve(path, argv, envp != NULL ? envp : environ); + + err = errno; + +fail_child: + write(fds[1], &err, sizeof(err)); + close(fds[1]); + _exit(127); + +fail: + err = errno; + close(fds[0]); + close(fds[1]); + return err; +} + + +int +posix_spawn(pid_t *pid, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) +{ + return do_posix_spawn(pid, path, file_actions, attrp, argv, envp, false); +} + + +int +posix_spawnp(pid_t *pid, const char *file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, char *const argv[], + char *const envp[]) +{ + return do_posix_spawn(pid, file, file_actions, attrp, argv, envp, true); +} + diff --git a/src/tests/system/libroot/posix/Jamfile b/src/tests/system/libroot/posix/Jamfile index fbd2c4f113..3736add4df 100644 --- a/src/tests/system/libroot/posix/Jamfile +++ b/src/tests/system/libroot/posix/Jamfile @@ -38,6 +38,7 @@ SimpleTest truncate : truncate.cpp ; SimpleTest init_rld_after_fork_test : init_rld_after_fork_test.cpp ; SimpleTest user_thread_fork_test : user_thread_fork_test.cpp ; SimpleTest pthread_barrier_test : pthread_barrier_test.cpp ; +SimpleTest posix_spawn_test : posix_spawn_test.cpp ; # XSI tests SimpleTest xsi_msg_queue_test1 : xsi_msg_queue_test1.cpp ; diff --git a/src/tests/system/libroot/posix/posix_spawn_test.cpp b/src/tests/system/libroot/posix/posix_spawn_test.cpp new file mode 100644 index 0000000000..957a4ab95f --- /dev/null +++ b/src/tests/system/libroot/posix/posix_spawn_test.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include + + +int main() +{ + + char* _args[4]; + char* _env[] = { "myenv=5", NULL }; + + _args[0] = "bash"; + _args[1] = "-c"; + _args[2] = "exit $myenv"; + _args[3] = NULL; + + pid_t pid; + int err = posix_spawnp(&pid, _args[0], NULL, NULL, _args, _env); + printf("posix_spawnp: %d, %d\n", err, pid); + + int status; + pid_t waitpid_res = waitpid(pid, &status, 0); + if (waitpid_res != pid) + printf("posix_spawnp: waitpid didn't return pid\n"); + + printf("posix_spawnp: WIFEXITED(): %d, WEXITSTATUS() %d => 5\n", + WIFEXITED(status), WEXITSTATUS(status)); + + _args[0] = "/tmp/toto"; + _args[1] = NULL; + + err = posix_spawn(&pid, _args[0], NULL, NULL, _args, _env); + printf("posix_spawn: %d, %d\n", err, pid); + + if (err == 0) { + waitpid_res = waitpid(pid, &status, 0); + if (waitpid_res != pid) + printf("posix_spawn: waitpid didn't return pid\n"); + printf("posix_spawn: WIFEXITED(): %d, WEXITSTATUS() %d => 127\n", + WIFEXITED(status), WEXITSTATUS(status)); + } else { + waitpid_res = waitpid(-1, NULL, 0); + printf("posix_spawn: waitpid %d, %d\n", waitpid_res, errno); + } + + return 0; +} +