| 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 | |