1//! Interface for the `signalfd` syscall. 2//! 3//! # Signal discarding 4//! When a signal can't be delivered to a process (or thread), it will become a pending signal. 5//! Failure to deliver could happen if the signal is blocked by every thread in the process or if 6//! the signal handler is still handling a previous signal. 7//! 8//! If a signal is sent to a process (or thread) that already has a pending signal of the same 9//! type, it will be discarded. This means that if signals of the same type are received faster than 10//! they are processed, some of those signals will be dropped. Because of this limitation, 11//! `signalfd` in itself cannot be used for reliable communication between processes or threads. 12//! 13//! Once the signal is unblocked, or the signal handler is finished, and a signal is still pending 14//! (ie. not consumed from a signalfd) it will be delivered to the signal handler. 15//! 16//! Please note that signal discarding is not specific to `signalfd`, but also happens with regular 17//! signal handlers. 18use crate::errno::Errno; 19pub use crate::sys::signal::{self, SigSet}; 20use crate::unistd; 21use crate::Result; 22pub use libc::signalfd_siginfo as siginfo; 23 24use std::mem; 25use std::os::unix::io::{AsRawFd, RawFd}; 26 27libc_bitflags! { 28 pub struct SfdFlags: libc::c_int { 29 SFD_NONBLOCK; 30 SFD_CLOEXEC; 31 } 32} 33 34pub const SIGNALFD_NEW: RawFd = -1; 35#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")] 36pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>(); 37 38/// Creates a new file descriptor for reading signals. 39/// 40/// **Important:** please read the module level documentation about signal discarding before using 41/// this function! 42/// 43/// The `mask` parameter specifies the set of signals that can be accepted via this file descriptor. 44/// 45/// A signal must be blocked on every thread in a process, otherwise it won't be visible from 46/// signalfd (the default handler will be invoked instead). 47/// 48/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html) 49pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> { 50 unsafe { 51 Errno::result(libc::signalfd( 52 fd as libc::c_int, 53 mask.as_ref(), 54 flags.bits(), 55 )) 56 } 57} 58 59/// A helper struct for creating, reading and closing a `signalfd` instance. 60/// 61/// **Important:** please read the module level documentation about signal discarding before using 62/// this struct! 63/// 64/// # Examples 65/// 66/// ``` 67/// # use nix::sys::signalfd::*; 68/// // Set the thread to block the SIGUSR1 signal, otherwise the default handler will be used 69/// let mut mask = SigSet::empty(); 70/// mask.add(signal::SIGUSR1); 71/// mask.thread_block().unwrap(); 72/// 73/// // Signals are queued up on the file descriptor 74/// let mut sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap(); 75/// 76/// match sfd.read_signal() { 77/// // we caught a signal 78/// Ok(Some(sig)) => (), 79/// // there were no signals waiting (only happens when the SFD_NONBLOCK flag is set, 80/// // otherwise the read_signal call blocks) 81/// Ok(None) => (), 82/// Err(err) => (), // some error happend 83/// } 84/// ``` 85#[derive(Debug, Eq, Hash, PartialEq)] 86pub struct SignalFd(RawFd); 87 88impl SignalFd { 89 pub fn new(mask: &SigSet) -> Result<SignalFd> { 90 Self::with_flags(mask, SfdFlags::empty()) 91 } 92 93 pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> { 94 let fd = signalfd(SIGNALFD_NEW, mask, flags)?; 95 96 Ok(SignalFd(fd)) 97 } 98 99 pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> { 100 signalfd(self.0, mask, SfdFlags::empty()).map(drop) 101 } 102 103 pub fn read_signal(&mut self) -> Result<Option<siginfo>> { 104 let mut buffer = mem::MaybeUninit::<siginfo>::uninit(); 105 106 let size = mem::size_of_val(&buffer); 107 let res = Errno::result(unsafe { 108 libc::read(self.0, buffer.as_mut_ptr() as *mut libc::c_void, size) 109 }) 110 .map(|r| r as usize); 111 match res { 112 Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })), 113 Ok(_) => unreachable!("partial read on signalfd"), 114 Err(Errno::EAGAIN) => Ok(None), 115 Err(error) => Err(error), 116 } 117 } 118} 119 120impl Drop for SignalFd { 121 fn drop(&mut self) { 122 let e = unistd::close(self.0); 123 if !std::thread::panicking() && e == Err(Errno::EBADF) { 124 panic!("Closing an invalid file descriptor!"); 125 }; 126 } 127} 128 129impl AsRawFd for SignalFd { 130 fn as_raw_fd(&self) -> RawFd { 131 self.0 132 } 133} 134 135impl Iterator for SignalFd { 136 type Item = siginfo; 137 138 fn next(&mut self) -> Option<Self::Item> { 139 match self.read_signal() { 140 Ok(Some(sig)) => Some(sig), 141 Ok(None) | Err(_) => None, 142 } 143 } 144} 145 146#[cfg(test)] 147mod tests { 148 use super::*; 149 150 #[test] 151 fn create_signalfd() { 152 let mask = SigSet::empty(); 153 SignalFd::new(&mask).unwrap(); 154 } 155 156 #[test] 157 fn create_signalfd_with_opts() { 158 let mask = SigSet::empty(); 159 SignalFd::with_flags( 160 &mask, 161 SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK, 162 ) 163 .unwrap(); 164 } 165 166 #[test] 167 fn read_empty_signalfd() { 168 let mask = SigSet::empty(); 169 let mut fd = 170 SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap(); 171 172 let res = fd.read_signal(); 173 assert!(res.unwrap().is_none()); 174 } 175} 176