Files
RedBear-OS/src/header/signal/mod.rs
T
2026-05-18 19:03:11 +07:00

590 lines
19 KiB
Rust

//! `signal.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
use core::{mem, ptr};
use cbitset::BitSet;
#[cfg(target_os = "redox")]
use crate::platform::types::pthread_attr_t;
use crate::{
error::{Errno, ResultExt},
header::{bits_sigset_t::sigset_t, errno, time::timespec},
platform::{
self, ERRNO, Pal, PalSignal, Sys,
types::{c_char, c_int, c_ulonglong, c_void, pid_t, pthread_t, size_t, uid_t},
},
};
pub use self::sys::*;
use super::{
errno::EFAULT,
stdio::{fprintf, stderr},
};
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
pub mod sys;
#[cfg(target_os = "redox")]
#[path = "redox.rs"]
pub mod sys;
type SigSet = BitSet<[u64; 1]>;
/// cbindgen:ignore
pub(crate) const SIG_DFL: usize = 0;
/// cbindgen:ignore
pub(crate) const SIG_IGN: usize = 1;
/// cbindgen:ignore
pub(crate) const SIG_ERR: isize = -1;
/// cbindgen:ignore
pub(crate) const SIG_HOLD: isize = 2;
pub const SIG_BLOCK: c_int = 0;
pub const SIG_UNBLOCK: c_int = 1;
pub const SIG_SETMASK: c_int = 2;
pub const SIGEV_SIGNAL: c_int = 0;
pub const SIGEV_NONE: c_int = 1;
pub const SIGEV_THREAD: c_int = 2;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
#[repr(C)]
#[derive(Clone, Debug)]
/// cbindgen:ignore
pub struct sigaction {
pub sa_handler: Option<extern "C" fn(c_int)>,
pub sa_flags: c_int,
pub sa_restorer: Option<unsafe extern "C" fn()>,
pub sa_mask: sigset_t,
}
#[repr(C)]
#[derive(Clone)]
pub struct sigaltstack {
pub ss_sp: *mut c_void,
pub ss_flags: c_int,
pub ss_size: size_t,
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
#[repr(C)]
#[derive(Clone)]
#[cfg(not(target_os = "linux"))]
pub struct sigevent {
pub sigev_value: sigval,
pub sigev_signo: c_int,
pub sigev_notify: c_int,
pub sigev_notify_function: Option<extern "C" fn(sigval)>,
pub sigev_notify_attributes: *mut pthread_attr_t,
}
// must match with signature from libc
// https://docs.rs/libc/0.2.186/src/libc/unix/linux_like/mod.rs.html#300-322
#[repr(C)]
#[derive(Clone)]
#[cfg(target_os = "linux")]
pub struct sigevent {
pub sigev_value: sigval,
pub sigev_signo: c_int,
pub sigev_notify: c_int,
// Actually a union. We only expose sigev_notify_thread_id because it's
// the most useful member
pub sigev_notify_thread_id: c_int,
#[cfg(target_pointer_width = "64")]
__unused1: [c_int; 11],
#[cfg(target_pointer_width = "32")]
__unused1: [c_int; 12],
}
// FIXME: This struct is wrong on Linux
#[repr(C)]
#[derive(Clone, Copy)]
pub struct siginfo {
pub si_signo: c_int,
pub si_errno: c_int,
pub si_code: c_int,
pub si_pid: pid_t,
pub si_uid: uid_t,
pub si_addr: *mut c_void,
pub si_status: c_int,
pub si_value: sigval,
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
#[derive(Clone, Copy)]
#[repr(C)]
pub union sigval {
pub sival_int: c_int,
pub sival_ptr: *mut c_void,
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
/// cbindgen:ignore
pub type siginfo_t = siginfo;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>
pub type stack_t = sigaltstack;
//NOTE for the following two functions, to see why they're implemented slightly differently from their intended behavior, read
// https://git.musl-libc.org/cgit/musl/commit/?id=583e55122e767b1586286a0d9c35e2a4027998ab
#[unsafe(no_mangle)]
unsafe extern "C" fn __sigsetjmp_tail(jb: *mut c_ulonglong, ret: c_int) -> c_int {
let set = jb.wrapping_add(9);
if ret > 0 {
unsafe { sigprocmask(SIG_SETMASK, set, ptr::null_mut()) };
} else {
unsafe { sigprocmask(SIG_SETMASK, ptr::null_mut(), set) };
}
ret
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html>.
#[unsafe(no_mangle)]
pub extern "C" fn kill(pid: pid_t, sig: c_int) -> c_int {
Sys::kill(pid, sig).map(|()| 0).or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigqueue.html>.
#[unsafe(no_mangle)]
pub extern "C" fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> c_int {
Sys::sigqueue(pid, sig, val)
.map(|()| 0)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/killpg.html>.
#[unsafe(no_mangle)]
pub extern "C" fn killpg(pgrp: pid_t, sig: c_int) -> c_int {
Sys::killpg(pgrp, sig).map(|()| 0).or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_kill.html>.
#[unsafe(no_mangle)]
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) };
unsafe { pthread.os_tid.get().read() }
};
crate::header::pthread::e(unsafe { Sys::rlct_kill(os_tid, sig as usize) })
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_sigmask.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn pthread_sigmask(
how: c_int,
set: *const sigset_t,
oldset: *mut sigset_t,
) -> c_int {
// On Linux and Redox, pthread_sigmask and sigprocmask are equivalent
if unsafe { sigprocmask(how, set, oldset) } == 0 {
0
} else {
//TODO: Fix race
platform::ERRNO.get()
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/raise.html>.
#[unsafe(no_mangle)]
pub extern "C" fn raise(sig: c_int) -> c_int {
Sys::raise(sig).map(|()| 0).or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigaction.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigaction(
sig: c_int,
act: *const sigaction,
oact: *mut sigaction,
) -> c_int {
Sys::sigaction(sig, unsafe { act.as_ref() }, unsafe { oact.as_mut() })
.map(|()| 0)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigaddset.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigaddset(set: *mut sigset_t, signo: c_int) -> c_int {
if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX)
/* TODO */
{
platform::ERRNO.set(errno::EINVAL);
return -1;
}
if let Some(set) = unsafe { (set.cast::<SigSet>()).as_mut() } {
set.insert(signo as usize - 1); // 0-indexed usize, please!
}
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigaltstack.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int {
unsafe {
Sys::sigaltstack(ss.as_ref(), old_ss.as_mut())
.map(|()| 0)
.or_minus_one_errno()
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigdelset.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigdelset(set: *mut sigset_t, signo: c_int) -> c_int {
if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX)
/* TODO */
{
platform::ERRNO.set(errno::EINVAL);
return -1;
}
if let Some(set) = unsafe { (set.cast::<SigSet>()).as_mut() } {
set.remove(signo as usize - 1); // 0-indexed usize, please!
}
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigemptyset.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigemptyset(set: *mut sigset_t) -> c_int {
if let Some(set) = unsafe { (set.cast::<SigSet>()).as_mut() } {
set.clear();
}
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigfillset.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigfillset(set: *mut sigset_t) -> c_int {
if let Some(set) = unsafe { (set.cast::<SigSet>()).as_mut() } {
set.fill(.., true);
}
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html>.
///
/// Present in issue 7. Removed in issue 8.
///
/// Use of this function is unspecified in a multi-threaded process.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sighold(sig: c_int) -> c_int {
let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
unsafe { sigemptyset(pset.as_mut_ptr()) };
let mut set = unsafe { pset.assume_init() };
if unsafe { sigaddset(&raw mut set, sig) } < 0 {
return -1;
}
unsafe { sigprocmask(SIG_BLOCK, &raw const set, ptr::null_mut()) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html>.
///
/// Present in issue 7. Removed in issue 8.
///
/// Use of this function is unspecified in a multi-threaded process.
#[allow(clippy::missing_transmute_annotations)]
#[unsafe(no_mangle)]
pub extern "C" fn sigignore(sig: c_int) -> c_int {
let mut psa = mem::MaybeUninit::<sigaction>::uninit();
unsafe { sigemptyset(&raw mut (*psa.as_mut_ptr()).sa_mask) };
let mut sa = unsafe { psa.assume_init() };
sa.sa_handler = unsafe { mem::transmute(SIG_IGN) };
sa.sa_flags = 0;
unsafe { sigaction(sig, &raw const sa, ptr::null_mut()) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/siginterrupt.html>.
///
/// Marked obsolescent in issue 7. Removed in issue 8.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn siginterrupt(sig: c_int, flag: c_int) -> c_int {
let mut psa = mem::MaybeUninit::<sigaction>::uninit();
unsafe { sigaction(sig, ptr::null_mut(), psa.as_mut_ptr()) };
let mut sa = unsafe { psa.assume_init() };
if flag != 0 {
sa.sa_flags &= !SA_RESTART as c_int;
} else {
sa.sa_flags |= SA_RESTART as c_int;
}
unsafe { sigaction(sig, &raw const sa, ptr::null_mut()) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigismember.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigismember(set: *const sigset_t, signo: c_int) -> c_int {
if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX)
/* TODO */
{
platform::ERRNO.set(errno::EINVAL);
return -1;
}
if let Some(set) = unsafe { (set as *mut SigSet).as_mut() }
&& set.contains(signo as usize - 1)
{
return 1;
}
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/signal.html>.
#[allow(clippy::missing_transmute_annotations)]
#[unsafe(no_mangle)]
pub extern "C" fn signal(
sig: c_int,
func: Option<extern "C" fn(c_int)>,
) -> Option<extern "C" fn(c_int)> {
let sa = sigaction {
sa_handler: func,
sa_flags: SA_RESTART as _,
sa_restorer: None, // set by platform if applicable
sa_mask: sigset_t::default(),
};
let mut old_sa = mem::MaybeUninit::uninit();
if unsafe { sigaction(sig, &raw const sa, old_sa.as_mut_ptr()) } < 0 {
return unsafe { mem::transmute(SIG_ERR) };
}
unsafe { old_sa.assume_init() }.sa_handler
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html>.
///
/// Present in issue 7. Removed in issue 8.
///
/// Use of this function is unspecified in a multi-threaded process.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigpause(sig: c_int) -> c_int {
let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
unsafe { sigprocmask(0, ptr::null_mut(), pset.as_mut_ptr()) };
let mut set = unsafe { pset.assume_init() };
if unsafe { sigdelset(&raw mut set, sig) } == -1 {
return -1;
}
unsafe { sigsuspend(&raw const set) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigpending.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigpending(set: *mut sigset_t) -> c_int {
(|| Sys::sigpending(unsafe { set.as_mut().ok_or(Errno(EFAULT)) }?))()
.map(|()| 0)
.or_minus_one_errno()
}
// TODO: Double-check this mask.
// This prevents the application from blocking the two signals SIGRTMIN - 1 and SIGRTMIN - 2 which
// are (at least meant to be) used internally for timers and pthread cancellation. On Linux this is
// 32 and 33 (same as NPTL reserves), whereas this on Redox is 33 and 34 (TODO: could this be
// changed to 32 and 33 for Redox too, since there's currently no support for "sigqueue" targeting
// specific threads).
const RLCT_SIGNAL_MASK: sigset_t = (1 << ((SIGRTMIN - 1) - 1)) | (1 << ((SIGRTMIN - 2) - 1));
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigprocmask.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigprocmask(
how: c_int,
set: *const sigset_t,
oset: *mut sigset_t,
) -> c_int {
(|| {
let set = unsafe { set.as_ref().map(|&block| block & !RLCT_SIGNAL_MASK) };
let mut oset = unsafe { oset.as_mut() };
Sys::sigprocmask(
how,
set.as_ref(),
oset.as_deref_mut(), // as_deref_mut for lifetime reasons
)?;
if let Some(oset) = oset {
*oset &= !RLCT_SIGNAL_MASK;
}
Ok(0)
})()
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html>.
///
/// Present in issue 7. Removed in issue 8.
///
/// Use of this function is unspecified in a multi-threaded process.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigrelse(sig: c_int) -> c_int {
let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
unsafe { sigemptyset(pset.as_mut_ptr()) };
let mut set = unsafe { pset.assume_init() };
if unsafe { sigaddset(&raw mut set, sig) } < 0 {
return -1;
}
unsafe { sigprocmask(SIG_UNBLOCK, &raw const set, ptr::null_mut()) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html>.
///
/// Present in issue 7. Removed in issue 8.
///
/// Use of this function is unspecified in a multi-threaded process.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigset(
sig: c_int,
func: Option<extern "C" fn(c_int)>,
) -> Option<extern "C" fn(c_int)> {
let mut old_sa = mem::MaybeUninit::uninit();
let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
let sig_hold: Option<extern "C" fn(c_int)> = unsafe { mem::transmute(SIG_HOLD) };
let sig_err: Option<extern "C" fn(c_int)> = unsafe { mem::transmute(SIG_ERR) };
unsafe { sigemptyset(pset.as_mut_ptr()) };
let mut set = unsafe { pset.assume_init() };
if unsafe { sigaddset(&raw mut set, sig) } < 0 {
return sig_err;
} else {
let is_equal = {
match (func, sig_hold) {
(None, None) => true,
(Some(_), None) | (None, Some(_)) => false,
(Some(f), Some(sh)) => ptr::fn_addr_eq(f, sh),
}
};
if is_equal {
if unsafe { sigaction(sig, ptr::null_mut(), old_sa.as_mut_ptr()) } < 0
|| unsafe { sigprocmask(SIG_BLOCK, &raw const set, &raw mut set) } < 0
{
return sig_err;
}
} else {
let mut sa = sigaction {
sa_handler: func,
sa_flags: c_int::from(0),
sa_restorer: None, // set by platform if applicable
sa_mask: sigset_t::default(),
};
unsafe { sigemptyset(&raw mut sa.sa_mask) };
if unsafe { sigaction(sig, &raw const sa, old_sa.as_mut_ptr()) } < 0
|| unsafe { sigprocmask(SIG_UNBLOCK, &raw const set, &raw mut set) } < 0
{
return sig_err;
}
}
}
if unsafe { sigismember(&raw const set, sig) } == 1 {
return sig_hold;
}
unsafe { old_sa.assume_init().sa_handler }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigsuspend.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigsuspend(sigmask: *const sigset_t) -> c_int {
Err(Sys::sigsuspend(unsafe { &*sigmask })).or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigwait.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigwait(set: *const sigset_t, sig: *mut c_int) -> c_int {
let mut pinfo = mem::MaybeUninit::<siginfo_t>::uninit();
if unsafe { sigtimedwait(set, pinfo.as_mut_ptr(), ptr::null_mut()) } < 0 {
return -1;
}
let info = unsafe { pinfo.assume_init() };
unsafe { (*sig) = info.si_signo };
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigtimedwait.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigtimedwait(
set: *const sigset_t,
// s/siginfo_t/siginfo due to https://github.com/mozilla/cbindgen/issues/621
sig: *mut siginfo,
// POSIX leaves behavior unspecified if this is NULL, but on both Linux and Redox, NULL is used
// to differentiate between sigtimedwait and sigwaitinfo internally
tp: *const timespec,
) -> c_int {
Sys::sigtimedwait(unsafe { &*set }, unsafe { sig.as_mut() }, unsafe {
tp.as_ref()
})
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigwaitinfo.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sigwaitinfo(set: *const sigset_t, sig: *mut siginfo_t) -> c_int {
unsafe { sigtimedwait(set, sig, core::ptr::null()) }
}
pub(crate) const SIGNAL_STRINGS: [&str; 32] = [
"Unknown signal\0",
"Hangup\0",
"Interrupt\0",
"Quit\0",
"Illegal instruction\0",
"Trace/breakpoint trap\0",
"Aborted\0",
"Bus error\0",
"Arithmetic exception\0",
"Killed\0",
"User defined signal 1\0",
"Segmentation fault\0",
"User defined signal 2\0",
"Broken pipe\0",
"Alarm clock\0",
"Terminated\0",
"Stack fault\0",
"Child process status\0",
"Continued\0",
"Stopped (signal)\0",
"Stopped\0",
"Stopped (tty input)\0",
"Stopped (tty output)\0",
"Urgent I/O condition\0",
"CPU time limit exceeded\0",
"File size limit exceeded\0",
"Virtual timer expired\0",
"Profiling timer expired\0",
"Window changed\0",
"I/O possible\0",
"Power failure\0",
"Bad system call\0",
];
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/psignal.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn psignal(sig: c_int, prefix: *const c_char) {
let c_description = usize::try_from(sig)
.ok()
.and_then(|idx| SIGNAL_STRINGS.get(idx))
.unwrap_or(&SIGNAL_STRINGS[0])
.as_ptr();
// fprintf can affect errno, so we save errno and restore it
let old_errno = ERRNO.get();
// POSIX says that "prefix" shall be written if it isn't null or an empty string.
// Otherwise, only the signal description should be written
if prefix.is_null() {
unsafe {
fprintf(stderr, c"%s\n".as_ptr(), c_description);
}
} else {
unsafe {
fprintf(stderr, c"%s: %s\n".as_ptr(), prefix, c_description);
}
}
ERRNO.set(old_errno);
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/psiginfo.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn psiginfo(info: *const siginfo_t, prefix: *const c_char) {
unsafe {
psignal((*info).si_signo, prefix);
}
}