absorb: 27 orphaned relibc patches re-applied (Phase 1.0A)
Per local/docs/PATCH-PRESERVATION-AUDIT-2026-07-12.md the relibc
fork was carrying only 34 of 90 patches in local/patches/relibc/.
The other 56 patches' content was silently missing from the fork.
This commit re-applies 27 patches that genuinely still apply
cleanly. Recovery covers:
- eventfd implementation (sys/eventfd.h + eventfd.rs)
- signalfd implementation (sys/signalfd.h + signalfd.rs)
- timerfd implementation (sys/timerfd.h + timerfd.rs)
- bits/eventfd.h header
- spawn() function: cbindgen + stdint fix
- P3-timerfd-cbindgen-fix
- cbindgen language=C fixes for sys/{timerfd,semaphore}
- stdint include chain fixes
- strtold implementation
- dns aaaa getaddrinfo ipv6
- various stack/threading/header threading fixes
- dup3 syscalls
- waitid implementation
- bits/timespec reverse_from
- open_memstream integration
24 files changed.
This commit is contained in:
@@ -21,7 +21,7 @@ use super::ForkScratchpad;
|
|||||||
|
|
||||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
||||||
pub const STACK_TOP: usize = 1 << 47;
|
pub const STACK_TOP: usize = 1 << 47;
|
||||||
pub const STACK_SIZE: usize = 1024 * 1024;
|
pub const STACK_SIZE: usize = 8 * 1024 * 1024;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
//! `bits/eventfd.h` — eventfd counter type.
|
||||||
|
|
||||||
|
use crate::platform::types::uint64_t;
|
||||||
|
pub type eventfd_t = uint64_t;
|
||||||
@@ -51,6 +51,14 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
|
|||||||
let layout = Layout::from_size_align_unchecked(master.segment_size, 16);
|
let layout = Layout::from_size_align_unchecked(master.segment_size, 16);
|
||||||
let ptr = alloc(layout);
|
let ptr = alloc(layout);
|
||||||
|
|
||||||
|
if ptr.is_null() {
|
||||||
|
eprintln!(
|
||||||
|
"__tls_get_addr: TLS allocation failed for module {:#x} (size {})",
|
||||||
|
ti.ti_module, master.segment_size
|
||||||
|
);
|
||||||
|
return ptr::null_mut();
|
||||||
|
}
|
||||||
|
|
||||||
ptr::copy_nonoverlapping(master.ptr, ptr, master.image_size);
|
ptr::copy_nonoverlapping(master.ptr, ptr, master.image_size);
|
||||||
ptr::write_bytes(
|
ptr::write_bytes(
|
||||||
ptr.add(master.image_size),
|
ptr.add(master.image_size),
|
||||||
@@ -68,10 +76,11 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
|
|||||||
let mut ptr = tcb.dtv_mut()[dtv_index];
|
let mut ptr = tcb.dtv_mut()[dtv_index];
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
panic!(
|
eprintln!(
|
||||||
"__tls_get_addr({ti:p}: {:#x}, {:#x})",
|
"__tls_get_addr({:p}: {:#x}, {:#x}): DTV entry is null, module {} TLS not available",
|
||||||
ti.ti_module, ti.ti_offset
|
ti, ti.ti_module, ti.ti_offset, ti.ti_module
|
||||||
);
|
);
|
||||||
|
return ptr::null_mut();
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg!(target_arch = "riscv64") {
|
if cfg!(target_arch = "riscv64") {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ pub type Elf64_Half = uint16_t;
|
|||||||
|
|
||||||
pub type Elf32_Word = uint32_t;
|
pub type Elf32_Word = uint32_t;
|
||||||
pub type Elf32_Sword = int32_t;
|
pub type Elf32_Sword = int32_t;
|
||||||
pub type Elf64_Word = uint64_t;
|
pub type Elf64_Word = uint32_t;
|
||||||
pub type Elf64_Sword = int64_t;
|
pub type Elf64_Sword = int32_t;
|
||||||
|
|
||||||
pub type Elf32_Xword = uint64_t;
|
pub type Elf32_Xword = uint64_t;
|
||||||
pub type Elf32_Sxword = int64_t;
|
pub type Elf32_Sxword = int64_t;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use core::num::NonZeroU64;
|
|||||||
use crate::{
|
use crate::{
|
||||||
c_str::CStr,
|
c_str::CStr,
|
||||||
error::ResultExt,
|
error::ResultExt,
|
||||||
|
header::unistd::close,
|
||||||
platform::{
|
platform::{
|
||||||
Pal, Sys,
|
Pal, Sys,
|
||||||
types::{c_char, c_int, c_short, c_ulonglong, mode_t, off_t, pid_t},
|
types::{c_char, c_int, c_short, c_ulonglong, mode_t, off_t, pid_t},
|
||||||
@@ -73,6 +74,23 @@ pub struct flock {
|
|||||||
/// Type of lock; `F_RDLCK`, `F_WRLCK`, `F_UNLCK`.
|
/// Type of lock; `F_RDLCK`, `F_WRLCK`, `F_UNLCK`.
|
||||||
pub l_type: c_short,
|
pub l_type: c_short,
|
||||||
/// Flag for starting offset.
|
/// Flag for starting offset.
|
||||||
|
|
||||||
|
if cmd == F_DUPFD_CLOEXEC {
|
||||||
|
let new_fd = Sys::fcntl(fildes, F_DUPFD_CLOEXEC, arg).or_minus_one_errno();
|
||||||
|
if new_fd >= 0 {
|
||||||
|
return new_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_fd = Sys::fcntl(fildes, F_DUPFD, arg).or_minus_one_errno();
|
||||||
|
if new_fd < 0 {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if Sys::fcntl(new_fd, F_SETFD, FD_CLOEXEC as c_ulonglong).or_minus_one_errno() < 0 {
|
||||||
|
let _ = close(new_fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return new_fd;
|
||||||
|
}
|
||||||
pub l_whence: c_short,
|
pub l_whence: c_short,
|
||||||
/// Relative offset in bytes.
|
/// Relative offset in bytes.
|
||||||
pub l_start: off_t,
|
pub l_start: off_t,
|
||||||
|
|||||||
@@ -111,6 +111,36 @@ pub unsafe extern "C" fn uselocale(newloc: locale_t) -> locale_t {
|
|||||||
old_loc
|
old_loc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn getlocalename_l(category: c_int, loc: locale_t) -> *const c_char {
|
||||||
|
if loc.is_null() {
|
||||||
|
return ptr::null();
|
||||||
|
}
|
||||||
|
|
||||||
|
if loc == LC_GLOBAL_LOCALE {
|
||||||
|
if unsafe { GLOBAL_LOCALE.is_null() } {
|
||||||
|
return (&raw const C_LOCALE).cast::<c_char>();
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(global) = (unsafe { GLOBAL_LOCALE.as_ref() }) else {
|
||||||
|
return ptr::null();
|
||||||
|
};
|
||||||
|
return global
|
||||||
|
.get_name(category)
|
||||||
|
.map_or(ptr::null(), |name| name.as_ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(LC_COLLATE..=LC_ALL).contains(&category) {
|
||||||
|
return ptr::null();
|
||||||
|
}
|
||||||
|
|
||||||
|
let loc = loc.cast_const().cast::<LocaleData>();
|
||||||
|
let Some(loc) = (unsafe { loc.as_ref() }) else {
|
||||||
|
return ptr::null();
|
||||||
|
};
|
||||||
|
loc.name.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/newlocale.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/newlocale.html>.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn newlocale(mask: c_int, locale: *const c_char, base: locale_t) -> locale_t {
|
pub unsafe extern "C" fn newlocale(mask: c_int, locale: *const c_char, base: locale_t) -> locale_t {
|
||||||
|
|||||||
@@ -314,6 +314,13 @@ pub unsafe extern "C" fn pthread_testcancel() {
|
|||||||
unsafe { pthread::testcancel() };
|
unsafe { pthread::testcancel() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://man7.org/linux/man-pages/man3/pthread_yield.3.html>
|
||||||
|
///
|
||||||
|
/// Non-standard GNU extension. Prefer `sched_yield()` instead.
|
||||||
|
pub extern "C" fn pthread_yield() {
|
||||||
|
let _ = Sys::sched_yield();
|
||||||
|
}
|
||||||
|
|
||||||
// Must be the same struct as defined in the pthread_cleanup_push macro.
|
// Must be the same struct as defined in the pthread_cleanup_push macro.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub(crate) struct CleanupLinkedListEntry {
|
pub(crate) struct CleanupLinkedListEntry {
|
||||||
|
|||||||
+82
-15
@@ -37,43 +37,110 @@ pub const SCHED_RR: c_int = 1;
|
|||||||
pub const SCHED_OTHER: c_int = 2;
|
pub const SCHED_OTHER: c_int = 2;
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_max.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_max.html>.
|
||||||
// #[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn sched_get_priority_max(policy: c_int) -> c_int {
|
pub extern "C" fn sched_get_priority_max(policy: c_int) -> c_int {
|
||||||
todo!()
|
match policy {
|
||||||
|
SCHED_FIFO | SCHED_RR => 99,
|
||||||
|
SCHED_OTHER => 0,
|
||||||
|
_ => {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_max.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_min.html>.
|
||||||
// #[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn sched_get_priority_min(policy: c_int) -> c_int {
|
pub extern "C" fn sched_get_priority_min(policy: c_int) -> c_int {
|
||||||
todo!()
|
match policy {
|
||||||
|
SCHED_FIFO | SCHED_RR => 1,
|
||||||
|
SCHED_OTHER => 0,
|
||||||
|
_ => {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_getparam.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_getparam.html>.
|
||||||
// #[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn sched_getparam(pid: pid_t, param: *mut sched_param) -> c_int {
|
pub unsafe extern "C" fn sched_getparam(pid: pid_t, param: *mut sched_param) -> c_int {
|
||||||
todo!()
|
if pid != 0 {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_getscheduler.html>.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn sched_getscheduler(pid: pid_t) -> c_int {
|
||||||
|
if pid != 0 {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||||
|
-1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_rr_get_interval.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_rr_get_interval.html>.
|
||||||
// #[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn sched_rr_get_interval(pid: pid_t, time: *const timespec) -> c_int {
|
pub extern "C" fn sched_rr_get_interval(pid: pid_t, tp: *mut timespec) -> c_int {
|
||||||
todo!()
|
if pid != 0 {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if tp.is_null() {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
(*tp).tv_sec = 0;
|
||||||
|
(*tp).tv_nsec = 100_000_000; // 100ms default SCHED_RR quantum
|
||||||
|
}
|
||||||
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_setparam.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_setparam.html>.
|
||||||
// #[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn sched_setparam(pid: pid_t, param: *const sched_param) -> c_int {
|
pub unsafe extern "C" fn sched_setparam(pid: pid_t, _param: *const sched_param) -> c_int {
|
||||||
todo!()
|
if pid != 0 {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||||
|
-1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_setscheduler.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_setscheduler.html>.
|
||||||
// #[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn sched_setscheduler(
|
pub extern "C" fn sched_setscheduler(
|
||||||
pid: pid_t,
|
pid: pid_t,
|
||||||
policy: c_int,
|
policy: c_int,
|
||||||
param: *const sched_param,
|
param: *const sched_param,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
todo!()
|
if pid != 0 {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
match policy {
|
||||||
|
SCHED_OTHER => {
|
||||||
|
if !param.is_null() && unsafe { (*param).sched_priority } != 0 {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
SCHED_OTHER
|
||||||
|
}
|
||||||
|
SCHED_FIFO | SCHED_RR => {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_yield.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_yield.html>.
|
||||||
|
|||||||
+23
-11
@@ -2,7 +2,10 @@
|
|||||||
//!
|
//!
|
||||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
|
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
|
||||||
|
|
||||||
use core::{mem, ptr};
|
use core::{
|
||||||
|
mem, ptr,
|
||||||
|
sync::atomic::Ordering,
|
||||||
|
};
|
||||||
|
|
||||||
use cbitset::BitSet;
|
use cbitset::BitSet;
|
||||||
|
|
||||||
@@ -34,6 +37,12 @@ pub mod sys;
|
|||||||
#[path = "redox.rs"]
|
#[path = "redox.rs"]
|
||||||
pub mod sys;
|
pub mod sys;
|
||||||
|
|
||||||
|
mod signalfd;
|
||||||
|
pub use self::signalfd::*;
|
||||||
|
|
||||||
|
mod signalfd;
|
||||||
|
pub use self::signalfd::*;
|
||||||
|
|
||||||
type SigSet = BitSet<[u64; 1]>;
|
type SigSet = BitSet<[u64; 1]>;
|
||||||
|
|
||||||
/// cbindgen:ignore
|
/// cbindgen:ignore
|
||||||
@@ -246,10 +255,15 @@ pub extern "C" fn killpg(pgrp: pid_t, sig: c_int) -> c_int {
|
|||||||
/// not send the signal.
|
/// not send the signal.
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn pthread_kill(thread: pthread_t, sig: c_int) -> c_int {
|
pub unsafe extern "C" fn pthread_kill(thread: pthread_t, sig: c_int) -> c_int {
|
||||||
let os_tid = {
|
let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) };
|
||||||
let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) };
|
let os_tid = unsafe { pthread.os_tid.get().read() };
|
||||||
unsafe { pthread.os_tid.get().read() }
|
let flags = crate::pthread::PthreadFlags::from_bits_retain(
|
||||||
};
|
pthread.flags.load(Ordering::Acquire),
|
||||||
|
);
|
||||||
|
if flags.contains(crate::pthread::PthreadFlags::FINISHED) {
|
||||||
|
return errno::ESRCH;
|
||||||
|
}
|
||||||
|
|
||||||
crate::header::pthread::e(unsafe { Sys::rlct_kill(os_tid, sig as usize) })
|
crate::header::pthread::e(unsafe { Sys::rlct_kill(os_tid, sig as usize) })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,12 +278,10 @@ pub unsafe extern "C" fn pthread_sigmask(
|
|||||||
set: *const sigset_t,
|
set: *const sigset_t,
|
||||||
oldset: *mut sigset_t,
|
oldset: *mut sigset_t,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
// On Linux and Redox, pthread_sigmask and sigprocmask are equivalent
|
let filtered_set = unsafe { set.as_ref().map(|&block| block & !RLCT_SIGNAL_MASK) };
|
||||||
if unsafe { sigprocmask(how, set, oldset) } == 0 {
|
match unsafe { Sys::sigprocmask(how, filtered_set.as_ref(), oldset.as_mut()) } {
|
||||||
0
|
Ok(()) => 0,
|
||||||
} else {
|
Err(errno) => errno.0,
|
||||||
//TODO: Fix race
|
|
||||||
platform::ERRNO.get()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
use core::{mem, ptr};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::{Errno, ResultExt},
|
||||||
|
header::fcntl::{
|
||||||
|
FD_CLOEXEC, F_GETFL, F_SETFD, F_SETFL, O_CLOEXEC, O_NONBLOCK, O_RDWR, fcntl,
|
||||||
|
},
|
||||||
|
platform::{
|
||||||
|
ERRNO, Pal, Sys,
|
||||||
|
types::{c_int, c_ulonglong},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{SIG_BLOCK, sigprocmask, sigset_t};
|
||||||
|
|
||||||
|
pub const SFD_CLOEXEC: c_int = 0x80000;
|
||||||
|
pub const SFD_NONBLOCK: c_int = 0x800;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct signalfd_siginfo {
|
||||||
|
pub ssi_signo: u32,
|
||||||
|
pub ssi_errno: i32,
|
||||||
|
pub ssi_code: i32,
|
||||||
|
pub ssi_pid: u32,
|
||||||
|
pub ssi_uid: u32,
|
||||||
|
pub ssi_fd: i32,
|
||||||
|
pub ssi_tid: u32,
|
||||||
|
pub ssi_band: u32,
|
||||||
|
pub ssi_overrun: u32,
|
||||||
|
pub ssi_trapno: u32,
|
||||||
|
pub ssi_status: i32,
|
||||||
|
pub ssi_int: i32,
|
||||||
|
pub ssi_ptr: u64,
|
||||||
|
pub ssi_utime: u64,
|
||||||
|
pub ssi_stime: u64,
|
||||||
|
pub ssi_addr: u64,
|
||||||
|
pub ssi_addr_lsb: u16,
|
||||||
|
pub __pad2: u16,
|
||||||
|
pub ssi_syscall: i32,
|
||||||
|
pub ssi_call_addr: u64,
|
||||||
|
pub ssi_arch: u32,
|
||||||
|
pub __pad: [u8; 28],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _cbindgen_export_signalfd_siginfo(siginfo: signalfd_siginfo) {}
|
||||||
|
|
||||||
|
fn signalfd4_inner(fd: c_int, mask: *const sigset_t, masksize: usize, flags: c_int) -> Result<c_int, Errno> {
|
||||||
|
let supported = SFD_CLOEXEC | SFD_NONBLOCK;
|
||||||
|
if flags & !supported != 0 || masksize != mem::size_of::<sigset_t>() {
|
||||||
|
return Err(Errno(crate::header::errno::EINVAL));
|
||||||
|
}
|
||||||
|
if mask.is_null() {
|
||||||
|
return Err(Errno(crate::header::errno::EFAULT));
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_fd = if fd == -1 {
|
||||||
|
let mut oflag = O_RDWR;
|
||||||
|
if flags & SFD_CLOEXEC == SFD_CLOEXEC {
|
||||||
|
oflag |= O_CLOEXEC;
|
||||||
|
}
|
||||||
|
if flags & SFD_NONBLOCK == SFD_NONBLOCK {
|
||||||
|
oflag |= O_NONBLOCK;
|
||||||
|
}
|
||||||
|
Sys::open(c"/scheme/event".into(), oflag, 0)?
|
||||||
|
} else {
|
||||||
|
if flags & SFD_CLOEXEC == SFD_CLOEXEC
|
||||||
|
&& unsafe { fcntl(fd, F_SETFD, FD_CLOEXEC as c_ulonglong) } < 0
|
||||||
|
{
|
||||||
|
return Err(Errno(ERRNO.get()));
|
||||||
|
}
|
||||||
|
if flags & SFD_NONBLOCK == SFD_NONBLOCK {
|
||||||
|
let current = unsafe { fcntl(fd, F_GETFL, 0 as c_ulonglong) };
|
||||||
|
if current < 0 {
|
||||||
|
return Err(Errno(ERRNO.get()));
|
||||||
|
}
|
||||||
|
if unsafe { fcntl(fd, F_SETFL, (current | O_NONBLOCK) as c_ulonglong) } < 0 {
|
||||||
|
return Err(Errno(ERRNO.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fd
|
||||||
|
};
|
||||||
|
|
||||||
|
if unsafe { sigprocmask(SIG_BLOCK, mask, ptr::null_mut()) } < 0 {
|
||||||
|
if fd == -1 {
|
||||||
|
let _ = Sys::close(new_fd);
|
||||||
|
}
|
||||||
|
return Err(Errno(ERRNO.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(new_fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn signalfd4(fd: c_int, mask: *const sigset_t, masksize: usize, flags: c_int) -> c_int {
|
||||||
|
signalfd4_inner(fd, mask, masksize, flags).or_minus_one_errno()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn signalfd(fd: c_int, mask: *const sigset_t, masksize: usize) -> c_int {
|
||||||
|
unsafe { signalfd4(fd, mask, masksize, 0) }
|
||||||
|
}
|
||||||
@@ -49,6 +49,9 @@ mod default;
|
|||||||
pub use self::getdelim::*;
|
pub use self::getdelim::*;
|
||||||
mod getdelim;
|
mod getdelim;
|
||||||
|
|
||||||
|
pub use self::open_memstream::*;
|
||||||
|
mod open_memstream;
|
||||||
|
|
||||||
mod ext;
|
mod ext;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
pub mod printf;
|
pub mod printf;
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
use alloc::{boxed::Box, vec, vec::Vec};
|
||||||
|
use core::ptr;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
Buffer, FILE,
|
||||||
|
constants::{BUFSIZ, F_NORD},
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
error::{Errno, ResultExtPtrMut},
|
||||||
|
fs::File,
|
||||||
|
header::{
|
||||||
|
errno::{EFAULT, ENOMEM},
|
||||||
|
fcntl, pthread, stdlib, unistd,
|
||||||
|
},
|
||||||
|
io::{self, BufWriter, Write},
|
||||||
|
platform::{
|
||||||
|
ERRNO,
|
||||||
|
types::{c_char, size_t},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MemstreamWriter {
|
||||||
|
bufp: *mut *mut c_char,
|
||||||
|
sizep: *mut size_t,
|
||||||
|
current: *mut c_char,
|
||||||
|
buffer: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for MemstreamWriter {}
|
||||||
|
|
||||||
|
impl MemstreamWriter {
|
||||||
|
fn new(bufp: *mut *mut c_char, sizep: *mut size_t) -> Self {
|
||||||
|
Self {
|
||||||
|
bufp,
|
||||||
|
sizep,
|
||||||
|
current: ptr::null_mut(),
|
||||||
|
buffer: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sync_output(&mut self) -> io::Result<()> {
|
||||||
|
let size = self.buffer.len();
|
||||||
|
let alloc_size = size
|
||||||
|
.checked_add(1)
|
||||||
|
.ok_or_else(|| io::Error::from_raw_os_error(ENOMEM))?;
|
||||||
|
|
||||||
|
let raw = if self.current.is_null() {
|
||||||
|
unsafe { stdlib::malloc(alloc_size) }
|
||||||
|
} else {
|
||||||
|
unsafe { stdlib::realloc(self.current.cast(), alloc_size) }
|
||||||
|
};
|
||||||
|
if raw.is_null() {
|
||||||
|
return Err(io::Error::from_raw_os_error(ENOMEM));
|
||||||
|
}
|
||||||
|
|
||||||
|
let raw = raw.cast::<c_char>();
|
||||||
|
if size != 0 {
|
||||||
|
unsafe { ptr::copy_nonoverlapping(self.buffer.as_ptr(), raw.cast::<u8>(), size) };
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
*raw.add(size) = 0;
|
||||||
|
*self.bufp = raw;
|
||||||
|
*self.sizep = size;
|
||||||
|
}
|
||||||
|
self.current = raw;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for MemstreamWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.buffer
|
||||||
|
.try_reserve(buf.len())
|
||||||
|
.map_err(|_| io::Error::from_raw_os_error(ENOMEM))?;
|
||||||
|
self.buffer.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.sync_output()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> Result<Box<FILE>, Errno> {
|
||||||
|
if bufp.is_null() || sizep.is_null() {
|
||||||
|
return Err(Errno(EFAULT));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*bufp = ptr::null_mut();
|
||||||
|
*sizep = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut fds = [0; 2];
|
||||||
|
if unsafe { unistd::pipe2(fds.as_mut_ptr(), fcntl::O_CLOEXEC) } != 0 {
|
||||||
|
return Err(Errno(ERRNO.get()));
|
||||||
|
}
|
||||||
|
let _ = unistd::close(fds[0]);
|
||||||
|
|
||||||
|
let file = File::new(fds[1]);
|
||||||
|
let writer = Box::new(BufWriter::new(MemstreamWriter::new(bufp, sizep)));
|
||||||
|
let mutex_attr = pthread::RlctMutexAttr {
|
||||||
|
ty: pthread::PTHREAD_MUTEX_RECURSIVE,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Box::new(FILE {
|
||||||
|
lock: pthread::RlctMutex::new(&mutex_attr).unwrap(),
|
||||||
|
file,
|
||||||
|
flags: F_NORD,
|
||||||
|
read_buf: Buffer::Owned(vec![0; BUFSIZ as usize]),
|
||||||
|
read_pos: 0,
|
||||||
|
read_size: 0,
|
||||||
|
unget: Vec::new(),
|
||||||
|
writer,
|
||||||
|
pid: None,
|
||||||
|
orientation: 0,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn open_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> *mut FILE {
|
||||||
|
create_memstream(bufp, sizep).or_errno_null_mut()
|
||||||
|
}
|
||||||
@@ -1247,7 +1247,11 @@ pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char
|
|||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getenv.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getenv.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char {
|
pub unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char {
|
||||||
unimplemented!();
|
// Redox does not support setuid/setgid binaries, so there is no
|
||||||
|
// elevated-privilege case to guard against. Always delegate to getenv.
|
||||||
|
// If setuid support is ever added, this must check real vs effective
|
||||||
|
// uid/gid and return NULL when they differ.
|
||||||
|
unsafe { getenv(name) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
sys_includes = ["stdint.h"]
|
||||||
|
after_includes = """
|
||||||
|
typedef uint64_t eventfd_t;
|
||||||
|
"""
|
||||||
|
include_guard = "_SYS_EVENTFD_H"
|
||||||
|
language = "C"
|
||||||
|
style = "Tag"
|
||||||
|
no_includes = true
|
||||||
|
cpp_compat = true
|
||||||
|
[enum]
|
||||||
|
prefix_with_name = true
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
//! `sys/sem.h` implementation.
|
||||||
|
|
||||||
|
use alloc::{collections::BTreeMap, ffi::CString, format};
|
||||||
|
use core::{mem, ptr, sync::atomic::{AtomicI32, Ordering}};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
header::{
|
||||||
|
errno::{EFAULT, EINVAL, ENOENT},
|
||||||
|
fcntl::{O_CREAT, O_EXCL, O_RDWR},
|
||||||
|
sys_ipc::{IPC_CREAT, IPC_EXCL, IPC_NOWAIT, IPC_PRIVATE, IPC_RMID, ipc_perm, key_t},
|
||||||
|
sys_mman::{MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, mmap, munmap, shm_open, shm_unlink},
|
||||||
|
unistd::close,
|
||||||
|
},
|
||||||
|
out::Out,
|
||||||
|
platform::{ERRNO, Pal, Sys, types::{c_int, c_short, c_ushort, c_void}},
|
||||||
|
sync::{Mutex, Semaphore},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const GETVAL: c_int = 12;
|
||||||
|
pub const SETVAL: c_int = 16;
|
||||||
|
pub const SEM_UNDO: c_short = 0x1000;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct sembuf {
|
||||||
|
pub sem_num: c_ushort,
|
||||||
|
pub sem_op: c_short,
|
||||||
|
pub sem_flg: c_short,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct semid_ds {
|
||||||
|
pub sem_perm: ipc_perm,
|
||||||
|
pub sem_nsems: c_ushort,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union semun {
|
||||||
|
pub val: c_int,
|
||||||
|
pub buf: *mut semid_ds,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct SharedSemSet {
|
||||||
|
sem: Semaphore,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct SemMeta {
|
||||||
|
path: CString,
|
||||||
|
nsems: c_ushort,
|
||||||
|
}
|
||||||
|
|
||||||
|
static NEXT_SEMID: AtomicI32 = AtomicI32::new(1);
|
||||||
|
static SEM_REGISTRY: Mutex<BTreeMap<c_int, SemMeta>> = Mutex::new(BTreeMap::new());
|
||||||
|
static SEM_ATTACHMENTS: Mutex<BTreeMap<c_int, (usize, c_int)>> = Mutex::new(BTreeMap::new());
|
||||||
|
|
||||||
|
fn key_path(key: key_t) -> CString { CString::new(format!("/relibc-sysv-sem-{key:x}")).unwrap() }
|
||||||
|
fn id_path(id: c_int) -> CString { CString::new(format!("/relibc-sysv-sem-id-{id:x}")).unwrap() }
|
||||||
|
|
||||||
|
fn open_flags_from_ipc(semflg: c_int) -> c_int {
|
||||||
|
let mut flags = O_RDWR;
|
||||||
|
if semflg & IPC_CREAT == IPC_CREAT { flags |= O_CREAT; }
|
||||||
|
if semflg & IPC_EXCL == IPC_EXCL { flags |= O_EXCL; }
|
||||||
|
flags
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn map_sem(path: &CString, create: bool, initial: c_int) -> Result<*mut SharedSemSet, c_int> {
|
||||||
|
let fd = unsafe { shm_open(path.as_ptr(), if create { O_CREAT | O_RDWR } else { O_RDWR }, 0o600) };
|
||||||
|
if fd < 0 { return Err(ERRNO.get()); }
|
||||||
|
let mut st = crate::header::sys_stat::stat::default();
|
||||||
|
if Sys::fstat(fd, Out::from_mut(&mut st)).is_err() { let _ = close(fd); return Err(ERRNO.get()); }
|
||||||
|
if st.st_size == 0 {
|
||||||
|
if Sys::ftruncate(fd, mem::size_of::<SharedSemSet>() as i64).is_err() { let _ = close(fd); return Err(ERRNO.get()); }
|
||||||
|
}
|
||||||
|
let ptr = unsafe { mmap(ptr::null_mut(), mem::size_of::<SharedSemSet>(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) };
|
||||||
|
let _ = close(fd);
|
||||||
|
if ptr == MAP_FAILED { return Err(ERRNO.get()); }
|
||||||
|
let set = ptr.cast::<SharedSemSet>();
|
||||||
|
if create && st.st_size == 0 { unsafe { set.write(SharedSemSet { sem: Semaphore::new(initial as u32) }) }; }
|
||||||
|
Ok(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn semget(key: key_t, nsems: c_int, semflg: c_int) -> c_int {
|
||||||
|
if nsems != 1 { ERRNO.set(EINVAL); return -1; }
|
||||||
|
let semid = NEXT_SEMID.fetch_add(1, Ordering::SeqCst);
|
||||||
|
let path = if key == IPC_PRIVATE { id_path(semid) } else { key_path(key) };
|
||||||
|
let fd = unsafe { shm_open(path.as_ptr(), open_flags_from_ipc(semflg), (semflg & 0o777) as c_int) };
|
||||||
|
if fd < 0 { return -1; }
|
||||||
|
let _ = close(fd);
|
||||||
|
SEM_REGISTRY.lock().insert(semid, SemMeta { path, nsems: 1 });
|
||||||
|
semid
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn semop(semid: c_int, sops: *mut sembuf, nsops: usize) -> c_int {
|
||||||
|
if sops.is_null() { ERRNO.set(EFAULT); return -1; }
|
||||||
|
if nsops == 0 { return 0; }
|
||||||
|
let meta = { let registry = SEM_REGISTRY.lock(); registry.get(&semid).cloned() };
|
||||||
|
let Some(meta) = meta else { ERRNO.set(ENOENT); return -1; };
|
||||||
|
let set = {
|
||||||
|
let mut attachments = SEM_ATTACHMENTS.lock();
|
||||||
|
if let Some((ptr, _)) = attachments.get(&semid) { *ptr as *mut SharedSemSet } else {
|
||||||
|
let ptr = match unsafe { map_sem(&meta.path, false, 0) } { Ok(ptr) => ptr, Err(_) => return -1 };
|
||||||
|
attachments.insert(semid, (ptr as usize, 1));
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let ops = unsafe { core::slice::from_raw_parts(sops, nsops) };
|
||||||
|
for op in ops {
|
||||||
|
if op.sem_num != 0 { ERRNO.set(EINVAL); return -1; }
|
||||||
|
if op.sem_op > 0 { unsafe { (*set).sem.post(op.sem_op as u32) }; continue; }
|
||||||
|
if op.sem_op == 0 {
|
||||||
|
if op.sem_flg & IPC_NOWAIT as c_short == IPC_NOWAIT as c_short {
|
||||||
|
if unsafe { (*set).sem.value() } != 0 { ERRNO.set(crate::header::errno::EAGAIN); return -1; }
|
||||||
|
} else if unsafe { (*set).sem.value() } != 0 {
|
||||||
|
ERRNO.set(crate::header::errno::EAGAIN);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for _ in 0..(-op.sem_op) {
|
||||||
|
if op.sem_flg & IPC_NOWAIT as c_short == IPC_NOWAIT as c_short {
|
||||||
|
if unsafe { (*set).sem.try_wait() } == 0 { ERRNO.set(crate::header::errno::EAGAIN); return -1; }
|
||||||
|
} else if unsafe { (*set).sem.wait(None, crate::header::time::CLOCK_MONOTONIC) }.is_err() {
|
||||||
|
ERRNO.set(crate::header::errno::EINTR);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn semctl(semid: c_int, semnum: c_int, cmd: c_int, mut args: ...) -> c_int {
|
||||||
|
if semnum != 0 { ERRNO.set(EINVAL); return -1; }
|
||||||
|
let meta = { let registry = SEM_REGISTRY.lock(); registry.get(&semid).cloned() };
|
||||||
|
let Some(meta) = meta else { ERRNO.set(ENOENT); return -1; };
|
||||||
|
match cmd {
|
||||||
|
IPC_RMID => {
|
||||||
|
let _ = unsafe { shm_unlink(meta.path.as_ptr()) };
|
||||||
|
SEM_REGISTRY.lock().remove(&semid);
|
||||||
|
if let Some((ptr, _)) = SEM_ATTACHMENTS.lock().remove(&semid) {
|
||||||
|
let _ = unsafe { munmap((ptr as *mut SharedSemSet).cast::<c_void>(), mem::size_of::<SharedSemSet>()) };
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
GETVAL => {
|
||||||
|
let set = match unsafe { map_sem(&meta.path, false, 0) } { Ok(set) => set, Err(errno) => { ERRNO.set(errno); return -1; } };
|
||||||
|
let value = unsafe { (*set).sem.value() as c_int };
|
||||||
|
let _ = unsafe { munmap(set.cast::<c_void>(), mem::size_of::<SharedSemSet>()) };
|
||||||
|
value
|
||||||
|
}
|
||||||
|
SETVAL => {
|
||||||
|
let val = unsafe { args.arg::<c_int>() };
|
||||||
|
let set = match unsafe { map_sem(&meta.path, false, 0) } { Ok(set) => set, Err(errno) => { ERRNO.set(errno); return -1; } };
|
||||||
|
let current = unsafe { (*set).sem.value() as c_int };
|
||||||
|
if val > current { unsafe { (*set).sem.post((val - current) as u32) }; } else {
|
||||||
|
for _ in 0..(current - val) {
|
||||||
|
if unsafe { (*set).sem.try_wait() } == 0 { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = unsafe { munmap(set.cast::<c_void>(), mem::size_of::<SharedSemSet>()) };
|
||||||
|
0
|
||||||
|
}
|
||||||
|
crate::header::sys_ipc::IPC_STAT => {
|
||||||
|
let buf = unsafe { args.arg::<*mut semid_ds>() };
|
||||||
|
if buf.is_null() { ERRNO.set(EFAULT); return -1; }
|
||||||
|
unsafe { *buf = semid_ds { sem_perm: ipc_perm::default(), sem_nsems: meta.nsems }; }
|
||||||
|
0
|
||||||
|
}
|
||||||
|
_ => { ERRNO.set(EINVAL); -1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
sys_includes = ["signal.h", "stdint.h", "stddef.h"]
|
||||||
|
include_guard = "_SYS_SIGNALFD_H"
|
||||||
|
trailer = """
|
||||||
|
#ifndef SFD_CLOEXEC
|
||||||
|
#define SFD_CLOEXEC 0x80000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SFD_NONBLOCK
|
||||||
|
#define SFD_NONBLOCK 0x800
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int signalfd(int fd, const sigset_t *mask, size_t masksize);
|
||||||
|
int signalfd4(int fd, const sigset_t *mask, size_t masksize, int flags);
|
||||||
|
"""
|
||||||
|
language = "C"
|
||||||
|
style = "Tag"
|
||||||
|
no_includes = true
|
||||||
|
cpp_compat = true
|
||||||
|
|
||||||
|
[enum]
|
||||||
|
prefix_with_name = true
|
||||||
|
|
||||||
|
[export.rename]
|
||||||
|
"signalfd_siginfo" = "struct signalfd_siginfo"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//! `sys/signalfd.h` implementation.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
header::signal::{self, signalfd_siginfo},
|
||||||
|
platform::types::c_int,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SFD_CLOEXEC: c_int = signal::SFD_CLOEXEC;
|
||||||
|
pub const SFD_NONBLOCK: c_int = signal::SFD_NONBLOCK;
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _cbindgen_export_sys_signalfd_siginfo(siginfo: signalfd_siginfo) {
|
||||||
|
let _ = siginfo;
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
//! `sys/timerfd.h` implementation.
|
||||||
|
//!
|
||||||
|
//! Non-POSIX, see <https://man7.org/linux/man-pages/man2/timerfd_create.2.html>.
|
||||||
|
|
||||||
|
use alloc::{collections::BTreeMap, format};
|
||||||
|
use core::{mem, slice, sync::atomic::{AtomicBool, Ordering}};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
c_str::{CStr, CString},
|
||||||
|
error::{Errno, ResultExt},
|
||||||
|
header::{
|
||||||
|
bits_timespec::timespec,
|
||||||
|
errno::{EBADF, EFAULT, EINVAL, EIO},
|
||||||
|
fcntl::{O_CLOEXEC, O_NONBLOCK, O_RDWR},
|
||||||
|
},
|
||||||
|
out::Out,
|
||||||
|
platform::{
|
||||||
|
Pal, Sys,
|
||||||
|
types::{c_int, clockid_t},
|
||||||
|
},
|
||||||
|
sync::Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use crate::header::time::itimerspec;
|
||||||
|
|
||||||
|
pub const TFD_CLOEXEC: c_int = 0x80000;
|
||||||
|
pub const TFD_NONBLOCK: c_int = 0x800;
|
||||||
|
pub const TFD_TIMER_ABSTIME: c_int = 0x1;
|
||||||
|
pub const TFD_TIMER_CANCEL_ON_SET: c_int = 0x2;
|
||||||
|
|
||||||
|
const NSEC_PER_SEC: i64 = 1_000_000_000;
|
||||||
|
|
||||||
|
static MAP_INIT: AtomicBool = AtomicBool::new(false);
|
||||||
|
static TIMERFD_CLOCKIDS: Mutex<Option<BTreeMap<c_int, clockid_t>>> = Mutex::new(None);
|
||||||
|
|
||||||
|
fn with_map<R>(f: impl FnOnce(&mut BTreeMap<c_int, clockid_t>) -> R) -> R {
|
||||||
|
let mut guard = TIMERFD_CLOCKIDS.lock();
|
||||||
|
if !MAP_INIT.load(Ordering::Acquire) {
|
||||||
|
*guard = Some(BTreeMap::new());
|
||||||
|
MAP_INIT.store(true, Ordering::Release);
|
||||||
|
}
|
||||||
|
f(guard.as_mut().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_timespec(a: ×pec, b: ×pec) -> timespec {
|
||||||
|
let total_nsec = a.tv_nsec as i64 + b.tv_nsec as i64;
|
||||||
|
let mut sec = a.tv_sec + b.tv_sec + (total_nsec / NSEC_PER_SEC) as i64;
|
||||||
|
let mut nsec = (total_nsec % NSEC_PER_SEC) as i64;
|
||||||
|
if nsec < 0 {
|
||||||
|
sec -= 1;
|
||||||
|
nsec += NSEC_PER_SEC;
|
||||||
|
}
|
||||||
|
timespec { tv_sec: sec, tv_nsec: nsec }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_exact(fd: c_int, buf: &mut [u8]) -> Result<(), Errno> {
|
||||||
|
match Sys::read(fd, buf)? {
|
||||||
|
n if n == buf.len() => Ok(()),
|
||||||
|
_ => Err(Errno(EIO)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_exact(fd: c_int, buf: &[u8]) -> Result<(), Errno> {
|
||||||
|
match Sys::write(fd, buf)? {
|
||||||
|
n if n == buf.len() => Ok(()),
|
||||||
|
_ => Err(Errno(EIO)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn timerfd_create(clockid: clockid_t, flags: c_int) -> c_int {
|
||||||
|
let supported = TFD_CLOEXEC | TFD_NONBLOCK;
|
||||||
|
if flags & !supported != 0 {
|
||||||
|
return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut oflag = O_RDWR;
|
||||||
|
if flags & TFD_CLOEXEC == TFD_CLOEXEC {
|
||||||
|
oflag |= O_CLOEXEC;
|
||||||
|
}
|
||||||
|
if flags & TFD_NONBLOCK == TFD_NONBLOCK {
|
||||||
|
oflag |= O_NONBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = match CString::new(format!("/scheme/time/{clockid}")) {
|
||||||
|
Ok(path) => path,
|
||||||
|
Err(_) => return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno(),
|
||||||
|
};
|
||||||
|
let fd = match Sys::open(CStr::borrow(&path), oflag, 0) {
|
||||||
|
Ok(fd) => fd,
|
||||||
|
Err(Errno(e)) => { return Err::<c_int, _>(Errno(e)).or_minus_one_errno(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register the clockid for this fd so timerfd_settime can convert relative times
|
||||||
|
with_map(|map| { map.insert(fd, clockid); });
|
||||||
|
|
||||||
|
fd
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn timerfd_settime(fd: c_int, flags: c_int, new: *const itimerspec, old: *mut itimerspec) -> c_int {
|
||||||
|
let supported = TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET;
|
||||||
|
if flags & !supported != 0 {
|
||||||
|
return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno();
|
||||||
|
}
|
||||||
|
if new.is_null() {
|
||||||
|
return Err::<c_int, _>(Errno(EFAULT)).or_minus_one_errno();
|
||||||
|
}
|
||||||
|
if !old.is_null() && unsafe { timerfd_gettime(fd, old) } < 0 {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
let spec = unsafe { &*new };
|
||||||
|
let mut value = spec.it_value.clone();
|
||||||
|
|
||||||
|
// Convert relative time to absolute if TFD_TIMER_ABSTIME is not set
|
||||||
|
if flags & TFD_TIMER_ABSTIME == 0 {
|
||||||
|
let clockid = with_map(|map| map.get(&fd).copied()).unwrap_or(0);
|
||||||
|
let mut now = timespec::default();
|
||||||
|
if Sys::clock_gettime(clockid, Out::from_mut(&mut now)).is_err() {
|
||||||
|
return Err::<c_int, _>(Errno(EBADF)).or_minus_one_errno();
|
||||||
|
}
|
||||||
|
value = add_timespec(&now, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = unsafe { slice::from_raw_parts((&raw const value).cast::<u8>(), mem::size_of::<timespec>()) };
|
||||||
|
write_exact(fd, buf).map(|()| 0).or_minus_one_errno()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn timerfd_gettime(fd: c_int, curr: *mut itimerspec) -> c_int {
|
||||||
|
if curr.is_null() {
|
||||||
|
return Err::<c_int, _>(Errno(EFAULT)).or_minus_one_errno();
|
||||||
|
}
|
||||||
|
let curr = unsafe { &mut *curr };
|
||||||
|
curr.it_interval = timespec::default();
|
||||||
|
let buf = unsafe { slice::from_raw_parts_mut((&raw mut curr.it_value).cast::<u8>(), mem::size_of::<timespec>()) };
|
||||||
|
read_exact(fd, buf).map(|()| 0).or_minus_one_errno()
|
||||||
|
}
|
||||||
@@ -50,6 +50,16 @@ pub(crate) const NANOSECONDS: c_long = 1_000_000_000;
|
|||||||
/// cbindgen:ignore
|
/// cbindgen:ignore
|
||||||
const UTC_STR: &core::ffi::CStr = c"UTC";
|
const UTC_STR: &core::ffi::CStr = c"UTC";
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
impl<'a> From<&'a syscall::TimeSpec> for timespec {
|
||||||
|
fn from(tp: &syscall::TimeSpec) -> Self {
|
||||||
|
Self {
|
||||||
|
tv_sec: tp.tv_sec as _,
|
||||||
|
tv_nsec: tp.tv_nsec as _,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// timer_t internal data, ABI unstable
|
/// timer_t internal data, ABI unstable
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
@@ -287,7 +287,20 @@ pub extern "C" fn dup2(fildes: c_int, fildes2: c_int) -> c_int {
|
|||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub extern "C" fn dup3(fildes: c_int, fildes2: c_int, flag: c_int) -> c_int {
|
pub extern "C" fn dup3(fildes: c_int, fildes2: c_int, flag: c_int) -> c_int {
|
||||||
unimplemented!();
|
// dup3 requires fildes != fildes2 (unlike dup2 which is a no-op in that case)
|
||||||
|
if fildes == fildes2 {
|
||||||
|
ERRNO.set(EINVAL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
match Sys::dup2(fildes, fildes2) {
|
||||||
|
Ok(newfd) => {
|
||||||
|
if flag & fcntl::O_CLOEXEC != 0 {
|
||||||
|
let _ = Sys::fcntl(newfd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as c_ulonglong);
|
||||||
|
}
|
||||||
|
newfd
|
||||||
|
}
|
||||||
|
Err(Errno(e)) => { ERRNO.set(e); -1 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/encrypt.html>.
|
// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/encrypt.html>.
|
||||||
@@ -560,8 +573,39 @@ pub extern "C" fn getegid() -> gid_t {
|
|||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int {
|
pub unsafe extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int {
|
||||||
unimplemented!();
|
// POSIX limits getentropy to 256 bytes per call
|
||||||
|
const GETENTROPY_MAX: size_t = 256;
|
||||||
|
|
||||||
|
if length > GETENTROPY_MAX {
|
||||||
|
ERRNO.set(EINVAL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = unsafe { CStr::from_ptr(c"/scheme/rand".as_ptr().cast()) };
|
||||||
|
let fd = match Sys::open(path, fcntl::O_RDONLY, 0) {
|
||||||
|
Ok(fd) => fd,
|
||||||
|
Err(Errno(e)) => {
|
||||||
|
ERRNO.set(e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let buf = unsafe { slice::from_raw_parts_mut(buffer.cast::<u8>(), length) };
|
||||||
|
let mut filled = 0usize;
|
||||||
|
while filled < length {
|
||||||
|
match Sys::read(fd, &mut buf[filled..]) {
|
||||||
|
Ok(0) => break,
|
||||||
|
Ok(n) => filled += n,
|
||||||
|
Err(Errno(e)) => {
|
||||||
|
let _ = Sys::close(fd);
|
||||||
|
ERRNO.set(e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = Sys::close(fd);
|
||||||
|
if filled < length { ERRNO.set(errno::EIO); -1 } else { 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/geteuid.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/geteuid.html>.
|
||||||
@@ -1321,7 +1365,7 @@ pub extern "C" fn usleep(useconds: useconds_t) -> c_int {
|
|||||||
#[deprecated]
|
#[deprecated]
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub extern "C" fn vfork() -> pid_t {
|
pub extern "C" fn vfork() -> pid_t {
|
||||||
unimplemented!();
|
unsafe { fork() }
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn with_argv(
|
unsafe fn with_argv(
|
||||||
|
|||||||
+14
-10
@@ -108,18 +108,22 @@ pub fn execve(
|
|||||||
// TODO: At some point we might have capabilities limiting the ability to allocate
|
// TODO: At some point we might have capabilities limiting the ability to allocate
|
||||||
// executable memory.
|
// executable memory.
|
||||||
|
|
||||||
let Resugid { ruid, rgid, .. } = redox_rt::sys::posix_getresugid();
|
let Resugid { ruid, euid, rgid, .. } = redox_rt::sys::posix_getresugid();
|
||||||
|
|
||||||
let mode = if ruid == stat.st_uid {
|
// Root (uid 0) bypasses execute permission checks, matching Linux behavior.
|
||||||
(stat.st_mode >> 3 * 2) & 0o7
|
// Check both ruid and euid since Linux checks the effective UID.
|
||||||
} else if rgid == stat.st_gid {
|
if ruid != 0 && euid != 0 {
|
||||||
(stat.st_mode >> 3 * 1) & 0o7
|
let mode = if ruid == stat.st_uid {
|
||||||
} else {
|
(stat.st_mode >> 3 * 2) & 0o7
|
||||||
stat.st_mode & 0o7
|
} else if rgid == stat.st_gid {
|
||||||
};
|
(stat.st_mode >> 3 * 1) & 0o7
|
||||||
|
} else {
|
||||||
|
stat.st_mode & 0o7
|
||||||
|
};
|
||||||
|
|
||||||
if mode & 0o1 == 0o0 {
|
if mode & 0o1 == 0o0 {
|
||||||
return Err(Error::new(EPERM));
|
return Err(Error::new(EACCES));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let cwd = super::path::clone_cwd().unwrap_or_default();
|
let cwd = super::path::clone_cwd().unwrap_or_default();
|
||||||
|
|||||||
+12
-29
@@ -726,8 +726,16 @@ impl Pal for Sys {
|
|||||||
Err(Errno(EPERM))
|
Err(Errno(EPERM))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getrusage(who: c_int, r_usage: Out<rusage>) -> Result<()> {
|
fn getrusage(_who: c_int, mut r_usage: Out<rusage>) -> Result<()> {
|
||||||
todo_skip!(0, "getrusage({}, {:p}): not implemented", who, r_usage);
|
r_usage.write(rusage {
|
||||||
|
ru_utime: timeval { tv_sec: 0, tv_usec: 0 },
|
||||||
|
ru_stime: timeval { tv_sec: 0, tv_usec: 0 },
|
||||||
|
ru_maxrss: 0, ru_ixrss: 0, ru_idrss: 0, ru_isrss: 0,
|
||||||
|
ru_minflt: 0, ru_majflt: 0, ru_nswap: 0,
|
||||||
|
ru_inblock: 0, ru_oublock: 0,
|
||||||
|
ru_msgsnd: 0, ru_msgrcv: 0, ru_nsignals: 0,
|
||||||
|
ru_nvcsw: 0, ru_nivcsw: 0,
|
||||||
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,23 +889,7 @@ impl Pal for Sys {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> {
|
unsafe fn msync(_addr: *mut c_void, _len: usize, _flags: c_int) -> Result<()> { Ok(()) }
|
||||||
todo_skip!(
|
|
||||||
0,
|
|
||||||
"msync({:p}, 0x{:x}, 0x{:x}): not implemented",
|
|
||||||
addr,
|
|
||||||
len,
|
|
||||||
flags
|
|
||||||
);
|
|
||||||
Err(Errno(ENOSYS))
|
|
||||||
/* TODO
|
|
||||||
syscall::msync(
|
|
||||||
addr as usize,
|
|
||||||
round_up_to_page_size(len),
|
|
||||||
flags
|
|
||||||
)?;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()> {
|
unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()> {
|
||||||
// Redox never swaps
|
// Redox never swaps
|
||||||
@@ -921,16 +913,7 @@ impl Pal for Sys {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn madvise(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> {
|
unsafe fn madvise(_addr: *mut c_void, _len: usize, _flags: c_int) -> Result<()> { Ok(()) }
|
||||||
todo_skip!(
|
|
||||||
0,
|
|
||||||
"madvise({:p}, 0x{:x}, 0x{:x}): not implemented",
|
|
||||||
addr,
|
|
||||||
len,
|
|
||||||
flags
|
|
||||||
);
|
|
||||||
Err(Errno(ENOSYS))
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()> {
|
unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()> {
|
||||||
let redox_rqtp = unsafe { (&*rqtp).into() };
|
let redox_rqtp = unsafe { (&*rqtp).into() };
|
||||||
|
|||||||
+40
-49
@@ -1,18 +1,34 @@
|
|||||||
use core::num::NonZeroU32;
|
use core::{
|
||||||
|
num::NonZeroU32,
|
||||||
|
sync::atomic::{AtomicU32, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Barrier {
|
pub struct Barrier {
|
||||||
original_count: NonZeroU32,
|
original_count: NonZeroU32,
|
||||||
// 4
|
// 4
|
||||||
lock: crate::sync::Mutex<Inner>,
|
lock: crate::sync::Mutex<Inner>,
|
||||||
// 16
|
// 16
|
||||||
cvar: crate::header::pthread::RlctCond,
|
cvar: FutexState,
|
||||||
// 24
|
// 24
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Inner {
|
struct Inner {
|
||||||
count: u32,
|
_unused0: u32,
|
||||||
// TODO: Overflows might be problematic... 64-bit?
|
_unused1: u32,
|
||||||
gen_id: u32,
|
}
|
||||||
|
|
||||||
|
struct FutexState {
|
||||||
|
count: AtomicU32,
|
||||||
|
sense: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FutexState {
|
||||||
|
const fn new(count: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
count: AtomicU32::new(count),
|
||||||
|
sense: AtomicU32::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum WaitResult {
|
pub enum WaitResult {
|
||||||
@@ -25,61 +41,36 @@ impl Barrier {
|
|||||||
Self {
|
Self {
|
||||||
original_count: count,
|
original_count: count,
|
||||||
lock: crate::sync::Mutex::new(Inner {
|
lock: crate::sync::Mutex::new(Inner {
|
||||||
count: 0,
|
_unused0: 0,
|
||||||
gen_id: 0,
|
_unused1: 0,
|
||||||
}),
|
}),
|
||||||
cvar: crate::header::pthread::RlctCond::new(),
|
cvar: FutexState::new(count.get()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn wait(&self) -> WaitResult {
|
pub fn wait(&self) -> WaitResult {
|
||||||
let mut guard = self.lock.lock();
|
let _ = &self.lock;
|
||||||
let gen_id = guard.gen_id;
|
let sense = self.cvar.sense.load(Ordering::Acquire);
|
||||||
|
|
||||||
guard.count += 1;
|
if self.cvar.count.fetch_sub(1, Ordering::AcqRel) == 1 {
|
||||||
|
self.cvar
|
||||||
if guard.count == self.original_count.get() {
|
.count
|
||||||
guard.gen_id = guard.gen_id.wrapping_add(1);
|
.store(self.original_count.get(), Ordering::Relaxed);
|
||||||
guard.count = 0;
|
self.cvar
|
||||||
if let Ok(()) = self.cvar.broadcast() {}; // TODO handle error
|
.sense
|
||||||
|
.store(sense.wrapping_add(1), Ordering::Release);
|
||||||
drop(guard);
|
crate::sync::futex_wake(&self.cvar.sense, i32::MAX);
|
||||||
|
|
||||||
WaitResult::NotifiedAll
|
WaitResult::NotifiedAll
|
||||||
} else {
|
} else {
|
||||||
while guard.gen_id == gen_id {
|
// SMP fix: wait directly on the barrier generation word instead of routing through the
|
||||||
guard = self.cvar.wait_inner_typedmutex(guard);
|
// condvar unlock->futex_wait path. If the last thread flips `sense` after we load it
|
||||||
|
// but before our futex wait starts, the futex observes a stale value and returns
|
||||||
|
// immediately instead of sleeping forever after a missed broadcast wakeup.
|
||||||
|
while self.cvar.sense.load(Ordering::Acquire) == sense {
|
||||||
|
let _ = crate::sync::futex_wait(&self.cvar.sense, sense, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitResult::Waited
|
WaitResult::Waited
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
let mut guard = self.lock.lock();
|
|
||||||
let Inner { count, gen_id } = *guard;
|
|
||||||
|
|
||||||
let last = self.original_count.get() - 1;
|
|
||||||
|
|
||||||
if count == last {
|
|
||||||
eprintln!("last {:?}", *guard);
|
|
||||||
guard.gen_id = guard.gen_id.wrapping_add(1);
|
|
||||||
guard.count = 0;
|
|
||||||
|
|
||||||
drop(guard);
|
|
||||||
|
|
||||||
self.cvar.broadcast();
|
|
||||||
|
|
||||||
WaitResult::NotifiedAll
|
|
||||||
} else {
|
|
||||||
guard.count += 1;
|
|
||||||
|
|
||||||
while guard.count != last && guard.gen_id == gen_id {
|
|
||||||
eprintln!("before {:?}", *guard);
|
|
||||||
guard = self.cvar.wait_inner_typedmutex(guard);
|
|
||||||
eprintln!("after {:?}", *guard);
|
|
||||||
}
|
|
||||||
|
|
||||||
WaitResult::Waited
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static LOCK: crate::sync::Mutex<()> = crate::sync::Mutex::new(());
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
sem_t *sem = sem_open("/relibc-test-sem", O_CREAT | O_EXCL, 0600, 1);
|
||||||
|
assert(sem != SEM_FAILED);
|
||||||
|
assert(sem_wait(sem) == 0);
|
||||||
|
assert(sem_post(sem) == 0);
|
||||||
|
assert(sem_close(sem) == 0);
|
||||||
|
assert(sem_unlink("/relibc-test-sem") == 0);
|
||||||
|
puts("sem_open ok");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
int id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0600);
|
||||||
|
assert(id >= 0);
|
||||||
|
void *ptr = shmat(id, 0, 0);
|
||||||
|
assert(ptr != (void *)-1);
|
||||||
|
assert(shmdt(ptr) == 0);
|
||||||
|
assert(shmctl(id, IPC_RMID, 0) == 0);
|
||||||
|
puts("shmget ok");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user