signal_hook/iterator/exfiltrator/
mod.rs

1//! An abstraction over exfiltrating information out of signal handlers.
2//!
3//! The [`Exfiltrator`] trait provides a way to abstract the information extracted from a signal
4//! handler and the way it is extracted out of it.
5//!
6//! The implementations can be used to parametrize the
7//! [`SignalsInfo`][crate::iterator::SignalsInfo] to specify what results are returned.
8//!
9//! # Sealed
10//!
11//! Currently, the trait is sealed and all methods hidden. This is likely temporary, until some
12//! experience with them is gained.
13
14#[cfg(feature = "extended-siginfo")]
15#[cfg_attr(docsrs, doc(cfg(feature = "extended-siginfo")))]
16pub mod origin;
17pub mod raw;
18
19#[cfg(feature = "extended-siginfo")]
20pub use origin::WithOrigin;
21pub use raw::WithRawSiginfo;
22
23use std::sync::atomic::{AtomicBool, Ordering};
24
25use libc::{c_int, siginfo_t};
26
27mod sealed {
28    use std::fmt::Debug;
29
30    use libc::{c_int, siginfo_t};
31
32    /// The actual implementation of the [`Exfiltrator`][super::Exfiltrator].
33    ///
34    /// For now, this is hidden from the public API, but the intention is to move it to a public
35    /// place so users can implement it eventually, once we verify that it works well.
36    ///
37    /// # Safety
38    ///
39    /// The trait is unsafe as the [`Exfiltrator::store`] is called inside the signal handler and
40    /// must be async-signal-safe. Implementing this correctly may be difficult, therefore care
41    /// needs to be taken. One method known to work is encoding the data into an atomic variable.
42    /// Other, less limiting approaches, will be eventually explored.
43    pub unsafe trait Exfiltrator: Debug + Send + Sync + 'static {
44        /// One slot for storing the data.
45        ///
46        /// Each signal will get its one slot of this type, independent of other signals. It can
47        /// store the information in there inside the signal handler and will be loaded from it in
48        /// load.
49        ///
50        /// Each slot is initialized to the [`Default`] value. It is expected this value represents
51        /// „no signal delivered“ state.
52        type Storage: Debug + Default + Send + Sync + 'static;
53
54        /// The type returned to the user.
55        type Output;
56
57        /// If the given signal is supported by this specific exfiltrator.
58        ///
59        /// Not all information is available to all signals, therefore not all exfiltrators must
60        /// support all signals. If `false` is returned, the user is prevented for registering such
61        /// signal number with the given exfiltrator.
62        fn supports_signal(&self, sig: c_int) -> bool;
63
64        /// Puts the signal information inside the slot.
65        ///
66        /// It needs to somehow store the relevant information and the fact that a signal happened.
67        ///
68        /// # Warning
69        ///
70        /// This will be called inside the signal handler. It needs to be async-signal-safe. In
71        /// particular, very small amount of operations are allowed in there. This namely does
72        /// *not* include any locking nor allocation.
73        ///
74        /// It is also possible that multiple store methods are called concurrently; it is up to
75        /// the implementor to deal with that.
76        fn store(&self, slot: &Self::Storage, signal: c_int, info: &siginfo_t);
77
78        /// Loads the signal information from the given slot.
79        ///
80        /// The method shall check if the signal happened (it may be possible to be called without
81        /// the signal previously being delivered; it is up to the implementer to recognize it). It
82        /// is assumed the [`Default`] value is recognized as no signal delivered.
83        ///
84        /// If it was delivered, the method shall extract the relevant information *and reset the
85        /// slot* to the no signal delivered state.
86        ///
87        /// It shall return `Some(value)` if the signal was successfully received and `None` in
88        /// case no signal was delivered.
89        ///
90        /// No blocking shall happen inside this method. It may be called concurrently with
91        /// [`store`][Exfiltrator::store] (due to how signals work, concurrently even inside the
92        /// same thread ‒ a `store` may „interrupt“ a call to `load`). It is up to the implementer
93        /// to deal with that.
94        fn load(&self, slot: &Self::Storage, signal: c_int) -> Option<Self::Output>;
95
96        /// Initialize the given slot for the given signal before the first use.
97        ///
98        /// This is called before the first use of the given slot (and it is annotated with the
99        /// corresponding signal). The default does nothing, this is just an opportunity to
100        /// allocate data lazily (this is called outside of the signal handler, so it doesn't have
101        /// to be async-signal-safe). It will be called at most once for each slot.
102        ///
103        /// Note that you can rely on this being called for correctness, but not for safety (this
104        /// crate calls it before the first use, but a user abusing the trait might not and in such
105        /// case it is OK to eg. lose signals, but not segfault).
106        fn init(&self, slot: &Self::Storage, signal: c_int) {
107            // Suppress unused variable warning without putting the underscores into public
108            // signature.
109            let _ = slot;
110            let _ = signal;
111        }
112    }
113}
114
115/// A trait describing what and how is extracted from signal handlers.
116///
117/// By choosing a specific implementor as the type parameter for
118/// [`SignalsInfo`][crate::iterator::SignalsInfo], one can pick how much and what information is
119/// returned from the iterator.
120pub trait Exfiltrator: sealed::Exfiltrator {}
121
122impl<E: sealed::Exfiltrator> Exfiltrator for E {}
123
124/// An [`Exfiltrator`] providing just the signal numbers.
125///
126/// This is the basic exfiltrator for most needs. For that reason, there's the
127/// [`crate::iterator::Signals`] type alias, to simplify the type names for usual needs.
128#[derive(Clone, Copy, Debug, Default)]
129pub struct SignalOnly;
130
131unsafe impl sealed::Exfiltrator for SignalOnly {
132    type Storage = AtomicBool;
133    fn supports_signal(&self, _: c_int) -> bool {
134        true
135    }
136    type Output = c_int;
137
138    fn store(&self, slot: &Self::Storage, _: c_int, _: &siginfo_t) {
139        slot.store(true, Ordering::SeqCst);
140    }
141
142    fn load(&self, slot: &Self::Storage, signal: c_int) -> Option<Self::Output> {
143        if slot
144            .compare_exchange(true, false, Ordering::SeqCst, Ordering::Relaxed)
145            .is_ok()
146        {
147            Some(signal)
148        } else {
149            None
150        }
151    }
152}