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