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