Compare commits

...

5 Commits

Author SHA1 Message Date
Bananymous c304133224 LibC: Indicate regex support in unistd.h 2026-01-25 01:47:30 +02:00
Bananymous 7843d3de62 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
2026-01-25 01:45:47 +02:00
Bananymous aef536fff3 Kernel: Fix SharedMemoryObject cloning on deleted keys 2026-01-25 01:42:17 +02:00
Bananymous d472e1ac0e Kernel: Remove obsolete FIXMEs and null pointer checks 2026-01-24 22:42:18 +02:00
Bananymous 120c08fb75 Kernel: Implement fcntl based locks 2026-01-24 22:38:34 +02:00
7 changed files with 590 additions and 94 deletions

View File

@ -396,6 +396,7 @@ namespace Kernel
BAN::UniqPtr<PageTable> m_page_table;
BAN::RefPtr<TTY> m_controlling_terminal;
friend class OpenFileDescriptorSet;
friend class Thread;
};

View File

@ -83,7 +83,7 @@ namespace Kernel
BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> SharedMemoryObject::clone(PageTable& new_page_table)
{
auto region = TRY(SharedMemoryObjectManager::get().map_object(m_object->key, new_page_table, { .start = vaddr(), .end = vaddr() + size() }));
auto region = TRY(SharedMemoryObject::create(m_object, new_page_table, { .start = vaddr(), .end = vaddr() + size() }));
return BAN::UniqPtr<MemoryRegion>(BAN::move(region));
}

View File

@ -12,6 +12,20 @@
namespace Kernel
{
struct InodeRefPtrHash
{
BAN::hash_t operator()(const BAN::RefPtr<Inode>& inode)
{
return BAN::hash<const Inode*>()(inode.ptr());
}
};
static Mutex s_fcntl_lock_mutex;
static ThreadBlocker s_fcntl_lock_thread_blocker;
static BAN::HashMap<BAN::RefPtr<Inode>, BAN::Vector<struct flock>, InodeRefPtrHash> s_fcntl_locks;
static BAN::ErrorOr<void> fcntl_lock(BAN::RefPtr<Inode> inode, int cmd, struct flock& flock);
OpenFileDescriptorSet::OpenFileDescriptorSet(const Credentials& credentials)
: m_credentials(credentials)
{
@ -219,6 +233,68 @@ namespace Kernel
BAN::ErrorOr<int> OpenFileDescriptorSet::fcntl(int fd, int cmd, uintptr_t extra)
{
if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK)
{
struct flock flock;
TRY(Process::current().read_from_user(reinterpret_cast<void*>(extra), &flock, sizeof(struct flock)));
flock.l_pid = Process::current().pid();
BAN::RefPtr<Inode> inode;
{
LockGuard _(m_mutex);
TRY(validate_fd(fd));
const auto& open_file = m_open_files[fd];
inode = open_file.inode();
switch (flock.l_whence)
{
case SEEK_SET:
break;
case SEEK_CUR:
if (BAN::Math::will_addition_overflow(flock.l_start, open_file.offset()))
return BAN::Error::from_errno(EOVERFLOW);
flock.l_start += m_open_files[fd].offset();
break;
case SEEK_END:
if (BAN::Math::will_addition_overflow(flock.l_start, inode->size()))
return BAN::Error::from_errno(EOVERFLOW);
flock.l_start += inode->size();
break;
default:
return BAN::Error::from_errno(EINVAL);
}
if (BAN::Math::will_addition_overflow(flock.l_start, flock.l_len))
return BAN::Error::from_errno(EOVERFLOW);
if (flock.l_len < 0)
{
flock.l_start += flock.l_len;
if (flock.l_len == BAN::numeric_limits<decltype(flock.l_len)>::min())
return BAN::Error::from_errno(EOVERFLOW);
flock.l_len = -flock.l_len;
}
flock.l_whence = SEEK_SET;
}
auto lock_ret = fcntl_lock(inode, cmd, flock);
if (lock_ret.is_error())
{
if (lock_ret.error().get_error_code() == ENOMEM)
return BAN::Error::from_errno(ENOLCK);
return lock_ret.release_error();
}
if (cmd == F_GETLK)
TRY(Process::current().write_to_user(reinterpret_cast<void*>(extra), &flock, sizeof(struct flock)));
return 0;
}
LockGuard _(m_mutex);
TRY(validate_fd(fd));
@ -252,46 +328,6 @@ namespace Kernel
m_open_files[fd].status_flags() &= O_ACCMODE;
m_open_files[fd].status_flags() |= extra;
return 0;
case F_GETLK:
{
dwarnln("TODO: proper fcntl F_GETLK");
auto* param = reinterpret_cast<struct flock*>(extra);
const auto& flock = m_open_files[fd].description->flock;
if (flock.lockers.empty())
param->l_type = F_UNLCK;
else
{
*param = {
.l_type = static_cast<short>(flock.shared ? F_RDLCK : F_WRLCK),
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = 1,
.l_pid = *flock.lockers.begin(),
};
}
return 0;
}
case F_SETLK:
case F_SETLKW:
{
dwarnln("TODO: proper fcntl F_SETLK(W)");
int op = cmd == F_SETLKW ? LOCK_NB : 0;
switch (reinterpret_cast<const struct flock*>(extra)->l_type)
{
case F_UNLCK: op |= LOCK_UN; break;
case F_RDLCK: op |= LOCK_SH; break;
case F_WRLCK: op |= LOCK_EX; break;
default:
return BAN::Error::from_errno(EINVAL);
}
TRY(flock(fd, op));
return 0;
}
default:
break;
}
@ -363,6 +399,18 @@ namespace Kernel
}
}
{
LockGuard _(s_fcntl_lock_mutex);
if (auto it = s_fcntl_locks.find(open_file.inode()); it != s_fcntl_locks.end())
{
auto& flocks = it->value;
for (size_t i = 0; i < flocks.size(); i++)
if (flocks[i].l_pid == Process::current().pid())
flocks.remove(i--);
s_fcntl_lock_thread_blocker.unblock();
}
}
open_file.inode()->on_close(open_file.status_flags());
open_file.description.clear();
open_file.descriptor_flags = 0;
@ -389,6 +437,147 @@ namespace Kernel
}
}
static BAN::ErrorOr<void> fcntl_lock(BAN::RefPtr<Inode> inode, int cmd, struct flock& flock)
{
if (!inode->mode().ifreg())
return BAN::Error::from_errno(EINVAL);
LockGuard _(s_fcntl_lock_mutex);
static constexpr auto locks_overlap =
[](struct flock a, struct flock b) -> bool
{
if (a.l_len == 0 && b.l_len == 0)
return true;
if (a.l_len == 0 && a.l_start < b.l_start + b.l_len)
return true;
if (b.l_len == 0 && b.l_start < a.l_start + a.l_len)
return true;
if (a.l_start + a.l_len <= b.l_start)
return false;
if (b.l_start + b.l_len <= a.l_start)
return false;
return true;
};
const auto lock_preventer =
[flock](BAN::Span<struct flock> locks) -> struct flock*
{
for (auto& lock : locks)
{
if (lock.l_pid == flock.l_pid)
continue;
if (!locks_overlap(lock, flock))
continue;
if (lock.l_type == F_RDLCK && flock.l_type == F_RDLCK)
continue;
return &lock;
}
return nullptr;
};
switch (cmd)
{
case F_GETLK:
{
auto it = s_fcntl_locks.find(inode);
if (it == s_fcntl_locks.end())
flock.l_type = F_UNLCK;
else if (auto* preventer = lock_preventer(it->value.span()))
flock = *preventer;
else
flock.l_type = F_UNLCK;
return {};
}
case F_SETLK:
case F_SETLKW:
{
if (flock.l_type == F_UNLCK)
{
auto it = s_fcntl_locks.find(inode);
if (it == s_fcntl_locks.end())
return {};
auto& flocks = it->value;
for (size_t i = 0; i < flocks.size(); i++)
{
if (flocks[i].l_pid != flock.l_pid)
continue;
if (!locks_overlap(flocks[i], flock))
continue;
struct flock new_flocks[2];
size_t new_flock_count { 0 };
if (flocks[i].l_start < flock.l_start)
{
const off_t flock_len = flocks[i].l_len ? flocks[i].l_len : inode->size();
new_flocks[new_flock_count] = flock;
new_flocks[new_flock_count].l_len = flocks[i].l_start + flock_len - flock.l_start;
new_flock_count++;
}
if (flock.l_len == 0)
;
else if (flocks[i].l_len == 0)
{
new_flocks[new_flock_count] = flock;
new_flocks[new_flock_count].l_start = flock.l_start + flock.l_len;
new_flocks[new_flock_count].l_len = 0;
new_flock_count++;
}
else if (flocks[i].l_start + flocks[i].l_len > flock.l_start + flock.l_len)
{
new_flocks[new_flock_count] = flock;
new_flocks[new_flock_count].l_start = flock.l_start + flock.l_len;
new_flocks[new_flock_count].l_len = (flocks[i].l_start + flocks[i].l_len) - (flock.l_start + flock.l_len);
new_flock_count++;
}
switch (new_flock_count)
{
case 0:
flocks.remove(i--);
break;
case 1:
flocks[i] = new_flocks[0];
break;
case 2:
TRY(flocks.insert(i + 1, new_flocks[1]));
flocks[i++] = new_flocks[0];
break;
}
}
if (flocks.empty())
s_fcntl_locks.remove(it);
s_fcntl_lock_thread_blocker.unblock();
return {};
}
else for (;;)
{
auto it = s_fcntl_locks.find(inode);
if (it == s_fcntl_locks.end())
it = TRY(s_fcntl_locks.emplace(inode));
if (lock_preventer(it->value.span()) == nullptr)
{
TRY(it->value.push_back(flock));
return {};
}
if (cmd == F_SETLK)
return BAN::Error::from_errno(EAGAIN);
TRY(Thread::current().block_or_eintr_indefinite(s_fcntl_lock_thread_blocker, &s_fcntl_lock_mutex));
}
}
default:
ASSERT_NOT_REACHED();
}
}
BAN::ErrorOr<void> OpenFileDescriptorSet::flock(int fd, int op)
{
const auto pid = Process::current().pid();

View File

@ -1271,9 +1271,8 @@ namespace Kernel
return 0;
}
// FIXME: buffer_region can be null as stack is not MemoryRegion
auto* buffer_region = TRY(validate_and_pin_pointer_access(buffer, count, true));
BAN::ScopeGuard _([buffer_region] { if (buffer_region) buffer_region->unpin(); });
BAN::ScopeGuard _([buffer_region] { buffer_region->unpin(); });
return TRY(m_open_file_descriptors.read(fd, BAN::ByteSpan(static_cast<uint8_t*>(buffer), count)));
}
@ -1285,9 +1284,8 @@ namespace Kernel
return 0;
}
// FIXME: buffer_region can be null as stack is not MemoryRegion
auto* buffer_region = TRY(validate_and_pin_pointer_access(buffer, count, false));
BAN::ScopeGuard _([buffer_region] { if (buffer_region) buffer_region->unpin(); });
BAN::ScopeGuard _([buffer_region] { buffer_region->unpin(); });
return TRY(m_open_file_descriptors.write(fd, BAN::ConstByteSpan(static_cast<const uint8_t*>(buffer), count)));
}
@ -1465,7 +1463,7 @@ namespace Kernel
auto inode = TRY(m_open_file_descriptors.inode_of(fd));
auto* buffer_region = TRY(validate_and_pin_pointer_access(buffer, count, true));
BAN::ScopeGuard _([buffer_region] { if (buffer_region) buffer_region->unpin(); });
BAN::ScopeGuard _([buffer_region] { buffer_region->unpin(); });
return TRY(inode->read(offset, { reinterpret_cast<uint8_t*>(buffer), count }));
}
@ -1475,7 +1473,7 @@ namespace Kernel
auto inode = TRY(m_open_file_descriptors.inode_of(fd));
auto* buffer_region = TRY(validate_and_pin_pointer_access(buffer, count, false));
BAN::ScopeGuard _([buffer_region] { if (buffer_region) buffer_region->unpin(); });
BAN::ScopeGuard _([buffer_region] { buffer_region->unpin(); });
return TRY(inode->write(offset, { reinterpret_cast<const uint8_t*>(buffer), count })); }
@ -1802,10 +1800,10 @@ namespace Kernel
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&regions] {
for (auto* region : regions)
if (region != nullptr)
region->unpin();
region->unpin();
});
// FIXME: this can leak memory if push to regions fails but pinning succeeded
if (message.msg_name)
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_name, message.msg_namelen, true))));
if (message.msg_control)
@ -1832,8 +1830,7 @@ namespace Kernel
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&regions] {
for (auto* region : regions)
if (region != nullptr)
region->unpin();
region->unpin();
});
if (message.msg_name)
@ -1985,7 +1982,7 @@ namespace Kernel
BAN::ErrorOr<long> Process::sys_ppoll(pollfd* fds, nfds_t nfds, const timespec* user_timeout, const sigset_t* user_sigmask)
{
auto* fds_region = TRY(validate_and_pin_pointer_access(fds, nfds * sizeof(pollfd), true));
BAN::ScopeGuard _([fds_region] { if (fds_region) fds_region->unpin(); });
BAN::ScopeGuard _([fds_region] { fds_region->unpin(); });
const auto old_sigmask = Thread::current().m_signal_block_mask;
if (user_sigmask != nullptr)
@ -2168,10 +2165,7 @@ namespace Kernel
}
auto* events_region = TRY(validate_and_pin_pointer_access(events, maxevents * sizeof(epoll_event), true));
BAN::ScopeGuard _([events_region] {
if (events_region)
events_region->unpin();
});
BAN::ScopeGuard _([events_region] { events_region->unpin(); });
const auto old_sigmask = Thread::current().m_signal_block_mask;
if (user_sigmask)
@ -2364,7 +2358,7 @@ namespace Kernel
return BAN::Error::from_errno(EOVERFLOW);
auto* list_region = TRY(validate_and_pin_pointer_access(list, list_len * sizeof(struct dirent), true));
BAN::ScopeGuard _([list_region] { if (list_region) list_region->unpin(); });
BAN::ScopeGuard _([list_region] { list_region->unpin(); });
return TRY(m_open_file_descriptors.read_dir_entries(fd, list, list_len));
}
@ -3188,7 +3182,7 @@ namespace Kernel
op &= ~(FUTEX_PRIVATE | FUTEX_REALTIME);
auto* buffer_region = TRY(validate_and_pin_pointer_access(addr, sizeof(uint32_t), false));
BAN::ScopeGuard pin_guard([&] { if (buffer_region) buffer_region->unpin(); });
BAN::ScopeGuard pin_guard([buffer_region] { buffer_region->unpin(); });
const paddr_t paddr = m_page_table->physical_address_of(vaddr & PAGE_ADDR_MASK) | (vaddr & ~PAGE_ADDR_MASK);
ASSERT(paddr != 0);
@ -3819,7 +3813,7 @@ namespace Kernel
return BAN::Error::from_errno(EPERM);
auto* region = TRY(validate_and_pin_pointer_access(groups, count * sizeof(gid_t), false));
BAN::ScopeGuard pin_guard([region] { if (region) region->unpin(); });
BAN::ScopeGuard pin_guard([region] { region->unpin(); });
TRY(m_credentials.set_groups({ groups, count }));

View File

@ -14,8 +14,58 @@ __BEGIN_DECLS
#define __need_pid_t
#include <sys/types.h>
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

View File

@ -36,12 +36,12 @@ __BEGIN_DECLS
#define _POSIX_RAW_SOCKETS 0 /* raw sockets :D */
#define _POSIX_READER_WRITER_LOCKS 200809L
#define _POSIX_REALTIME_SIGNALS -1 /* siq{queue,timedwait,waitinfo} */
#define _POSIX_REGEXP -1 /* reg{comp,error,exec,free} */
#define _POSIX_REGEXP 200809L
#define _POSIX_SAVED_IDS 200809L
#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

View File

@ -1,34 +1,316 @@
#include <BAN/Debug.h>
#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; }
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<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(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)