Skip to main content

rusl/process/
signal.rs

1use core::ffi::c_void;
2use core::hint::unreachable_unchecked;
3use core::mem::MaybeUninit;
4
5use sc::syscall;
6
7use crate::platform::{NonNegativeI32, SaMask, SigSetT, SIG_DFL, SIG_IGN};
8
9/// This struct can differ between architectures, it's the same on aarch64 and `x86_64` though.
10#[repr(C)]
11#[expect(clippy::struct_field_names)]
12struct Sigaction {
13    // Fn pointer to an extern C Fn(int) -> ()
14    sa_sigaction: SaSigaction,
15    sa_flags: i32,
16    // Fn pointer to an extern C Fn() -> (), according to the docs shouldn't be used
17    // by applications
18    sa_restorer: unsafe extern "C" fn() -> !,
19    sa_mask: SigSetT,
20}
21
22/// On aarch64 and `x86_64` this is a union, depending on if you specify `SA_SIGINFO` you get one or the other.
23/// The raw `value` takes either 0 or 1 for `default handling` or `ignore` respectively,
24/// Anything other than that or a valid function pointer there is instant UB (likely a segfault)
25#[repr(C)]
26#[derive(Copy, Clone)]
27union SaSigaction {
28    value: usize,
29    sa_handler: unsafe extern "C" fn(i32),
30    sa_sigaction: unsafe extern "C" fn(i32, info: *mut SigInfo, *const c_void),
31}
32
33#[derive(Debug, Copy, Clone)]
34pub enum SaSignalaction {
35    Dfl,
36    Ign,
37    Handler(unsafe extern "C" fn(i32)),
38    SigAction(unsafe extern "C" fn(i32, info: *mut SigInfo, *const c_void)),
39}
40
41#[repr(C)]
42pub struct SigInfo {
43    pub si_signo: i32,
44    pub si_errno: i32,
45    pub si_code: i32,
46    pub si_trapno: i32,
47    pub si_pid: i32,
48    _pad: [i32; 27],
49    _align: [u64; 0],
50}
51
52/// We don't have to do much here, just return from the handler
53unsafe extern "C" fn restorer() -> ! {
54    syscall!(RT_SIGRETURN);
55    unreachable_unchecked()
56}
57
58/// Some (not all) signals it makes sense to handle.
59/// See the [Linux documentation for details](https://man7.org/linux/man-pages/man7/signal.7.html)
60/// Some are expclicitly disallowed (`SigKill`, `SigStop`), some cannot be handled safely by this
61/// library yet `SigAbrt` f.e.
62pub enum CatchSignal {
63    // Keyboard interrupt
64    Int,
65    // Termination signal
66    Term,
67    // Hangup
68    Hup,
69    // Invalid memory reference
70    Segv,
71    // Child stopped or terminated
72    Chld,
73}
74
75impl CatchSignal {
76    fn into_raw(self) -> NonNegativeI32 {
77        match self {
78            CatchSignal::Int => crate::platform::SignalKind::SIGINT.0,
79            CatchSignal::Term => crate::platform::SignalKind::SIGTERM.0,
80            CatchSignal::Hup => crate::platform::SignalKind::SIGHUP.0,
81            CatchSignal::Segv => crate::platform::SignalKind::SIGSEGV.0,
82            CatchSignal::Chld => crate::platform::SignalKind::SIGCHLD.0,
83        }
84    }
85}
86
87/// Attempts to set up a signal handler for the provided signal number
88/// # Errors
89/// Syscall errors if the provided functions doesn't make sense or the `syscall` doesn't make sense.
90/// # Safety
91/// Invalid function pointers is UB.
92/// Additionally, signal handlers have to by async-signal-safe. Essentially meaning that
93/// anything they touch have to be safely accessible concurrently. Some things `Rust` may guarantee
94/// but many it won't.
95#[expect(clippy::cast_possible_truncation)]
96pub unsafe fn add_signal_action(
97    signal: CatchSignal,
98    sigaction: SaSignalaction,
99) -> crate::error::Result<()> {
100    let mut constructed_action: MaybeUninit<Sigaction> = MaybeUninit::uninit();
101    let mut flags = SaMask::SA_RESTART | SaMask::SA_RESTORER;
102    let s_ptr = constructed_action.as_mut_ptr();
103    (*s_ptr).sa_mask = SigSetT::default();
104    (*s_ptr).sa_restorer = restorer;
105    (*s_ptr).sa_sigaction = match sigaction {
106        SaSignalaction::Dfl => SaSigaction { value: SIG_DFL },
107        SaSignalaction::Ign => SaSigaction { value: SIG_IGN },
108        SaSignalaction::Handler(sa_handler) => {
109            // TODO: Double check this
110            flags = SaMask(flags.bits() - SaMask::SA_SIGINFO.bits());
111            SaSigaction { sa_handler }
112        }
113        SaSignalaction::SigAction(sa_sigaction) => {
114            flags |= SaMask::SA_SIGINFO;
115            SaSigaction { sa_sigaction }
116        }
117    };
118    (*s_ptr).sa_flags = flags.bits() as i32;
119    let res = syscall!(
120        RT_SIGACTION,
121        signal.into_raw().value(),
122        constructed_action.as_ptr(),
123        0,
124        8
125    );
126    bail_on_below_zero!(res, "`RT_SIGACTION` syscall failed");
127    Ok(())
128}