simple_sigh/
unix.rs

1use std::thread;
2
3use parking_lot::Mutex;
4
5use nix::sys::signal::{SigSet, SigmaskHow, Signal};
6
7use crate::{Error, SigType};
8
9type Callback = dyn FnOnce(SigType) + Send + Sync;
10
11static HANDLER: Mutex<Option<Box<Callback>>> = Mutex::new(None);
12
13
14/// Used to wait for signal handler to be triggered.
15pub struct KillWait {
16  jh: thread::JoinHandle<()>
17}
18
19impl KillWait {
20  /// Wait for a signal to terminate the signal handler.
21  ///
22  /// # Panics
23  /// If the signal handler returns an error this function will panic.
24  pub fn wait(self) {
25    self.jh.join().unwrap();
26  }
27}
28
29
30/// Register signal handler for `SIGINT` and `SIGTERM`.
31///
32/// This function must be called before any other signal management and any
33/// threads are launched.
34///
35/// Note that this doesn't actually set a callback.  That must be done using
36/// [`register()`].
37///
38/// # Errors
39/// [`Error::Internal`] indicates that the internal signal monitoring thread
40/// failed to launch.
41///
42/// # Panics
43/// If the signal mask can not be set this function will panic.
44pub fn init() -> Result<KillWait, Error> {
45  //
46  // Block signals-of-interest on main thread.
47  //
48  let mut ss = SigSet::empty();
49  ss.add(Signal::SIGINT);
50  ss.add(Signal::SIGTERM);
51
52  let mut oldset = SigSet::empty();
53  nix::sys::signal::pthread_sigmask(
54    SigmaskHow::SIG_SETMASK,
55    Some(&ss),
56    Some(&mut oldset)
57  )
58  .unwrap();
59
60  let jh = thread::Builder::new()
61    .name("sigmon".into())
62    .spawn(move || {
63      // Note: Don't need to unblock signals in this thread, because sigwait()
64      // does it implicitly.
65      let mask = unsafe {
66        let mut mask: libc::sigset_t = std::mem::zeroed();
67        libc::sigemptyset(&mut mask);
68        libc::sigaddset(&mut mask, libc::SIGINT);
69        libc::sigaddset(&mut mask, libc::SIGTERM);
70        mask
71      };
72
73      loop {
74        let mut sig: libc::c_int = 0;
75        let ret = unsafe { libc::sigwait(&mask, &mut sig) };
76        if ret == 0 {
77          let signal = Signal::try_from(sig).unwrap();
78
79          let Some(handler) = HANDLER.lock().take() else {
80            // No handler registered.  For now, just continue (wait for a
81            // handler to be registered).
82            continue;
83          };
84
85          match signal {
86            Signal::SIGINT => {
87              handler(SigType::Int);
88              break;
89            }
90            Signal::SIGTERM => {
91              handler(SigType::Term);
92              break;
93            }
94            _ => {}
95          }
96        }
97      }
98    })
99    .map_err(|e| {
100      let msg = format!("Unable to spawn signal monitoring thread; {e}");
101      Error::Internal(msg)
102    })?;
103
104
105  Ok(KillWait { jh })
106}
107
108/// Register a callback function to be called on `SIGINT` or `SIGTERM`.
109///
110/// # Errors
111/// Won't actually fail, but needs to return a `Result` for parity with
112/// implementation on other platforms.
113pub fn register(
114  handler: impl FnOnce(SigType) + 'static + Send + Sync
115) -> Result<(), Error> {
116  let mut g = HANDLER.lock();
117  *g = Some(Box::new(handler));
118  drop(g);
119  Ok(())
120}
121
122// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :