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")))] 16 pub mod origin; 17 pub mod raw; 18 19 #[cfg(feature = "extended-siginfo")] 20 pub use origin::WithOrigin; 21 pub use raw::WithRawSiginfo; 22 23 use std::sync::atomic::{AtomicBool, Ordering}; 24 25 use libc::{c_int, siginfo_t}; 26 27 mod 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. supports_signal(&self, sig: c_int) -> bool62 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. store(&self, slot: &Self::Storage, signal: c_int, info: &siginfo_t)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. load(&self, slot: &Self::Storage, signal: c_int) -> Option<Self::Output>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). init(&self, slot: &Self::Storage, signal: c_int)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. 120 pub trait Exfiltrator: sealed::Exfiltrator {} 121 122 impl<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)] 129 pub struct SignalOnly; 130 131 unsafe impl sealed::Exfiltrator for SignalOnly { 132 type Storage = AtomicBool; supports_signal(&self, _: c_int) -> bool133 fn supports_signal(&self, _: c_int) -> bool { 134 true 135 } 136 type Output = c_int; 137 store(&self, slot: &Self::Storage, _: c_int, _: &siginfo_t)138 fn store(&self, slot: &Self::Storage, _: c_int, _: &siginfo_t) { 139 slot.store(true, Ordering::SeqCst); 140 } 141 load(&self, slot: &Self::Storage, signal: c_int) -> Option<Self::Output>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 } 153