Add posix_spawn_file_actions_t and posix_spawnattr_t and tests

This commit is contained in:
R Aadarsh
2026-05-12 21:03:10 +05:30
parent 0dd5cf597a
commit df78820f01
7 changed files with 583 additions and 1 deletions
+1 -1
View File
@@ -124,7 +124,7 @@ pub mod setjmp;
pub mod sgtty;
pub mod shadow;
pub mod signal;
// TODO: spawn.h
pub mod spawn;
// TODO: stdalign.h (likely C implementation)
// stdarg.h implemented in C
// stdatomic.h implemented in C
+10
View File
@@ -0,0 +1,10 @@
sys_includes = ["sched.h", "bits/sigset-t.h"]
include_guard = "_RELIBC_SPAWN_H"
language = "C"
cpp_compat = true
[enum]
prefix_with_name = true
[export.rename]
"sched_param" = "struct sched_param"
+200
View File
@@ -0,0 +1,200 @@
use core::{
ffi::{c_char, c_int},
ptr::null,
};
use crate::{
error::{Errno, Result},
header::{
errno::{EBADF, EINVAL, ENOMEM},
stdlib::{free, malloc},
string::memcpy,
},
platform::types::{c_void, mode_t},
};
const OPEN: c_char = 1;
const CLOSE: c_char = 2;
const CHDIR: c_char = 3;
const FCHDIR: c_char = 4;
const DUP2: c_char = 5;
#[repr(C)]
enum Operation {
Open {
fd: c_int,
path: *const c_char,
flag: c_int,
mode: mode_t,
},
Close(c_int),
Chdir(*const c_char),
FChdir(c_int),
Dup2(c_int, c_int),
}
#[repr(C)]
pub struct posix_spawn_file_actions_t {
size: usize,
operation: *const Operation,
}
fn copy_op(file_actions: *mut posix_spawn_file_actions_t, op: Operation) -> Result<()> {
if file_actions.is_null() {
return Err(Errno(EINVAL));
}
let new = unsafe { malloc((*file_actions).size + size_of::<Operation>()) };
if new == -1isize as *mut c_void {
return Err(Errno(ENOMEM));
}
unsafe {
if (*file_actions).size >= size_of::<Operation>() {
memcpy(
new,
(*file_actions).operation as *const c_void,
(*file_actions).size / size_of::<Operation>(),
);
free((*file_actions).operation as *mut c_void);
}
(*file_actions).operation = new as *const Operation;
let new = new as *mut Operation;
(*new.add((*file_actions).size)) = op;
(*file_actions).size += size_of::<Operation>()
}
Ok(())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawn_file_actions_init(
file_actions: *mut posix_spawn_file_actions_t,
) -> c_int {
if file_actions.is_null() {
return EINVAL;
}
unsafe {
(*file_actions).operation = null();
(*file_actions).size = 0;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawn_file_actions_destroy(
file_actions: *mut posix_spawn_file_actions_t,
) -> c_int {
if file_actions.is_null() {
return EINVAL;
}
unsafe {
free((*file_actions).operation as *mut c_void);
(*file_actions).operation = null();
(*file_actions).size = 0;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawn_file_actions_addopen(
file_actions: *mut posix_spawn_file_actions_t,
fd: c_int,
path: *const c_char,
oflag: c_int,
mode: mode_t,
) -> c_int {
if fd < 0 {
return EBADF;
}
let open_op = Operation::Open {
fd,
path,
flag: oflag,
mode,
};
if let Err(e) = copy_op(file_actions, open_op) {
return e.0;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawn_file_actions_addclose(
file_actions: *mut posix_spawn_file_actions_t,
fd: c_int,
) -> c_int {
if fd < 0 {
return EBADF;
}
let close_op = Operation::Close(fd);
if let Err(e) = copy_op(file_actions, close_op) {
return e.0;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawn_file_actions_addchdir(
file_actions: *mut posix_spawn_file_actions_t,
path: *const c_char,
) -> c_int {
let chdir_op = Operation::Chdir(path);
if let Err(e) = copy_op(file_actions, chdir_op) {
return e.0;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawn_file_actions_addfchdir(
file_actions: *mut posix_spawn_file_actions_t,
fd: c_int,
) -> c_int {
if fd < 0 {
return EBADF;
}
let fchdir_op = Operation::FChdir(fd);
if let Err(e) = copy_op(file_actions, fchdir_op) {
return e.0;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawn_file_actions_adddup2(
file_actions: *mut posix_spawn_file_actions_t,
fd: c_int,
new: c_int,
) -> c_int {
if fd < 0 || new < 0 {
return EBADF;
}
let dup2_op = Operation::Dup2(fd, new);
if let Err(e) = copy_op(file_actions, dup2_op) {
return e.0;
}
0
}
+25
View File
@@ -0,0 +1,25 @@
//! `spawn.h` implementation
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/spawn.h.html>.
mod file_actions;
mod spawn_attr;
use core::ffi::c_char;
use crate::{
header::spawn::{file_actions::posix_spawn_file_actions_t, spawn_attr::posix_spawnattr_t},
platform::types::{c_int, pid_t},
};
#[unsafe(no_mangle)]
pub fn posix_spawn(
pid: *const pid_t,
path: *const c_char,
file_actions: *const posix_spawn_file_actions_t,
spawn_attr: *const posix_spawnattr_t,
argv: *const *const c_char,
envp: *const *const c_char,
) -> c_int {
todo!()
}
+254
View File
@@ -0,0 +1,254 @@
use core::{
ffi::{c_int, c_short},
mem::zeroed,
};
use bitflags;
use crate::header::{
bits_sigset_t::sigset_t,
errno::EINVAL,
sched::{SCHED_FIFO, SCHED_OTHER, SCHED_RR, sched_param},
sys_types_internal::pid_t,
};
bitflags::bitflags! {
struct Flags: c_short
{
const POSIX_SPAWN_RESETIDS = 1;
const POSIX_SPAWN_SETPGROUP = 2;
const POSIX_SPAWN_SETSIGDEF = 3;
const POSIX_SPAWN_SETSIGMASK = 4;
const POSIX_SPAWN_SETSCHEDPARAM = 5;
const POSIX_SPAWN_SETSCHEDULER = 6;
}
}
pub const POSIX_SPAWN_RESETIDS: c_short = 1;
pub const POSIX_SPAWN_SETPGROUP: c_short = 2;
pub const POSIX_SPAWN_SETSIGDEF: c_short = 3;
pub const POSIX_SPAWN_SETSIGMASK: c_short = 4;
pub const POSIX_SPAWN_SETSCHEDPARAM: c_short = 5;
pub const POSIX_SPAWN_SETSCHEDULER: c_short = 6;
#[repr(C)]
pub struct posix_spawnattr_t {
pub(crate) param: sched_param,
flags: c_short,
pgroup: c_int,
policy: c_int,
sigdefault: sigset_t,
sigmask: sigset_t,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_init(attr: *mut posix_spawnattr_t) -> c_int {
if attr.is_null() {
return EINVAL;
}
unsafe {
*attr = zeroed();
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_destroy(attr: *mut posix_spawnattr_t) -> c_int {
if attr.is_null() {
return EINVAL;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_setschedparam(
attr: *mut posix_spawnattr_t,
schedparam: *const sched_param,
) -> c_int {
if attr.is_null() || schedparam.is_null() {
return EINVAL;
}
unsafe {
(*attr).param = *schedparam;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_getschedparam(
attr: *const posix_spawnattr_t,
schedparam: *mut sched_param,
) -> c_int {
if attr.is_null() || schedparam.is_null() {
return EINVAL;
}
unsafe {
*schedparam = (*attr).param;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_setschedpolicy(
attr: *mut posix_spawnattr_t,
schedpolicy: c_int,
) -> c_int {
if attr.is_null() {
return EINVAL;
}
match schedpolicy {
SCHED_FIFO | SCHED_RR | SCHED_OTHER => unsafe {
(*attr).policy = schedpolicy;
},
_ => return EINVAL,
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_getschedpolicy(
attr: *const posix_spawnattr_t,
schedpolicy: *mut c_int,
) -> c_int {
if attr.is_null() || schedpolicy.is_null() {
return EINVAL;
}
unsafe {
*schedpolicy = (*attr).policy;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_setsigdefault(
attr: *mut posix_spawnattr_t,
sigdefault: *const sigset_t,
) -> c_int {
if attr.is_null() || sigdefault.is_null() {
return EINVAL;
}
unsafe {
(*attr).sigdefault = *sigdefault;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_getsigdefault(
attr: *const posix_spawnattr_t,
sigdefault: *mut sigset_t,
) -> c_int {
if attr.is_null() || sigdefault.is_null() {
return EINVAL;
}
unsafe {
*sigdefault = (*attr).sigdefault;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_setsigmask(
attr: *mut posix_spawnattr_t,
sigmask: *const sigset_t,
) -> c_int {
if attr.is_null() || sigmask.is_null() {
return EINVAL;
}
unsafe {
(*attr).sigmask = *sigmask;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_getsigmask(
attr: *const posix_spawnattr_t,
sigmask: *mut sigset_t,
) -> c_int {
if attr.is_null() || sigmask.is_null() {
return EINVAL;
}
unsafe {
*sigmask = (*attr).sigmask;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_setflags(
attr: *mut posix_spawnattr_t,
flags: c_short,
) -> c_int {
if attr.is_null() {
return EINVAL;
}
unsafe {
match Flags::from_bits(flags) {
Some(v) => (*attr).flags = v.bits(),
None => {
return EINVAL;
}
}
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_getflags(
attr: *const posix_spawnattr_t,
flags: *mut c_short,
) -> c_int {
if attr.is_null() || flags.is_null() {
return EINVAL;
}
unsafe {
*flags = (*attr).flags;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_setpgroup(
attr: *mut posix_spawnattr_t,
pgroup: pid_t,
) -> c_int {
if attr.is_null() {
return EINVAL;
}
unsafe {
(*attr).pgroup = pgroup;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_spawnattr_getpgroup(
attr: *const posix_spawnattr_t,
pgroup: *mut pid_t,
) -> c_int {
if attr.is_null() || pgroup.is_null() {
return EINVAL;
}
unsafe {
*pgroup = (*attr).pgroup;
}
0
}
+1
View File
@@ -77,6 +77,7 @@ EXPECT_NAMES=\
sigaltstack \
signal \
sigsetjmp \
spawn \
stdio/all \
stdio/buffer \
stdio/dprintf \
+92
View File
@@ -0,0 +1,92 @@
#include <spawn.h>
#include <fcntl.h>
#include <stdio.h>
#include <bits/sigset-t.h>
#include <sched.h>
#include "test_helpers.h"
#define CHECK(cond, msg) \
if (!cond) \
{ \
fprintf(stderr, "Error in spawn.h: %s\n", msg); \
_exit(EXIT_FAILURE); \
}
#define CHECK_WHEN_NULL(call, name) \
if (!(call)) \
{ \
fprintf(stderr, "Error in spawn.h: %s expected to fail when passing NULL\n", name); \
_exit(EXIT_FAILURE); \
}
int main()
{
posix_spawn_file_actions_t fa_t;
CHECK((posix_spawn_file_actions_init(&fa_t) == 0), "posix_spawn_file_actions_init failed")
CHECK_WHEN_NULL(posix_spawn_file_actions_init(NULL), "posix_spawn_file_actions_init")
CHECK((posix_spawn_file_actions_addopen(&fa_t, 1, ".", O_RDONLY, 1) == 0), "Error while adding open action")
CHECK((posix_spawn_file_actions_addclose(&fa_t, 1) == 0), "Error while adding close action")
CHECK((posix_spawn_file_actions_adddup2(&fa_t, -1, 3) == EBADF), "Adding dup2 with negative fd must fail")
CHECK((fa_t.size == 64), "Size remains unchanged")
CHECK_WHEN_NULL(posix_spawn_file_actions_destroy(NULL), "posix_spawn_file_actions_destroy")
CHECK_WHEN_NULL(posix_spawn_file_actions_addclose(NULL, 1), "posix_spawn_file_actions_addclose")
CHECK((posix_spawn_file_actions_destroy(&fa_t) == 0), "posix_spawn_file_actions_destroy failed")
CHECK((fa_t.size == 0), "Expected 0 size in posix_spawn_file_actions_t after destruction")
posix_spawnattr_t sa;
CHECK((posix_spawnattr_init(&sa) == 0), "posix_spawnattr_init failed")
CHECK((sa.param.sched_priority == 0 && sa.flags == 0 && sa.pgroup == 0 && sa.policy == 0 && sa.sigdefault == 0 && sa.sigmask == 0), "All fields expected to be zero after init in posix_spawnattr_t")
CHECK_WHEN_NULL(posix_spawnattr_init(NULL), "posix_spawnattr_init")
CHECK_WHEN_NULL(posix_spawnattr_destroy(NULL), "posix_spawnattr_destroy")
struct sched_param sp1;
sp1.sched_priority = 2;
struct sched_param sp2;
sp2.sched_priority = 0;
CHECK_WHEN_NULL(posix_spawnattr_setschedparam(NULL, &sp1), "posix_spawnattr_setschedparam")
CHECK_WHEN_NULL(posix_spawnattr_setschedparam(&sa, NULL), "posix_spawnattr_setschedparam")
CHECK((posix_spawnattr_setschedparam(&sa, &sp1) == 0), "posix_spawnattr_setschedparam failed")
CHECK_WHEN_NULL(posix_spawnattr_getschedparam(NULL, &sp2), "posix_spawnattr_getschedparam")
CHECK_WHEN_NULL(posix_spawnattr_getschedparam(&sa, NULL), "posix_spawnattr_getschedparam")
CHECK((posix_spawnattr_getschedparam(&sa, &sp2) == 0 && sp2.sched_priority == 2), "posix_spawnattr_getschedparam failed")
CHECK_WHEN_NULL(posix_spawnattr_setschedpolicy(NULL, 0), "posix_spawnattr_setschedpolicy")
CHECK((posix_spawnattr_setschedpolicy(&sa, 1) == 0), "posix_spawnattr_setschedpolicy failed")
int pol = 0;
CHECK_WHEN_NULL(posix_spawnattr_getschedpolicy(NULL, &pol), "posix_spawnattr_getschedpolicy")
CHECK_WHEN_NULL(posix_spawnattr_getschedpolicy(&sa, NULL), "posix_spawnattr_getschedpolicy")
CHECK((posix_spawnattr_getschedpolicy(&sa, &pol) == 0 && pol == 1), "posix_spawnattr_getschedpolicy failed")
sigset_t sst = 1;
sigset_t sst2 = 0;
CHECK_WHEN_NULL(posix_spawnattr_setsigdefault(NULL, &sst), "posix_spawnattr_setsigdefault")
CHECK_WHEN_NULL(posix_spawnattr_setsigdefault(&sa, NULL), "posix_spawnattr_setsigdefault")
CHECK((posix_spawnattr_setsigdefault(&sa, &sst) == 0), "posix_spawnattr_setsigdefault failed")
CHECK((posix_spawnattr_getsigdefault(&sa, &sst2) == 0 && sst2 == 1), "posix_spawnattr_getsigdefault failed")
CHECK_WHEN_NULL(posix_spawnattr_getsigdefault(NULL, &sst2), "posix_spawnattr_getsigdefault")
CHECK_WHEN_NULL(posix_spawnattr_getsigdefault(&sa, NULL), "posix_spawnattr_getsigdefault")
sst = 3;
CHECK_WHEN_NULL(posix_spawnattr_setsigmask(NULL, &sst), "posix_spawnattr_setsigmask")
CHECK_WHEN_NULL(posix_spawnattr_setsigmask(&sa, NULL), "posix_spawnattr_setsigmask")
CHECK((posix_spawnattr_setsigmask(&sa, &sst) == 0), "posix_spawnattr_setsigmask failed")
CHECK_WHEN_NULL(posix_spawnattr_getsigmask(NULL, &sst2), "posix_spawnattr_getsigmask")
CHECK_WHEN_NULL(posix_spawnattr_getsigmask(&sa, NULL), "posix_spawnattr_getsigmask")
CHECK((posix_spawnattr_getsigmask(&sa, &sst2) == 0 && sst2 == 3), "posix_spawnattr_getsigmask failed")
CHECK_WHEN_NULL(posix_spawnattr_setflags(NULL, 0), "posix_spawnattr_setflags")
CHECK((posix_spawnattr_setflags(&sa, 1) == 0), "posix_spawnattr_setflags failed")
short int pf = 0;
CHECK_WHEN_NULL(posix_spawnattr_getflags(NULL, &pf), "posix_spawnattr_getflags")
CHECK_WHEN_NULL(posix_spawnattr_getflags(&sa, NULL), "posix_spawnattr_getflags")
CHECK((posix_spawnattr_getflags(&sa, &pf) == 0 && pf == 1), "posix_spawnattr_getflags failed")
CHECK((posix_spawnattr_setflags(&sa, 7565) == EINVAL), "posix_spawnattr_setflags expected to fail when passing invalid bits")
pid_t id = 0;
CHECK_WHEN_NULL(posix_spawnattr_setpgroup(NULL, 3), "posix_spawnattr_setpgroup")
CHECK((posix_spawnattr_setpgroup(&sa, 3) == 0), "posix_spawnattr_setpgroup failed")
CHECK_WHEN_NULL(posix_spawnattr_getpgroup(NULL, &id), "posix_spawnattr_getpgroup")
CHECK_WHEN_NULL(posix_spawnattr_getpgroup(&sa, NULL), "posix_spawnattr_getpgroup")
CHECK((posix_spawnattr_getpgroup(&sa, &id) == 0 && id == 3), "posix_spawnattr_getpgroup failed")
}