#include #include #include #include #include #include #include #include #include #define TODO_FUNC(name, ...) int name(__VA_ARGS__) { dwarnln("TODO: " #name); errno = ENOTSUP; return -1; } int posix_spawnattr_init(posix_spawnattr_t* attr) { 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) { case SCHED_FIFO: case SCHED_RR: case SCHED_SPORADIC: case SCHED_OTHER: break; default: return EINVAL; } 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++) { 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(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) *pid = child_pid; return 0; } int 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[]) { return do_posix_spawn(pid, path,file_actions, attrp, argv, envp, false); } int posix_spawnp(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[]) { return do_posix_spawn(pid, path,file_actions, attrp, argv, envp, true); }