Files
RedBear-OS/src/header/signal/mod.rs
T
2024-09-27 11:12:19 +02:00

486 lines
13 KiB
Rust

//! signal implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/signal.h.html
use core::{mem, ptr};
use cbitset::BitSet;
use crate::{
c_str::CStr,
error::{Errno, ResultExt},
header::{errno, time::timespec},
platform::{self, types::*, Pal, PalSignal, Sys},
};
pub use self::sys::*;
use super::{errno::EFAULT, unistd};
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
pub mod sys;
#[cfg(target_os = "redox")]
#[path = "redox.rs"]
pub mod sys;
type SigSet = BitSet<[c_ulong; 1]>;
pub(crate) const SIG_DFL: usize = 0;
pub(crate) const SIG_IGN: usize = 1;
pub(crate) const SIG_ERR: isize = -1;
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 SI_QUEUE: c_int = -1;
pub const SI_USER: c_int = 0;
#[repr(C)]
#[derive(Clone, Debug)]
/// cbindgen:ignore
pub struct sigaction {
pub sa_handler: Option<extern "C" fn(c_int)>,
pub sa_flags: c_ulong,
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,
}
// 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,
}
#[no_mangle]
pub extern "C" fn _cbindgen_export_siginfo(a: siginfo) {}
#[derive(Clone, Copy)]
#[repr(C)]
pub union sigval {
pub sival_int: c_int,
pub sival_ptr: *mut c_void,
}
/// cbindgen:ignore
pub type sigset_t = c_ulonglong;
/// cbindgen:ignore
pub type siginfo_t = siginfo;
pub type stack_t = sigaltstack;
#[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()
}
#[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()
}
#[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()
}
#[no_mangle]
pub unsafe extern "C" fn pthread_kill(thread: pthread_t, sig: c_int) -> c_int {
let os_tid = {
let pthread = &*(thread as *const crate::pthread::Pthread);
pthread.os_tid.get().read()
};
crate::header::pthread::e(Sys::rlct_kill(os_tid, sig as usize))
}
#[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 sigprocmask(how, set, oldset) == 0 {
0
} else {
//TODO: Fix race
platform::ERRNO.get()
}
}
#[no_mangle]
pub extern "C" fn raise(sig: c_int) -> c_int {
Sys::raise(sig).map(|()| 0).or_minus_one_errno()
}
#[no_mangle]
pub unsafe extern "C" fn sigaction(
sig: c_int,
act: *const sigaction,
oact: *mut sigaction,
) -> c_int {
Sys::sigaction(sig, act.as_ref(), oact.as_mut())
.map(|()| 0)
.or_minus_one_errno()
}
#[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 as *mut SigSet).as_mut() } {
set.insert(signo as usize - 1); // 0-indexed usize, please!
}
0
}
#[no_mangle]
pub unsafe extern "C" fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int {
Sys::sigaltstack(ss.as_ref(), old_ss.as_mut())
.map(|()| 0)
.or_minus_one_errno()
}
#[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 as *mut SigSet).as_mut() } {
set.remove(signo as usize - 1); // 0-indexed usize, please!
}
0
}
#[no_mangle]
pub unsafe extern "C" fn sigemptyset(set: *mut sigset_t) -> c_int {
if let Some(set) = (set as *mut SigSet).as_mut() {
set.clear();
}
0
}
#[no_mangle]
pub unsafe extern "C" fn sigfillset(set: *mut sigset_t) -> c_int {
if let Some(set) = (set as *mut SigSet).as_mut() {
set.fill(.., true);
}
0
}
#[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 sigaddset(&mut set, sig) < 0 {
return -1;
}
sigprocmask(SIG_BLOCK, &set, ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn sigignore(sig: c_int) -> c_int {
let mut psa = mem::MaybeUninit::<sigaction>::uninit();
unsafe { sigemptyset(&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, &mut sa, ptr::null_mut()) }
}
#[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_ulong;
} else {
sa.sa_flags |= SA_RESTART as c_ulong;
}
unsafe { sigaction(sig, &mut sa, ptr::null_mut()) }
}
#[no_mangle]
pub unsafe extern "C" fn sigismember(set: *const sigset_t, signo: c_int) -> c_int {
if signo <= 0 || signo as usize > NSIG {
platform::ERRNO.set(errno::EINVAL);
return -1;
}
if let Some(set) = unsafe { (set as *mut SigSet).as_mut() } {
if set.contains(signo as usize - 1) {
return 1;
}
}
0
}
#[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, &sa, old_sa.as_mut_ptr()) } < 0 {
mem::forget(old_sa);
return unsafe { mem::transmute(SIG_ERR) };
}
unsafe { old_sa.assume_init() }.sa_handler
}
#[no_mangle]
pub unsafe extern "C" fn sigpause(sig: c_int) -> c_int {
let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
sigprocmask(0, ptr::null_mut(), pset.as_mut_ptr());
let mut set = pset.assume_init();
sigdelset(&mut set, sig);
sigsuspend(&set)
}
#[no_mangle]
pub unsafe extern "C" fn sigpending(set: *mut sigset_t) -> c_int {
(|| Sys::sigpending(set.as_mut().ok_or(Errno(EFAULT))?))()
.map(|()| 0)
.or_minus_one_errno()
}
const BELOW_SIGRTMIN_MASK: sigset_t = (1 << SIGRTMIN) - 1;
const STANDARD_SIG_MASK: sigset_t = (1 << 32) - 1;
const RLCT_SIGNAL_MASK: sigset_t = BELOW_SIGRTMIN_MASK & !STANDARD_SIG_MASK;
#[no_mangle]
pub unsafe extern "C" fn sigprocmask(
how: c_int,
set: *const sigset_t,
oset: *mut sigset_t,
) -> c_int {
(|| {
let set = set.as_ref().map(|&block| block & !RLCT_SIGNAL_MASK);
let mut oset = 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()
}
#[no_mangle]
pub unsafe extern "C" fn sigrelse(sig: c_int) -> c_int {
let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
sigemptyset(pset.as_mut_ptr());
let mut set = pset.assume_init();
if sigaddset(&mut set, sig) < 0 {
return -1;
}
sigprocmask(SIG_UNBLOCK, &mut set, ptr::null_mut())
}
#[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)> = mem::transmute(SIG_HOLD);
let sig_err: Option<extern "C" fn(c_int)> = mem::transmute(SIG_ERR);
sigemptyset(pset.as_mut_ptr());
let mut set = pset.assume_init();
if sigaddset(&mut set, sig) < 0 {
return sig_err;
} else {
if func == sig_hold {
if sigaction(sig, ptr::null_mut(), old_sa.as_mut_ptr()) < 0
|| sigprocmask(SIG_BLOCK, &mut set, &mut set) < 0
{
mem::forget(old_sa);
return sig_err;
}
} else {
let mut sa = sigaction {
sa_handler: func,
sa_flags: 0 as c_ulong,
sa_restorer: None, // set by platform if applicable
sa_mask: sigset_t::default(),
};
sigemptyset(&mut sa.sa_mask);
if sigaction(sig, &sa, old_sa.as_mut_ptr()) < 0
|| sigprocmask(SIG_UNBLOCK, &mut set, &mut set) < 0
{
mem::forget(old_sa);
return sig_err;
}
}
}
if sigismember(&mut set, sig) == 1 {
return sig_hold;
}
old_sa.assume_init().sa_handler
}
#[no_mangle]
pub unsafe extern "C" fn sigsuspend(sigmask: *const sigset_t) -> c_int {
Err(Sys::sigsuspend(&*sigmask)).or_minus_one_errno()
}
#[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 sigtimedwait(set, pinfo.as_mut_ptr(), ptr::null_mut()) < 0 {
return -1;
}
let info = pinfo.assume_init();
(*sig) = info.si_signo;
0
}
#[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(&*set, sig.as_mut(), tp.as_ref())
.map(|()| 0)
.or_minus_one_errno()
}
#[no_mangle]
pub unsafe extern "C" fn sigwaitinfo(set: *const sigset_t, sig: *mut siginfo_t) -> c_int {
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",
];
#[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]);
let description = &c_description[..c_description.len() - 1];
let prefix = CStr::from_ptr(prefix).to_string_lossy();
// TODO: stack vec or print directly?
let string = alloc::format!("{prefix}:{description}\n");
// TODO: better internal libc API?
let _ = unistd::write(
unistd::STDERR_FILENO,
string.as_bytes().as_ptr().cast(),
string.as_bytes().len(),
);
}
#[no_mangle]
pub unsafe extern "C" fn psiginfo(info: *const siginfo_t, prefix: *const c_char) {
let siginfo_t {
si_code,
si_signo,
si_pid,
si_uid,
si_errno,
si_addr,
si_status,
si_value,
} = &*info;
let sival_ptr = si_value.sival_ptr;
let prefix = CStr::from_ptr(prefix).to_string_lossy();
// TODO: stack vec or print directly?
let string = alloc::format!(
"{prefix}:siginfo_t {{
si_code: {si_code}
si_signo: {si_signo}
si_pid: {si_pid}
si_uid: {si_uid}
si_errno: {si_errno}
si_addr: {si_addr:p}
si_status: {si_status}
si_value: {sival_ptr:p}
}}
"
);
// TODO: better internal libc API?
let _ = unistd::write(
unistd::STDERR_FILENO,
string.as_bytes().as_ptr().cast(),
string.as_bytes().len(),
);
}