diff --git a/userspace/libraries/LibC/include/spawn.h b/userspace/libraries/LibC/include/spawn.h index c1f022d1..eba43645 100644 --- a/userspace/libraries/LibC/include/spawn.h +++ b/userspace/libraries/LibC/include/spawn.h @@ -14,8 +14,58 @@ __BEGIN_DECLS #define __need_pid_t #include -typedef int posix_spawnattr_t; -typedef int posix_spawn_file_actions_t; +typedef struct +{ + short flags; + pid_t pgroup; + sched_param schedparam; + int schedpolicy; + sigset_t sigdefault; + sigset_t sigmask; +} posix_spawnattr_t; + +typedef struct +{ + int fildes; +} _posix_spawn_file_actions_close_t; + +typedef struct +{ + int fildes; + int newfildes; +} _posix_spawn_file_actions_dup2_t; + +typedef struct +{ + int fildes; + char* path; + int oflag; + mode_t mode; +} _posix_spawn_file_actions_open_t; + +typedef enum +{ + _POSIX_SPAWN_FILE_ACTION_CLOSE, + _POSIX_SPAWN_FILE_ACTION_DUP2, + _POSIX_SPAWN_FILE_ACTION_OPEN, +} _posix_spawn_file_action_type_e; + +typedef struct +{ + _posix_spawn_file_action_type_e type; + union + { + _posix_spawn_file_actions_close_t close; + _posix_spawn_file_actions_dup2_t dup2; + _posix_spawn_file_actions_open_t open; + }; +} _posix_spawn_file_action_t; + +typedef struct +{ + _posix_spawn_file_action_t* actions; + size_t action_count; +} posix_spawn_file_actions_t; #define POSIX_SPAWN_RESETIDS 0x01 #define POSIX_SPAWN_SETPGROUP 0x02 diff --git a/userspace/libraries/LibC/include/unistd.h b/userspace/libraries/LibC/include/unistd.h index 84961ba1..17c98999 100644 --- a/userspace/libraries/LibC/include/unistd.h +++ b/userspace/libraries/LibC/include/unistd.h @@ -41,7 +41,7 @@ __BEGIN_DECLS #define _POSIX_SEMAPHORES -1 /* sem_{close,destroy,getvalue,init,open,post,trywait,unlink,wait} */ #define _POSIX_SHARED_MEMORY_OBJECTS -1 /* shm_{open,unlink} */ #define _POSIX_SHELL 200809L -#define _POSIX_SPAWN -1 /* posix_spawn* */ +#define _POSIX_SPAWN 200809L #define _POSIX_SPIN_LOCKS 200809L #define _POSIX_SPORADIC_SERVER -1 /* sched_{setparam,setscheduler} with SCHED_SPORADIC */ #define _POSIX_SYNCHRONIZED_IO 200809L diff --git a/userspace/libraries/LibC/spawn.cpp b/userspace/libraries/LibC/spawn.cpp index 081b4753..fc30aff9 100644 --- a/userspace/libraries/LibC/spawn.cpp +++ b/userspace/libraries/LibC/spawn.cpp @@ -1,34 +1,316 @@ -#include +#include #include +#include #include #include +#include +#include +#include #include #define TODO_FUNC(name, ...) int name(__VA_ARGS__) { dwarnln("TODO: " #name); errno = ENOTSUP; return -1; } -static int do_posix_spawn(pid_t* __restrict pid, const char* __restrict path, const posix_spawn_file_actions_t* file_actions, const posix_spawnattr_t* __restrict attrp, char* const argv[], char* const envp[], bool do_path_resolution) +int posix_spawnattr_init(posix_spawnattr_t* attr) { - if (file_actions != nullptr) + attr->flags = 0; + return 0; +} + +int posix_spawnattr_destroy(posix_spawnattr_t* attr) +{ + (void)attr; + return 0; +} + +int posix_spawnattr_getflags(const posix_spawnattr_t* __restrict attr, short* __restrict flags) +{ + *flags = attr->flags; + return 0; +} + +int posix_spawnattr_setflags(posix_spawnattr_t* attr, short flags) +{ + if (flags & ~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK)) + return EINVAL; + attr->flags = flags; + return 0; +} + +int posix_spawnattr_getpgroup(const posix_spawnattr_t* __restrict attr, pid_t* __restrict pgroup) +{ + *pgroup = attr->pgroup; + return 0; +} + +int posix_spawnattr_setpgroup(posix_spawnattr_t* attr, pid_t pgroup) +{ + attr->pgroup = pgroup; + return 0; +} + +int posix_spawnattr_getschedparam(const posix_spawnattr_t* __restrict attr, struct sched_param* __restrict schedparam) +{ + *schedparam = attr->schedparam; + return 0; +} + +int posix_spawnattr_setschedparam(posix_spawnattr_t* __restrict attr, const struct sched_param* __restrict schedparam) +{ + attr->schedparam = *schedparam; + return 0; +} + +int posix_spawnattr_getschedpolicy(const posix_spawnattr_t* __restrict attr, int* __restrict schedpolicy) +{ + *schedpolicy = attr->schedpolicy; + return 0; +} + +int posix_spawnattr_setschedpolicy(posix_spawnattr_t* attr, int schedpolicy) +{ + switch (schedpolicy) { - dwarnln("TODO: posix_spawn with file actions"); - errno = ENOTSUP; - return -1; + case SCHED_FIFO: + case SCHED_RR: + case SCHED_SPORADIC: + case SCHED_OTHER: + break; + default: + return EINVAL; } - if (attrp != nullptr) + attr->schedpolicy = schedpolicy; + return 0; +} + +int posix_spawnattr_getsigdefault(const posix_spawnattr_t* __restrict attr, sigset_t* __restrict sigdefault) +{ + *sigdefault = attr->sigdefault; + return 0; +} + +int posix_spawnattr_setsigdefault(posix_spawnattr_t* __restrict attr, const sigset_t* __restrict sigdefault) +{ + attr->sigdefault = *sigdefault; + return 0; +} + +int posix_spawnattr_getsigmask(const posix_spawnattr_t* __restrict attr, sigset_t* __restrict sigmask) +{ + *sigmask = attr->sigmask; + return 0; +} + +int posix_spawnattr_setsigmask(posix_spawnattr_t* __restrict attr, const sigset_t* __restrict sigmask) +{ + attr->sigmask = *sigmask; + return 0; +} + +int posix_spawn_file_actions_init(posix_spawn_file_actions_t* file_actions) +{ + *file_actions = { + .actions = nullptr, + .action_count = 0, + }; + return 0; +} + +int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t* file_actions) +{ + for (size_t i = 0; i < file_actions->action_count; i++) { - dwarnln("TODO: posix_spawn with attributes"); - errno = ENOTSUP; - return -1; + switch (file_actions->actions[i].type) + { + case _POSIX_SPAWN_FILE_ACTION_CLOSE: + case _POSIX_SPAWN_FILE_ACTION_DUP2: + break; + case _POSIX_SPAWN_FILE_ACTION_OPEN: + free(file_actions->actions[i].open.path); + break; + } } + if (file_actions->actions != nullptr) + free(file_actions->actions); + + file_actions->actions = nullptr; + file_actions->action_count = 0; + + return 0; +} + +static int add_file_action(posix_spawn_file_actions_t* file_actions, _posix_spawn_file_action_t action) +{ + void* new_actions = realloc(file_actions->actions, sizeof(_posix_spawn_file_action_t) * (file_actions->action_count + 1)); + if (new_actions == nullptr) + return ENOMEM; + file_actions->actions = static_cast<_posix_spawn_file_action_t*>(new_actions); + file_actions->actions[file_actions->action_count++] = action; + return 0; +} + +int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t* file_actions, int fildes) +{ + if (fildes < 0 || fildes >= OPEN_MAX) + return EBADF; + + return add_file_action(file_actions, { + .type = _POSIX_SPAWN_FILE_ACTION_CLOSE, + .close = { + .fildes = fildes, + } + }); +} + +int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t* file_actions, int fildes, int newfildes) +{ + if (fildes < 0 || fildes >= OPEN_MAX || newfildes < 0 || newfildes >= OPEN_MAX) + return EBADF; + + return add_file_action(file_actions, { + .type = _POSIX_SPAWN_FILE_ACTION_DUP2, + .dup2 = { + .fildes = fildes, + .newfildes = newfildes, + } + }); +} + +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) +{ + if (fildes < 0 || fildes >= OPEN_MAX) + return EBADF; + + char* path_copy = strdup(path); + if (path_copy == nullptr) + return ENOMEM; + + const auto ret = add_file_action(file_actions, { + .type = _POSIX_SPAWN_FILE_ACTION_OPEN, + .open = { + .fildes = fildes, + .path = path_copy, + .oflag = oflag, + .mode = mode, + } + }); + + if (ret != 0) + free(path_copy); + + return ret; +} + +static int do_posix_spawn(pid_t* __restrict pid, const char* __restrict path, const posix_spawn_file_actions_t* file_actions, const posix_spawnattr_t* __restrict attrp, char* const argv[], char* const envp[], bool do_path_resolution) +{ + // MAP_SHARED | MAP_ANONYMOUS is not supported :D + + const auto smo_key = smo_create(sizeof(int), PROT_READ | PROT_WRITE); + if (smo_key == -1) + return errno; + + void* addr = smo_map(smo_key); + smo_delete(smo_key); + if (addr == MAP_FAILED) + return errno; + + auto& child_status = *static_cast*>(addr); + child_status = INT_MAX; + const pid_t child_pid = fork(); if (child_pid == 0) { +#define DIE_ON_ERROR(err, ...) \ + do { \ + auto ret = __VA_ARGS__; \ + if (ret != (err)) \ + break; \ + child_status = errno; \ + exit(127); \ + } while (false) + + if (attrp != nullptr) + { + if (attrp->flags & POSIX_SPAWN_RESETIDS) + DIE_ON_ERROR(-1, seteuid(getuid())); + + if (attrp->flags & POSIX_SPAWN_SETPGROUP) + DIE_ON_ERROR(-1, setpgid(0, attrp->pgroup)); + + if (attrp->flags & POSIX_SPAWN_SETSCHEDULER) + DIE_ON_ERROR(-1, (errno = ENOTSUP, -1)); + // DIE_ON_ERROR(-1, sched_setscheduler(0, attrp->schedpolicy, &attrp->schedparam)); + else if (attrp->flags & POSIX_SPAWN_SETSCHEDPARAM) + DIE_ON_ERROR(-1, (errno = ENOTSUP, -1)); + // DIE_ON_ERROR(-1, sched_setparam(0, &attrp->schedparam)); + + if (attrp->flags & POSIX_SPAWN_SETSIGDEF) + for (int sig = _SIGMIN; sig <= _SIGMAX; sig++) + if (attrp->sigdefault & (1ull << sig)) + DIE_ON_ERROR(NULL, signal(sig, SIG_DFL)); + + if (attrp->flags & POSIX_SPAWN_SETSIGMASK) + DIE_ON_ERROR(-1, sigprocmask(SIG_SETMASK, &attrp->sigmask, nullptr)); + } + + if (file_actions != nullptr) + { + for (size_t i = 0; i < file_actions->action_count; i++) + { + const auto& action = file_actions->actions[i]; + switch (action.type) + { + case _POSIX_SPAWN_FILE_ACTION_CLOSE: + // EBADF is not considered an error with addclose + close(action.close.fildes); + break; + case _POSIX_SPAWN_FILE_ACTION_DUP2: + if (action.dup2.fildes != action.dup2.newfildes) + DIE_ON_ERROR(-1, dup2(action.dup2.fildes, action.dup2.newfildes)); + else + DIE_ON_ERROR(-1, fcntl(action.dup2.fildes, F_SETFD, fcntl(action.dup2.fildes, F_GETFD) & ~O_CLOEXEC)); + break; + case _POSIX_SPAWN_FILE_ACTION_OPEN: + const int fd = open(action.open.path, action.open.oflag, action.open.mode); + DIE_ON_ERROR(-1, fd); + if (fd != action.open.fildes) + { + DIE_ON_ERROR(-1, dup2(fd, action.open.fildes)); + close(fd); + } + break; + } + } + } + +#undef DIE_ON_ERROR + + child_status = 0; + auto* func = do_path_resolution ? execvpe : execve; func(path, argv, envp); - exit(128 + errno); + exit(127); + } + + if (child_pid == -1) + { + munmap(addr, sizeof(int)); + return errno; + } + + while (child_status == INT_MAX) + sched_yield(); + + const int child_status_copy = child_status; + munmap(addr, sizeof(int)); + + if (child_status_copy != 0) + { + while (waitpid(child_pid, nullptr, 0) == -1 && errno == EINTR) + continue; + return child_status_copy; } if (pid != nullptr) @@ -46,23 +328,3 @@ int posix_spawnp(pid_t* __restrict pid, const char* __restrict path, const posix { return do_posix_spawn(pid, path,file_actions, attrp, argv, envp, true); } - -TODO_FUNC(posix_spawn_file_actions_addclose, posix_spawn_file_actions_t*, int) -TODO_FUNC(posix_spawn_file_actions_adddup2, posix_spawn_file_actions_t*, int, int) -TODO_FUNC(posix_spawn_file_actions_addopen, posix_spawn_file_actions_t* __restrict, int, const char* __restrict, int, mode_t) -TODO_FUNC(posix_spawn_file_actions_destroy, posix_spawn_file_actions_t*) -TODO_FUNC(posix_spawn_file_actions_init, posix_spawn_file_actions_t*) -TODO_FUNC(posix_spawnattr_destroy, posix_spawnattr_t*) -TODO_FUNC(posix_spawnattr_getflags, const posix_spawnattr_t* __restrict, short* __restrict) -TODO_FUNC(posix_spawnattr_getpgroup, const posix_spawnattr_t* __restrict, pid_t* __restrict) -TODO_FUNC(posix_spawnattr_getschedparam, const posix_spawnattr_t* __restrict, struct sched_param* __restrict) -TODO_FUNC(posix_spawnattr_getschedpolicy, const posix_spawnattr_t* __restrict, int* __restrict) -TODO_FUNC(posix_spawnattr_getsigdefault, const posix_spawnattr_t* __restrict, sigset_t* __restrict) -TODO_FUNC(posix_spawnattr_getsigmask, const posix_spawnattr_t* __restrict, sigset_t* __restrict) -TODO_FUNC(posix_spawnattr_init, posix_spawnattr_t*) -TODO_FUNC(posix_spawnattr_setflags, posix_spawnattr_t*, short) -TODO_FUNC(posix_spawnattr_setpgroup, posix_spawnattr_t*, pid_t) -TODO_FUNC(posix_spawnattr_setschedparam, posix_spawnattr_t* __restrict, const struct sched_param* __restrict) -TODO_FUNC(posix_spawnattr_setschedpolicy, posix_spawnattr_t*, int) -TODO_FUNC(posix_spawnattr_setsigdefault, posix_spawnattr_t* __restrict, const sigset_t* __restrict) -TODO_FUNC(posix_spawnattr_setsigmask, posix_spawnattr_t* __restrict, const sigset_t* __restrict)