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