banan-os/userspace/libraries/LibC/spawn.cpp

331 lines
8.3 KiB
C++

#include <BAN/Atomic.h>
#include <errno.h>
#include <fcntl.h>
#include <spawn.h>
#include <stdlib.h>
#include <string.h>
#include <sys/banan-os.h>
#include <sys/mman.h>
#include <unistd.h>
#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<BAN::Atomic<int>*>(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);
}