• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! An exfiltrator providing the raw [`siginfo_t`].
2 
3 // Note on unsafety in this module:
4 // * Implementing an unsafe trait, that one needs to ensure at least store is async-signal-safe.
5 //   That's done by delegating to the Channel (and reading an atomic pointer, but that one is
6 //   primitive op).
7 // * A bit of juggling with atomic and raw pointers. In effect, that is just late lazy
8 //   initialization, the Slot is in line with Option would be, except that it is set atomically
9 //   during the init. Lifetime is ensured by not dropping until the Drop of the whole slot and that
10 //   is checked by taking `&mut self`.
11 
12 use std::sync::atomic::{AtomicPtr, Ordering};
13 
14 use libc::{c_int, siginfo_t};
15 
16 use super::sealed::Exfiltrator;
17 use crate::low_level::channel::Channel;
18 
19 #[doc(hidden)]
20 #[derive(Default, Debug)]
21 pub struct Slot(AtomicPtr<Channel<siginfo_t>>);
22 
23 impl Drop for Slot {
drop(&mut self)24     fn drop(&mut self) {
25         let ptr = self.0.load(Ordering::Acquire);
26         if !ptr.is_null() {
27             drop(unsafe { Box::from_raw(ptr) });
28         }
29     }
30 }
31 
32 /// The [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that produces the raw
33 /// [`libc::siginfo_t`]. Note that it might look differently on different OSes and its API is a
34 /// little bit more limited than its C counterpart.
35 ///
36 /// You might prefer the [`WithOrigin`][super::WithOrigin] if you simply need information about the
37 /// origin of the signal.
38 ///
39 /// # Examples
40 ///
41 /// ```rust
42 /// # use signal_hook::consts::SIGUSR1;
43 /// # use signal_hook::iterator::SignalsInfo;
44 /// # use signal_hook::iterator::exfiltrator::WithRawSiginfo;
45 /// #
46 /// # fn main() -> Result<(), std::io::Error> {
47 /// // Subscribe to SIGUSR1, with information about the process.
48 /// let mut signals = SignalsInfo::<WithRawSiginfo>::new(&[SIGUSR1])?;
49 ///
50 /// // Send ourselves a signal.
51 /// signal_hook::low_level::raise(SIGUSR1)?;
52 ///
53 /// // Grab the signal and look into the details.
54 /// let received = signals.wait().next().unwrap();
55 ///
56 /// // Not much else is useful in a cross-platform way :-(
57 /// assert_eq!(SIGUSR1, received.si_signo);
58 /// # Ok(()) }
59 /// ```
60 #[derive(Copy, Clone, Debug, Default)]
61 pub struct WithRawSiginfo;
62 
63 unsafe impl Exfiltrator for WithRawSiginfo {
64     type Storage = Slot;
65     type Output = siginfo_t;
66 
supports_signal(&self, _: c_int) -> bool67     fn supports_signal(&self, _: c_int) -> bool {
68         true
69     }
70 
store(&self, slot: &Slot, _: c_int, info: &siginfo_t)71     fn store(&self, slot: &Slot, _: c_int, info: &siginfo_t) {
72         let info = *info;
73         // Condition just not to crash if someone forgot to call init.
74         //
75         // Lifetime is from init to our own drop, and drop needs &mut self.
76         if let Some(slot) = unsafe { slot.0.load(Ordering::Acquire).as_ref() } {
77             slot.send(info);
78         }
79     }
80 
load(&self, slot: &Slot, _: libc::c_int) -> Option<siginfo_t>81     fn load(&self, slot: &Slot, _: libc::c_int) -> Option<siginfo_t> {
82         let slot = unsafe { slot.0.load(Ordering::Acquire).as_ref() };
83         // Condition just not to crash if someone forgot to call init.
84         slot.and_then(|s| s.recv())
85     }
86 
init(&self, slot: &Self::Storage, _: c_int)87     fn init(&self, slot: &Self::Storage, _: c_int) {
88         let new = Box::default();
89         let old = slot.0.swap(Box::into_raw(new), Ordering::Release);
90         // We leak the pointer on purpose here. This is invalid state anyway and must not happen,
91         // but if it still does, we can't drop that while some other thread might still be having
92         // the raw pointer.
93         assert!(old.is_null(), "Init called multiple times");
94     }
95 }
96