LibC: Support attrs and file actions in posix spawn

Apparently GCC wants to use posix_spawn now that it is available, this
patch adds support for the missing fields. POSIX Issue 8 did add some
fields that are not supported here
This commit is contained in:
Bananymous 2026-01-25 01:43:45 +02:00
parent aef536fff3
commit 7843d3de62
3 changed files with 346 additions and 34 deletions

View File

@ -14,8 +14,58 @@ __BEGIN_DECLS
#define __need_pid_t #define __need_pid_t
#include <sys/types.h> #include <sys/types.h>
typedef int posix_spawnattr_t; typedef struct
typedef int posix_spawn_file_actions_t; {
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_RESETIDS 0x01
#define POSIX_SPAWN_SETPGROUP 0x02 #define POSIX_SPAWN_SETPGROUP 0x02

View File

@ -41,7 +41,7 @@ __BEGIN_DECLS
#define _POSIX_SEMAPHORES -1 /* sem_{close,destroy,getvalue,init,open,post,trywait,unlink,wait} */ #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_SHARED_MEMORY_OBJECTS -1 /* shm_{open,unlink} */
#define _POSIX_SHELL 200809L #define _POSIX_SHELL 200809L
#define _POSIX_SPAWN -1 /* posix_spawn* */ #define _POSIX_SPAWN 200809L
#define _POSIX_SPIN_LOCKS 200809L #define _POSIX_SPIN_LOCKS 200809L
#define _POSIX_SPORADIC_SERVER -1 /* sched_{setparam,setscheduler} with SCHED_SPORADIC */ #define _POSIX_SPORADIC_SERVER -1 /* sched_{setparam,setscheduler} with SCHED_SPORADIC */
#define _POSIX_SYNCHRONIZED_IO 200809L #define _POSIX_SYNCHRONIZED_IO 200809L

View File

@ -1,34 +1,316 @@
#include <BAN/Debug.h> #include <BAN/Atomic.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <spawn.h> #include <spawn.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <sys/banan-os.h>
#include <sys/mman.h>
#include <unistd.h> #include <unistd.h>
#define TODO_FUNC(name, ...) int name(__VA_ARGS__) { dwarnln("TODO: " #name); errno = ENOTSUP; return -1; } #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"); case SCHED_FIFO:
errno = ENOTSUP; case SCHED_RR:
return -1; 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"); switch (file_actions->actions[i].type)
errno = ENOTSUP; {
return -1; 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(); const pid_t child_pid = fork();
if (child_pid == 0) 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; auto* func = do_path_resolution ? execvpe : execve;
func(path, argv, envp); 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) 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); 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)