devela/sys/os/linux/fns/signal.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
// devela::sys::os::linux::fns::signal
//
//! signal related functions
//
use crate::{
linux_sys_rt_sigaction, AtomicOrdering, AtomicPtr, LinuxSigaction, LinuxSigset,
LINUX_SIGACTION as SA,
};
use core::{mem::transmute, ptr::null_mut};
/// Registers multiple signals using a handler function that never returns.
///
/// # Examples
/// ```no_run
/// use std::{process::exit, time::Duration, thread::sleep};
/// use devela::{linux_sig_handler_no_return, LINUX_SIGNAL as LS};
///
/// fn handler(sig: i32) -> ! {
/// println!("\nsignal `{sig}` received! exiting. . .");
/// exit(1);
/// }
///
/// fn main() {
/// // handle all the signals used to quit
/// linux_sig_handler_no_return(handler, &[LS::SIGINT, LS::SIGQUIT, LS::SIGSEGV, LS::SIGABRT]);
/// // press Ctrl+C before the time expires to catch the SIGINT signal
/// sleep(Duration::from_secs(2));
/// println!("bye");
/// }
/// ```
///
/// # Rationale
/// It would be very nice to be able to register a signal handler that can return,
/// unfortunately I've been unable to make it work.
///
/// Apparently the handler needs the [`SA_RESTORER`] flag to run, but doing so
/// without providing a restorer function produces a segmentation fault. The only
/// way to simply avoid that is to not return from the handler function.
///
/// The `libc` library sets it up correctly but doing so manually seems a too
/// complex too low level task.
///
/// [`SA_RESTORER`]: SA::SA_RESTORER
pub fn linux_sig_handler_no_return(handler: fn(i32) -> !, signals: &[i32]) {
// We store the given `handler` function in a static to be able to call it
// from the new extern function which can't capture its environment.
static HANDLER: AtomicPtr<fn(i32) -> !> = AtomicPtr::new(null_mut());
HANDLER.store(handler as *mut _, AtomicOrdering::SeqCst);
extern "C" fn c_handler(sig: i32) {
let handler = HANDLER.load(AtomicOrdering::SeqCst);
if !handler.is_null() {
#[allow(clippy::crosspointer_transmute)]
// SAFETY: The non-null pointer is originally created from a `fn(i32) -> !` pointer.
let handler = unsafe { transmute::<*mut fn(i32) -> !, fn(i32) -> !>(handler) };
handler(sig);
}
}
// Apparently Rust doesn't call the handler unless we set the SA_RESTORER flag.
let flags = SA::SA_RESETHAND | SA::SA_RESTORER;
let mask = LinuxSigset::default();
let sigaction = LinuxSigaction::new(c_handler, flags, mask);
for s in signals {
// make sure the signal is a valid number
if (1..=36).contains(s) {
unsafe {
let _ = linux_sys_rt_sigaction(*s, &sigaction, null_mut(), LinuxSigset::size());
}
}
}
}