• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
18 use crate::errno::Errno;
19 pub use crate::sys::signal::{self, SigSet};
20 use crate::unistd;
21 use crate::Result;
22 pub use libc::signalfd_siginfo as siginfo;
23 
24 use std::mem;
25 use std::os::unix::io::{AsRawFd, RawFd};
26 
27 libc_bitflags! {
28     pub struct SfdFlags: libc::c_int {
29         SFD_NONBLOCK;
30         SFD_CLOEXEC;
31     }
32 }
33 
34 pub const SIGNALFD_NEW: RawFd = -1;
35 #[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
36 pub 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)
signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd>49 pub 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)]
86 pub struct SignalFd(RawFd);
87 
88 impl SignalFd {
new(mask: &SigSet) -> Result<SignalFd>89     pub fn new(mask: &SigSet) -> Result<SignalFd> {
90         Self::with_flags(mask, SfdFlags::empty())
91     }
92 
with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd>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 
set_mask(&mut self, mask: &SigSet) -> Result<()>99     pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
100         signalfd(self.0, mask, SfdFlags::empty()).map(drop)
101     }
102 
read_signal(&mut self) -> Result<Option<siginfo>>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 
120 impl Drop for SignalFd {
drop(&mut self)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 
129 impl AsRawFd for SignalFd {
as_raw_fd(&self) -> RawFd130     fn as_raw_fd(&self) -> RawFd {
131         self.0
132     }
133 }
134 
135 impl Iterator for SignalFd {
136     type Item = siginfo;
137 
next(&mut self) -> Option<Self::Item>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)]
147 mod tests {
148     use super::*;
149 
150     #[test]
create_signalfd()151     fn create_signalfd() {
152         let mask = SigSet::empty();
153         SignalFd::new(&mask).unwrap();
154     }
155 
156     #[test]
create_signalfd_with_opts()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]
read_empty_signalfd()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