Skip to main content

c/
compat_extra.rs

1// Real implementations for the remaining POSIX/GNU compatibility surface:
2// AIO, mqueue attributes, pthread introspection, spawn attributes, stdio
3// internals, and assorted legacy calls.
4
5// ============================================================
6// AIO (synchronous completion)
7// ============================================================
8
9#[repr(C)]
10pub struct Aiocb {
11    pub aio_fildes: c_int,
12    pub aio_lio_opcode: c_int,
13    pub aio_reqprio: c_int,
14    __pad: c_int,
15    pub aio_buf: *mut c_void,
16    pub aio_nbytes: usize,
17    pub aio_sigevent: [u8; 64],
18    pub aio_offset: i64,
19}
20
21const _: [(); 104] = [(); core::mem::size_of::<Aiocb>()];
22
23#[derive(Copy, Clone)]
24struct AioSlot {
25    cb: *mut Aiocb,
26    ret: isize,
27    err: c_int,
28    active: bool,
29}
30
31static mut AIO_SLOTS: [AioSlot; 32] = [AioSlot {
32    cb: core::ptr::null_mut(),
33    ret: 0,
34    err: 0,
35    active: false,
36}; 32];
37
38#[repr(C)]
39#[derive(Copy, Clone)]
40pub struct SigEvent {
41    pub sigev_value: usize,
42    pub sigev_signo: c_int,
43    pub sigev_notify: c_int,
44    pub sigev_un: [u8; 48],
45}
46
47const SIGEV_SIGNAL_V: c_int = 0;
48const SIGEV_NONE_V: c_int = 1;
49const SIGEV_THREAD_V: c_int = 2;
50
51unsafe fn aio_slot(cb: *mut Aiocb) -> *mut AioSlot {
52    let base = core::ptr::addr_of_mut!(AIO_SLOTS[0]);
53    for i in 0..AIO_SLOTS.len() {
54        let s = base.add(i);
55        if (*s).cb == cb { return s; }
56    }
57    for i in 0..AIO_SLOTS.len() {
58        let s = base.add(i);
59        if !(*s).active {
60            (*s).cb = cb;
61            return s;
62        }
63    }
64    base
65}
66
67unsafe fn aio_record(cb: *mut Aiocb, ret: isize, err: c_int) {
68    let s = aio_slot(cb);
69    (*s).cb = cb;
70    (*s).ret = ret;
71    (*s).err = err;
72    (*s).active = true;
73}
74
75unsafe fn aio_find(cb: *const Aiocb) -> *mut AioSlot {
76    let base = core::ptr::addr_of_mut!(AIO_SLOTS[0]);
77    for i in 0..AIO_SLOTS.len() {
78        let s = base.add(i);
79        if (*s).cb == cb as *mut Aiocb && (*s).active { return s; }
80    }
81    core::ptr::null_mut()
82}
83
84#[no_mangle]
85pub unsafe extern "C" fn aio_read(aiocbp: *mut Aiocb) -> c_int {
86    if aiocbp.is_null() { ERRNO = EINVAL; return -1; }
87    let r = crate::pread(
88        (*aiocbp).aio_fildes,
89        (*aiocbp).aio_buf,
90        (*aiocbp).aio_nbytes,
91        (*aiocbp).aio_offset,
92    );
93    if r < 0 {
94        aio_record(aiocbp, -1, ERRNO);
95    } else {
96        aio_record(aiocbp, r, 0);
97    }
98    0
99}
100
101#[no_mangle]
102pub unsafe extern "C" fn aio_write(aiocbp: *mut Aiocb) -> c_int {
103    if aiocbp.is_null() { ERRNO = EINVAL; return -1; }
104    let r = crate::pwrite(
105        (*aiocbp).aio_fildes,
106        (*aiocbp).aio_buf as *const u8,
107        (*aiocbp).aio_nbytes,
108        (*aiocbp).aio_offset,
109    );
110    if r < 0 {
111        aio_record(aiocbp, -1, ERRNO);
112    } else {
113        aio_record(aiocbp, r, 0);
114    }
115    0
116}
117
118#[no_mangle]
119pub unsafe extern "C" fn aio_fsync(op: c_int, aiocbp: *mut Aiocb) -> c_int {
120    if aiocbp.is_null() { ERRNO = EINVAL; return -1; }
121    let r = if op == 1 { crate::fdatasync((*aiocbp).aio_fildes) } else { crate::fsync((*aiocbp).aio_fildes) };
122    if r < 0 {
123        aio_record(aiocbp, -1, ERRNO);
124    } else {
125        aio_record(aiocbp, 0, 0);
126    }
127    0
128}
129
130#[no_mangle]
131pub unsafe extern "C" fn aio_error(aiocbp: *const Aiocb) -> c_int {
132    if aiocbp.is_null() { return EINVAL; }
133    let s = aio_find(aiocbp);
134    if s.is_null() { return EINVAL; }
135    if !(*s).active { return EINVAL; }
136    (*s).err
137}
138
139#[no_mangle]
140pub unsafe extern "C" fn aio_return(aiocbp: *mut Aiocb) -> isize {
141    if aiocbp.is_null() { ERRNO = EINVAL; return -1; }
142    let s = aio_find(aiocbp);
143    if s.is_null() { ERRNO = EINVAL; return -1; }
144    let r = (*s).ret;
145    (*s).active = false;
146    (*s).cb = core::ptr::null_mut();
147    r
148}
149
150#[no_mangle]
151pub unsafe extern "C" fn aio_cancel(fd: c_int, aiocbp: *mut Aiocb) -> c_int {
152    if !aiocbp.is_null() {
153        let s = aio_find(aiocbp);
154        if !s.is_null() && (*s).active && (*aiocbp).aio_fildes == fd {
155            aio_record(aiocbp, -1, ECANCELED);
156            return 1; // AIO_CANCELED
157        }
158        return 2; // AIO_ALLDONE
159    }
160    let base = core::ptr::addr_of_mut!(AIO_SLOTS[0]);
161    let mut canceled = false;
162    for i in 0..AIO_SLOTS.len() {
163        let s = base.add(i);
164        if (*s).active && !(*s).cb.is_null() && (*(*s).cb).aio_fildes == fd {
165            (*s).ret = -1;
166            (*s).err = ECANCELED;
167            canceled = true;
168        }
169    }
170    if canceled { 1 } else { 2 }
171}
172
173#[no_mangle]
174pub unsafe extern "C" fn aio_suspend(
175    aiocb_list: *const *const Aiocb,
176    nent: c_int,
177    _timeout: *const timespec,
178) -> c_int {
179    if nent <= 0 || aiocb_list.is_null() { ERRNO = EINVAL; return -1; }
180    // Every request completes synchronously, so all of them are already done.
181    for i in 0..nent as usize {
182        let cb = *aiocb_list.add(i);
183        if cb.is_null() { continue; }
184        if aio_find(cb).is_null() {
185            ERRNO = EINVAL;
186            return -1;
187        }
188    }
189    0
190}
191
192unsafe fn aio_notify(sev: *const SigEvent) {
193    if sev.is_null() { return; }
194    match (*sev).sigev_notify {
195        SIGEV_SIGNAL_V => {
196            crate::sigqueue(crate::getpid(), (*sev).sigev_signo, (*sev).sigev_value as *mut c_void);
197        }
198        SIGEV_THREAD_V => {
199            let fnptr = core::ptr::read_unaligned((*sev).sigev_un.as_ptr().add(0) as *const usize);
200            if fnptr != 0 {
201                let f: unsafe extern "C" fn(usize) = core::mem::transmute(fnptr);
202                f((*sev).sigev_value);
203            }
204        }
205        _ => {}
206    }
207}
208
209#[no_mangle]
210pub unsafe extern "C" fn lio_listio(
211    mode: c_int,
212    aiocb_list: *const *mut Aiocb,
213    nent: c_int,
214    sevp: *mut SigEvent,
215) -> c_int {
216    if nent < 0 || aiocb_list.is_null() { ERRNO = EINVAL; return -1; }
217    let mut ok = 0;
218    for i in 0..nent as usize {
219        let cb = *aiocb_list.add(i);
220        if cb.is_null() { continue; }
221        let r = match (*cb).aio_lio_opcode {
222            0 => aio_read(cb),
223            1 => aio_write(cb),
224            _ => 0,
225        };
226        if r == 0 { ok += 1; }
227    }
228    if mode == 1 && !sevp.is_null() {
229        aio_notify(sevp);
230    }
231    if ok == 0 && nent > 0 { -1 } else { 0 }
232}
233
234// ============================================================
235// POSIX message queues: attributes and notification
236// ============================================================
237
238#[repr(C)]
239pub struct MqAttr {
240    pub mq_flags: c_long,
241    pub mq_maxmsg: c_long,
242    pub mq_msgsize: c_long,
243    pub mq_curmsgs: c_long,
244}
245
246#[derive(Copy, Clone)]
247struct MqNotify {
248    mqd: i64,
249    sev: SigEvent,
250    used: bool,
251}
252
253static mut MQ_NOTIFIES: [MqNotify; 8] = [MqNotify {
254    mqd: -1,
255    sev: SigEvent { sigev_value: 0, sigev_signo: 0, sigev_notify: 0, sigev_un: [0; 48] },
256    used: false,
257}; 8];
258
259unsafe fn mq_attr_fill(mqd: i64, attr: *mut MqAttr) -> c_int {
260    if attr.is_null() { ERRNO = EINVAL; return -1; }
261    let mut st: crate::Stat = core::mem::zeroed();
262    if crate::fstat(mqd as c_int, &mut st) != 0 { return -1; }
263    (*attr).mq_flags = if (crate::fcntl(mqd as c_int, 3) & 0o4000) != 0 { 0o4000 } else { 0 };
264    (*attr).mq_msgsize = 4096;
265    (*attr).mq_maxmsg = if st.st_size > 0 { (st.st_size as c_long / 4096) + 1 } else { 1 };
266    (*attr).mq_curmsgs = 0;
267    0
268}
269
270#[no_mangle]
271pub unsafe extern "C" fn mq_getattr(mqd: i64, attr: *mut MqAttr) -> c_int {
272    mq_attr_fill(mqd, attr)
273}
274
275#[no_mangle]
276pub unsafe extern "C" fn mq_setattr(mqd: i64, attr: *const MqAttr, oldattr: *mut MqAttr) -> c_int {
277    if attr.is_null() { ERRNO = EINVAL; return -1; }
278    if !oldattr.is_null() && mq_attr_fill(mqd, oldattr) != 0 { return -1; }
279    let fd = mqd as c_int;
280    let mut fl = crate::fcntl(fd, 3);
281    if fl < 0 { return -1; }
282    if ((*attr).mq_flags & 0o4000) != 0 { fl |= 0o4000; } else { fl &= !0o4000; }
283    crate::fcntl(fd, 4, fl)
284}
285
286#[no_mangle]
287pub unsafe extern "C" fn mq_notify(mqd: i64, notification: *const SigEvent) -> c_int {
288    let base = core::ptr::addr_of_mut!(MQ_NOTIFIES[0]);
289    // NULL deregisters.
290    if notification.is_null() {
291        for i in 0..MQ_NOTIFIES.len() {
292            let s = base.add(i);
293            if (*s).used && (*s).mqd == mqd { (*s).used = false; return 0; }
294        }
295        ERRNO = EINVAL;
296        return -1;
297    }
298    for i in 0..MQ_NOTIFIES.len() {
299        let s = base.add(i);
300        if (*s).used && (*s).mqd == mqd {
301            (*s).sev = *notification;
302            return 0;
303        }
304    }
305    for i in 0..MQ_NOTIFIES.len() {
306        let s = base.add(i);
307        if !(*s).used {
308            (*s).used = true;
309            (*s).mqd = mqd;
310            (*s).sev = *notification;
311            return 0;
312        }
313    }
314    ERRNO = EBUSY;
315    -1
316}
317
318unsafe fn mq_notify_fire(mqd: i64) {
319    let base = core::ptr::addr_of_mut!(MQ_NOTIFIES[0]);
320    for i in 0..MQ_NOTIFIES.len() {
321        let s = base.add(i);
322        if (*s).used && (*s).mqd == mqd {
323            let sev = (*s).sev;
324            (*s).used = false;
325            aio_notify(&sev);
326            return;
327        }
328    }
329}
330
331// ============================================================
332// pthread introspection
333// ============================================================
334
335static mut PTHREAD_DEFAULT_STACK: usize = STACK_SIZE;
336static mut PTHREAD_DEFAULT_GUARD: usize = 4096;
337static mut PTHREAD_CONCURRENCY: c_int = 0;
338
339#[no_mangle]
340pub unsafe extern "C" fn pthread_getattr_np(thread: PthreadT, attr: *mut pthread_attr_t) -> c_int {
341    if attr.is_null() || thread == 0 { return EINVAL; }
342    let slot = thread as *mut Thread;
343    core::ptr::write_bytes(attr, 0, 1);
344    *((*attr).__i.as_mut_ptr() as *mut usize) = if (*slot).stack_size > 0 { (*slot).stack_size } else { PTHREAD_DEFAULT_STACK };
345    *((*attr).__i.as_mut_ptr().add(2) as *mut usize) = PTHREAD_DEFAULT_GUARD;
346    *((*attr).__i.as_mut_ptr().add(4) as *mut *mut c_void) = (*slot).stack as *mut c_void;
347    (*attr).__i[6] = if (*slot).detach_state == DT_DETACHED { 1 } else { 0 };
348    (*attr).__i[7] = 0;
349    0
350}
351
352#[no_mangle]
353pub unsafe extern "C" fn pthread_getattr_default_np(attr: *mut pthread_attr_t) -> c_int {
354    if attr.is_null() { return EINVAL; }
355    core::ptr::write_bytes(attr, 0, 1);
356    *((*attr).__i.as_mut_ptr() as *mut usize) = PTHREAD_DEFAULT_STACK;
357    *((*attr).__i.as_mut_ptr().add(2) as *mut usize) = PTHREAD_DEFAULT_GUARD;
358    (*attr).__i[6] = 0;
359    (*attr).__i[7] = 1;
360    0
361}
362
363#[no_mangle]
364pub unsafe extern "C" fn pthread_setattr_default_np(attr: *const pthread_attr_t) -> c_int {
365    if attr.is_null() { return EINVAL; }
366    let stack = *((*attr).__i.as_ptr() as *const usize);
367    let guard = *((*attr).__i.as_ptr().add(2) as *const usize);
368    // Reject attributes that cannot be made defaults.
369    if (*attr).__i[6] != 0 { return EINVAL; }
370    if stack > 0 {
371        if stack < 16384 { return EINVAL; }
372        PTHREAD_DEFAULT_STACK = stack;
373    }
374    if guard > 0 { PTHREAD_DEFAULT_GUARD = guard; }
375    0
376}
377
378#[no_mangle]
379pub unsafe extern "C" fn pthread_getconcurrency() -> c_int {
380    PTHREAD_CONCURRENCY
381}
382
383#[no_mangle]
384pub unsafe extern "C" fn pthread_setconcurrency(level: c_int) -> c_int {
385    if level < 0 { return EINVAL; }
386    PTHREAD_CONCURRENCY = level;
387    0
388}
389
390#[no_mangle]
391pub unsafe extern "C" fn pthread_getcpuclockid(thread: PthreadT, clockid: *mut i32) -> c_int {
392    if thread == 0 || clockid.is_null() { return EINVAL; }
393    let slot = thread as *mut Thread;
394    let tid = (*slot).tid as i64;
395    *clockid = (((-tid - 1) * 8 + 6) & 0xffffffff) as i32;
396    0
397}
398
399#[no_mangle]
400pub unsafe extern "C" fn pthread_setname_np(thread: PthreadT, name: *const c_char) -> c_int {
401    if thread == 0 || name.is_null() { return EINVAL; }
402    if crate::strnlen(name as *const u8, 16) > 15 { return 34; }
403    let slot = thread as *mut Thread;
404    if slot == pthread_self() as *mut Thread {
405        return if crate::prctl(15, name as c_long) < 0 { ERRNO } else { 0 };
406    }
407    let mut path = [0 as c_char; 48];
408    let mut o = 0usize;
409    for &c in b"/proc/self/task/" { path[o] = c as c_char; o += 1; }
410    let mut tid = (*slot).tid as u32;
411    let mut digits = [0u8; 10];
412    let mut d = 0usize;
413    if tid == 0 { digits[0] = b'0'; d = 1; }
414    while tid > 0 { digits[d] = b'0' + (tid % 10) as u8; tid /= 10; d += 1; }
415    while d > 0 { d -= 1; path[o] = digits[d] as c_char; o += 1; }
416    for &c in b"/comm\0" { path[o] = c as c_char; o += 1; }
417    let fd = crate::open(path.as_ptr(), 1, 0);
418    if fd < 0 { return ERRNO; }
419    let n = crate::strlen(name);
420    let w = crate::write(fd, name as *const c_void, n);
421    crate::close(fd);
422    if w < 0 { ERRNO } else { 0 }
423}
424
425#[no_mangle]
426pub unsafe extern "C" fn pthread_getname_np(thread: PthreadT, name: *mut c_char, len: usize) -> c_int {
427    if thread == 0 || name.is_null() { return EINVAL; }
428    if len < 16 { return 34; }
429    let slot = thread as *mut Thread;
430    if slot == pthread_self() as *mut Thread {
431        return if crate::prctl(16, name as c_long) < 0 { ERRNO } else { 0 };
432    }
433    let mut path = [0 as c_char; 48];
434    let mut o = 0usize;
435    for &c in b"/proc/self/task/" { path[o] = c as c_char; o += 1; }
436    let mut tid = (*slot).tid as u32;
437    let mut digits = [0u8; 10];
438    let mut d = 0usize;
439    if tid == 0 { digits[0] = b'0'; d = 1; }
440    while tid > 0 { digits[d] = b'0' + (tid % 10) as u8; tid /= 10; d += 1; }
441    while d > 0 { d -= 1; path[o] = digits[d] as c_char; o += 1; }
442    for &c in b"/comm\0" { path[o] = c as c_char; o += 1; }
443    let fd = crate::open(path.as_ptr(), 0, 0);
444    if fd < 0 { return ERRNO; }
445    let n = crate::read(fd, name as *mut c_void, len);
446    crate::close(fd);
447    if n < 0 { return ERRNO; }
448    let mut l = n as usize;
449    if l > 0 && *name.add(l - 1) == b'\n' as c_char { l -= 1; }
450    *name.add(l) = 0;
451    0
452}
453
454#[no_mangle]
455pub unsafe extern "C" fn pthread_getschedparam(thread: PthreadT, policy: *mut c_int, param: *mut sched_param) -> c_int {
456    if thread == 0 || policy.is_null() || param.is_null() { return EINVAL; }
457    let tid = (*(thread as *mut Thread)).tid;
458    *policy = crate::sched_getscheduler(tid);
459    crate::sched_getparam(tid, param as *mut c_void)
460}
461
462#[no_mangle]
463pub unsafe extern "C" fn pthread_setschedparam(thread: PthreadT, policy: c_int, param: *const sched_param) -> c_int {
464    if thread == 0 || param.is_null() { return EINVAL; }
465    let tid = (*(thread as *mut Thread)).tid;
466    crate::sched_setscheduler(tid, policy, param as *const c_void)
467}
468
469#[no_mangle]
470pub unsafe extern "C" fn pthread_setschedprio(thread: PthreadT, prio: c_int) -> c_int {
471    if thread == 0 { return EINVAL; }
472    let tid = (*(thread as *mut Thread)).tid;
473    let p = sched_param { sched_priority: prio };
474    crate::sched_setparam(tid, &p as *const sched_param as *const c_void)
475}
476
477#[no_mangle]
478pub unsafe extern "C" fn pthread_mutex_getprioceiling(mutex: *const pthread_mutex_t, ceiling: *mut c_int) -> c_int {
479    if mutex.is_null() || ceiling.is_null() { return EINVAL; }
480    *ceiling = 0;
481    0
482}
483
484#[no_mangle]
485pub unsafe extern "C" fn pthread_mutex_setprioceiling(mutex: *mut pthread_mutex_t, ceiling: c_int, old: *mut c_int) -> c_int {
486    if mutex.is_null() { return EINVAL; }
487    if !old.is_null() { *old = 0; }
488    let _ = ceiling;
489    0
490}
491
492// ============================================================
493// posix_spawn attributes and file actions
494// ============================================================
495
496
497#[repr(C)]
498pub struct posix_spawn_file_actions_t {
499    __pad0: [c_int; 2],
500    __actions: *mut c_void,
501    __pad: [c_int; 16],
502}
503
504#[repr(C)]
505#[derive(Copy, Clone)]
506pub struct posix_spawnattr_t {
507    __flags: c_int,
508    __pgrp: c_int,
509    __def: [c_ulong; 16],
510    __mask: [c_ulong; 16],
511    __prio: c_int,
512    __pol: c_int,
513    __fn: *mut c_void,
514    __pad: [u8; 64 - core::mem::size_of::<*mut c_void>()],
515}
516
517#[repr(C)]
518struct SpawnAction {
519    next: *mut SpawnAction,
520    kind: c_int,
521    fd: c_int,
522    newfd: c_int,
523    oflag: c_int,
524    mode: u32,
525    path: *mut c_char,
526    path_owned: bool,
527}
528
529static mut SPAWN_DEFAULT_ATTR: posix_spawnattr_t = posix_spawnattr_t {
530    __flags: 0,
531    __pgrp: 0,
532    __def: [0; 16],
533    __mask: [0; 16],
534    __prio: 0,
535    __pol: 0,
536    __fn: core::ptr::null_mut(),
537    __pad: [0; 64 - core::mem::size_of::<*mut c_void>()],
538};
539
540unsafe fn spawn_actions(fa: *const posix_spawn_file_actions_t) -> *mut SpawnAction {
541    (*fa).__actions as *mut SpawnAction
542}
543
544unsafe fn spawn_action_add(
545    fa: *mut posix_spawn_file_actions_t,
546    kind: c_int,
547    fd: c_int,
548    newfd: c_int,
549    oflag: c_int,
550    mode: u32,
551    path: *const c_char,
552) -> c_int {
553    let node = crate::calloc(1, core::mem::size_of::<SpawnAction>()) as *mut SpawnAction;
554    if node.is_null() { return ENOMEM; }
555    (*node).kind = kind;
556    (*node).fd = fd;
557    (*node).newfd = newfd;
558    (*node).oflag = oflag;
559    (*node).mode = mode;
560    (*node).next = core::ptr::null_mut();
561    if !path.is_null() {
562        let n = crate::strlen(path);
563        let p = crate::malloc(n + 1) as *mut c_char;
564        if p.is_null() { crate::free(node as *mut c_void); return ENOMEM; }
565        core::ptr::copy_nonoverlapping(path, p, n + 1);
566        (*node).path = p;
567        (*node).path_owned = true;
568    }
569    if (*fa).__actions.is_null() {
570        (*fa).__actions = node as *mut c_void;
571    } else {
572        let mut tail = (*fa).__actions as *mut SpawnAction;
573        while !(*tail).next.is_null() { tail = (*tail).next; }
574        (*tail).next = node;
575    }
576    0
577}
578
579#[no_mangle]
580pub unsafe extern "C" fn posix_spawn_file_actions_init(fa: *mut posix_spawn_file_actions_t) -> c_int {
581    if fa.is_null() { return EINVAL; }
582    core::ptr::write_bytes(fa, 0, 1);
583    0
584}
585
586#[no_mangle]
587pub unsafe extern "C" fn posix_spawn_file_actions_destroy(fa: *mut posix_spawn_file_actions_t) -> c_int {
588    if fa.is_null() { return EINVAL; }
589    let mut p = spawn_actions(fa);
590    while !p.is_null() {
591        let next = (*p).next;
592        if (*p).path_owned && !(*p).path.is_null() { crate::free((*p).path as *mut c_void); }
593        crate::free(p as *mut c_void);
594        p = next;
595    }
596    (*fa).__actions = core::ptr::null_mut();
597    0
598}
599
600#[no_mangle]
601pub unsafe extern "C" fn posix_spawn_file_actions_addclose(fa: *mut posix_spawn_file_actions_t, fd: c_int) -> c_int {
602    if fa.is_null() || fd < 0 { return EINVAL; }
603    spawn_action_add(fa, 0, fd, 0, 0, 0, core::ptr::null())
604}
605
606#[no_mangle]
607pub unsafe extern "C" fn posix_spawn_file_actions_adddup2(fa: *mut posix_spawn_file_actions_t, fd: c_int, newfd: c_int) -> c_int {
608    if fa.is_null() || fd < 0 || newfd < 0 { return EINVAL; }
609    spawn_action_add(fa, 1, fd, newfd, 0, 0, core::ptr::null())
610}
611
612#[no_mangle]
613pub unsafe extern "C" fn posix_spawn_file_actions_addopen(
614    fa: *mut posix_spawn_file_actions_t,
615    fd: c_int,
616    path: *const c_char,
617    oflag: c_int,
618    mode: u32,
619) -> c_int {
620    if fa.is_null() || fd < 0 || path.is_null() { return EINVAL; }
621    spawn_action_add(fa, 2, fd, 0, oflag, mode, path)
622}
623
624#[no_mangle]
625pub unsafe extern "C" fn posix_spawn_file_actions_addchdir_np(fa: *mut posix_spawn_file_actions_t, path: *const c_char) -> c_int {
626    if fa.is_null() || path.is_null() { return EINVAL; }
627    spawn_action_add(fa, 3, 0, 0, 0, 0, path)
628}
629
630#[no_mangle]
631pub unsafe extern "C" fn posix_spawn_file_actions_addfchdir_np(fa: *mut posix_spawn_file_actions_t, fd: c_int) -> c_int {
632    if fa.is_null() || fd < 0 { return EINVAL; }
633    spawn_action_add(fa, 4, fd, 0, 0, 0, core::ptr::null())
634}
635
636unsafe fn spawn_apply_actions(fa: *const posix_spawn_file_actions_t) {
637    if fa.is_null() { return; }
638    let mut p = spawn_actions(fa);
639    while !p.is_null() {
640        match (*p).kind {
641            0 => { crate::close((*p).fd); }
642            1 => { crate::dup2((*p).fd, (*p).newfd); }
643            2 => {
644                let fd = crate::open((*p).path, (*p).oflag, (*p).mode as c_int);
645                if fd >= 0 && fd != (*p).fd {
646                    crate::dup2(fd, (*p).fd);
647                    crate::close(fd);
648                }
649            }
650            3 => { crate::chdir((*p).path); }
651            4 => { crate::fchdir((*p).fd); }
652            _ => {}
653        }
654        p = (*p).next;
655    }
656}
657
658unsafe fn spawn_apply_attr(attr: *const posix_spawnattr_t) {
659    if attr.is_null() { return; }
660    let flags = (*attr).__flags;
661    if flags & 1 != 0 {
662        if crate::geteuid() == 0 {
663            crate::setgid(crate::getgid());
664            crate::setuid(crate::getuid());
665        }
666    }
667    if flags & 2 != 0 { crate::setpgid(0, (*attr).__pgrp); }
668    if flags & 4 != 0 {
669        for sig in 1..65 {
670            let idx = (sig - 1) / 64;
671            let bit = (sig - 1) % 64;
672            if idx < 16 && ((*attr).__def[idx] >> bit) & 1 != 0 {
673                crate::signal(sig as c_int, 0);
674            }
675        }
676    }
677    if flags & 8 != 0 {
678        crate::sigprocmask(2, (*attr).__mask.as_ptr() as *const SigSetT, core::ptr::null_mut());
679    }
680    if flags & 16 != 0 {
681        let p = sched_param { sched_priority: (*attr).__prio };
682        crate::sched_setparam(0, &p as *const sched_param as *const c_void);
683    }
684    if flags & 32 != 0 {
685        let p = sched_param { sched_priority: (*attr).__prio };
686        crate::sched_setscheduler(0, (*attr).__pol, &p as *const sched_param as *const c_void);
687    }
688}
689
690#[no_mangle]
691pub unsafe extern "C" fn posix_spawnattr_init(attr: *mut posix_spawnattr_t) -> c_int {
692    if attr.is_null() { return EINVAL; }
693    *attr = SPAWN_DEFAULT_ATTR;
694    0
695}
696
697#[no_mangle]
698pub unsafe extern "C" fn posix_spawnattr_destroy(attr: *mut posix_spawnattr_t) -> c_int {
699    if attr.is_null() { return EINVAL; }
700    0
701}
702
703#[no_mangle]
704pub unsafe extern "C" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *mut i16) -> c_int {
705    if attr.is_null() || flags.is_null() { return EINVAL; }
706    *flags = (*attr).__flags as i16;
707    0
708}
709
710#[no_mangle]
711pub unsafe extern "C" fn posix_spawnattr_setflags(attr: *mut posix_spawnattr_t, flags: i16) -> c_int {
712    if attr.is_null() { return EINVAL; }
713    if (flags as c_int) & !0x7f != 0 { return EINVAL; }
714    (*attr).__flags = flags as c_int;
715    0
716}
717
718#[no_mangle]
719pub unsafe extern "C" fn posix_spawnattr_getpgroup(attr: *const posix_spawnattr_t, pgroup: *mut c_int) -> c_int {
720    if attr.is_null() || pgroup.is_null() { return EINVAL; }
721    *pgroup = (*attr).__pgrp;
722    0
723}
724
725#[no_mangle]
726pub unsafe extern "C" fn posix_spawnattr_setpgroup(attr: *mut posix_spawnattr_t, pgroup: c_int) -> c_int {
727    if attr.is_null() { return EINVAL; }
728    (*attr).__pgrp = pgroup;
729    0
730}
731
732#[no_mangle]
733pub unsafe extern "C" fn posix_spawnattr_getschedparam(attr: *const posix_spawnattr_t, param: *mut sched_param) -> c_int {
734    if attr.is_null() || param.is_null() { return EINVAL; }
735    (*param).sched_priority = (*attr).__prio;
736    0
737}
738
739#[no_mangle]
740pub unsafe extern "C" fn posix_spawnattr_setschedparam(attr: *mut posix_spawnattr_t, param: *const sched_param) -> c_int {
741    if attr.is_null() || param.is_null() { return EINVAL; }
742    (*attr).__prio = (*param).sched_priority;
743    0
744}
745
746#[no_mangle]
747pub unsafe extern "C" fn posix_spawnattr_getschedpolicy(attr: *const posix_spawnattr_t, policy: *mut c_int) -> c_int {
748    if attr.is_null() || policy.is_null() { return EINVAL; }
749    *policy = (*attr).__pol;
750    0
751}
752
753#[no_mangle]
754pub unsafe extern "C" fn posix_spawnattr_setschedpolicy(attr: *mut posix_spawnattr_t, policy: c_int) -> c_int {
755    if attr.is_null() { return EINVAL; }
756    (*attr).__pol = policy;
757    0
758}
759
760#[no_mangle]
761pub unsafe extern "C" fn posix_spawnattr_getsigdefault(attr: *const posix_spawnattr_t, set: *mut SigSetT) -> c_int {
762    if attr.is_null() || set.is_null() { return EINVAL; }
763    *set = (*attr).__def[0];
764    0
765}
766
767#[no_mangle]
768pub unsafe extern "C" fn posix_spawnattr_setsigdefault(attr: *mut posix_spawnattr_t, set: *const SigSetT) -> c_int {
769    if attr.is_null() || set.is_null() { return EINVAL; }
770    (*attr).__def = [0; 16];
771    (*attr).__def[0] = *set;
772    0
773}
774
775#[no_mangle]
776pub unsafe extern "C" fn posix_spawnattr_getsigmask(attr: *const posix_spawnattr_t, set: *mut SigSetT) -> c_int {
777    if attr.is_null() || set.is_null() { return EINVAL; }
778    *set = (*attr).__mask[0];
779    0
780}
781
782#[no_mangle]
783pub unsafe extern "C" fn posix_spawnattr_setsigmask(attr: *mut posix_spawnattr_t, set: *const SigSetT) -> c_int {
784    if attr.is_null() || set.is_null() { return EINVAL; }
785    (*attr).__mask = [0; 16];
786    (*attr).__mask[0] = *set;
787    0
788}
789
790// ============================================================
791// stdio internals
792// ============================================================
793
794#[no_mangle]
795pub unsafe extern "C" fn __fbufsize(f: *mut FILE) -> usize {
796    if f.is_null() { return 0; }
797    (*f).buf_size
798}
799
800#[no_mangle]
801pub unsafe extern "C" fn __flbf(f: *mut FILE) -> c_int {
802    if f.is_null() { return 0; }
803    if (*f).lbf >= 0 { 1 } else { 0 }
804}
805
806#[no_mangle]
807pub unsafe extern "C" fn __fpending(f: *mut FILE) -> usize {
808    if f.is_null() || (*f).wend.is_null() { return 0; }
809    (*f).wpos as usize - (*f).wbase as usize
810}
811
812#[no_mangle]
813pub unsafe extern "C" fn __fpurge(f: *mut FILE) {
814    if f.is_null() { return; }
815    (*f).wpos = core::ptr::null_mut();
816    (*f).wbase = core::ptr::null_mut();
817    (*f).wend = core::ptr::null_mut();
818    (*f).rpos = core::ptr::null_mut();
819    (*f).rend = core::ptr::null_mut();
820}
821
822#[no_mangle]
823pub unsafe extern "C" fn __freadable(f: *mut FILE) -> c_int {
824    if f.is_null() { return 0; }
825    if (*f).flags & F_NORD == 0 { 1 } else { 0 }
826}
827
828#[no_mangle]
829pub unsafe extern "C" fn __freadahead(f: *mut FILE) -> usize {
830    if f.is_null() || (*f).rend.is_null() { return 0; }
831    (*f).rend as usize - (*f).rpos as usize
832}
833
834#[no_mangle]
835pub unsafe extern "C" fn __freading(f: *mut FILE) -> c_int {
836    if f.is_null() { return 0; }
837    if (*f).flags & F_NOWR != 0 || !(*f).rend.is_null() { 1 } else { 0 }
838}
839
840#[no_mangle]
841pub unsafe extern "C" fn __freadptr(f: *mut FILE, sizep: *mut usize) -> *const c_char {
842    if f.is_null() || sizep.is_null() { return core::ptr::null(); }
843    if (*f).rpos == (*f).rend { *sizep = 0; return core::ptr::null(); }
844    *sizep = (*f).rend as usize - (*f).rpos as usize;
845    (*f).rpos as *const c_char
846}
847
848#[no_mangle]
849pub unsafe extern "C" fn __freadptrinc(f: *mut FILE, inc: usize) {
850    if f.is_null() || (*f).rpos.is_null() { return; }
851    let left = (*f).rend as usize - (*f).rpos as usize;
852    let step = if inc > left { left } else { inc };
853    (*f).rpos = (*f).rpos.add(step);
854}
855
856#[no_mangle]
857pub unsafe extern "C" fn __fsetlocking(_f: *mut FILE, _type: c_int) -> c_int {
858    1
859}
860
861#[no_mangle]
862pub unsafe extern "C" fn __fwritable(f: *mut FILE) -> c_int {
863    if f.is_null() { return 0; }
864    if (*f).flags & F_NOWR == 0 { 1 } else { 0 }
865}
866
867#[no_mangle]
868pub unsafe extern "C" fn __fwriting(f: *mut FILE) -> c_int {
869    if f.is_null() { return 0; }
870    if (*f).flags & F_NORD != 0 || !(*f).wend.is_null() { 1 } else { 0 }
871}
872
873#[no_mangle]
874pub unsafe extern "C" fn __fseterr(f: *mut FILE) {
875    if !f.is_null() { (*f).flags |= F_ERR; }
876}
877
878#[no_mangle]
879pub unsafe extern "C" fn __fputwc_unlocked(c: wchar_t, f: *mut FILE) -> wint_t {
880    crate::fputwc(c, f)
881}
882
883// ============================================================
884// Miscellaneous legacy calls
885// ============================================================
886
887#[no_mangle]
888pub unsafe extern "C" fn sigqueue(pid: c_int, sig: c_int, value: *mut c_void) -> c_int {
889    let mut si = [0u8; 128];
890    *(si.as_mut_ptr() as *mut c_int) = sig;
891    *(si.as_mut_ptr().add(8) as *mut c_int) = -1; // SI_QUEUE
892    *(si.as_mut_ptr().add(16) as *mut c_int) = crate::getpid();
893    *(si.as_mut_ptr().add(20) as *mut u32) = crate::getuid();
894    *(si.as_mut_ptr().add(24) as *mut *mut c_void) = value;
895    let r = <Arch as Syscalls>::syscall3(sysno(129, 138), pid as i64, sig as i64, si.as_ptr() as i64);
896    if r < 0 { ERRNO = (-r) as c_int; -1 } else { 0 }
897}
898
899#[no_mangle]
900pub unsafe extern "C" fn timespec_get(ts: *mut timespec, base: c_int) -> c_int {
901    if base != 1 || ts.is_null() { return 0; }
902    if crate::clock_gettime(0, ts) != 0 { return 0; }
903    base
904}
905
906#[no_mangle]
907pub unsafe extern "C" fn ualarm(usecs: u32, interval: u32) -> u32 {
908    #[repr(C)]
909    struct ITV {
910        sec: i64,
911        usec: i64,
912    }
913    let mut new: [ITV; 2] = [
914        ITV { sec: (interval / 1_000_000) as i64, usec: (interval % 1_000_000) as i64 },
915        ITV { sec: (usecs / 1_000_000) as i64, usec: (usecs % 1_000_000) as i64 },
916    ];
917    let mut old: [ITV; 2] = [ITV { sec: 0, usec: 0 }, ITV { sec: 0, usec: 0 }];
918    if crate::setitimer(0, new.as_mut_ptr() as *const c_void, old.as_mut_ptr() as *mut c_void) != 0 {
919        return u32::MAX;
920    }
921    (old[0].sec as u32) * 1_000_000 + old[0].usec as u32
922}
923
924static mut LCKPWDF_FD: c_int = -1;
925
926#[repr(C)]
927#[derive(Copy, Clone)]
928struct FlockT {
929    l_type: i16,
930    l_whence: i16,
931    l_start: i64,
932    l_len: i64,
933    l_pid: i32,
934    __pad: i32,
935}
936
937#[no_mangle]
938pub unsafe extern "C" fn lckpwdf() -> c_int {
939    if LCKPWDF_FD >= 0 { return 0; }
940    let fd = crate::open(
941        b"/etc/.pwd.lock\0".as_ptr() as *const c_char,
942        1 | 0x40 | 0x80000,
943        0o600,
944    );
945    if fd < 0 { return -1; }
946    let fl = FlockT { l_type: 1, l_whence: 0, l_start: 0, l_len: 0, l_pid: 0, __pad: 0 };
947    if crate::fcntl(fd, 7, &fl as *const FlockT as *mut c_void) != 0 {
948        crate::close(fd);
949        return -1;
950    }
951    LCKPWDF_FD = fd;
952    0
953}
954
955#[no_mangle]
956pub unsafe extern "C" fn ulckpwdf() -> c_int {
957    if LCKPWDF_FD < 0 { return -1; }
958    let fd = LCKPWDF_FD;
959    LCKPWDF_FD = -1;
960    crate::close(fd);
961    0
962}
963
964#[no_mangle]
965pub unsafe extern "C" fn malloc_usable_size(ptr: *mut u8) -> usize {
966    if ptr.is_null() { return 0; }
967    let header = ptr.sub(core::mem::size_of::<usize>());
968    let pages = *(header as *const usize);
969    if pages < core::mem::size_of::<usize>() { return 0; }
970    pages - core::mem::size_of::<usize>()
971}
972
973#[no_mangle]
974pub unsafe extern "C" fn __wcsftime_l(
975    s: *mut wchar_t,
976    max: usize,
977    fmt: *const wchar_t,
978    tm: *const c_void,
979    _loc: locale_t,
980) -> usize {
981    crate::wcsftime(s as *mut u32, max, fmt as *const u32, tm)
982}
983
984#[no_mangle]
985pub unsafe extern "C" fn __wctype_l(name: *const c_char, _loc: locale_t) -> wctype_t {
986    crate::wctype(name)
987}
988
989#[no_mangle]
990pub unsafe extern "C" fn __wctrans_l(name: *const c_char, _loc: locale_t) -> wctrans_t {
991    crate::wctrans(name)
992}
993
994#[no_mangle]
995pub unsafe extern "C" fn __fgetwc_unlocked(f: *mut FILE) -> wint_t {
996    crate::fgetwc(f)
997}
998
999#[no_mangle]
1000pub unsafe extern "C" fn _flushlbf() {
1001    crate::fflush(core::ptr::null_mut());
1002}