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 { |
24 | fn drop(&mut self) { |
25 | let ptr: *mut Channel = self.0.load(order: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 | |
67 | fn supports_signal(&self, _: c_int) -> bool { |
68 | true |
69 | } |
70 | |
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 | |
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 | |
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 | |