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
12use std::sync::atomic::{AtomicPtr, Ordering};
13
14use libc::{c_int, siginfo_t};
15
16use super::sealed::Exfiltrator;
17use crate::low_level::channel::Channel;
18
19#[doc(hidden)]
20#[derive(Default, Debug)]
21pub struct Slot(AtomicPtr<Channel<siginfo_t>>);
22
23impl 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)]
61pub struct WithRawSiginfo;
62
63unsafe 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