blob: e3ded1f7aef7cf8738f12f8be7168248330b2147 [file] [log] [blame]
Andrew Walbran12f61402020-10-14 11:10:53 +01001//! 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 libc;
19use crate::unistd;
20use crate::{Error, Result};
21use crate::errno::Errno;
22pub use crate::sys::signal::{self, SigSet};
23pub use libc::signalfd_siginfo as siginfo;
24
25use std::os::unix::io::{RawFd, AsRawFd};
26use std::mem;
27
28
29libc_bitflags!{
30 pub struct SfdFlags: libc::c_int {
31 SFD_NONBLOCK;
32 SFD_CLOEXEC;
33 }
34}
35
36pub const SIGNALFD_NEW: RawFd = -1;
37pub 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)
50pub 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(Clone, Debug, Eq, Hash, PartialEq)]
83pub struct SignalFd(RawFd);
84
85impl SignalFd {
86 pub fn new(mask: &SigSet) -> Result<SignalFd> {
87 Self::with_flags(mask, SfdFlags::empty())
88 }
89
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
96 pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
97 signalfd(self.0, mask, SfdFlags::empty()).map(drop)
98 }
99
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
117impl Drop for SignalFd {
118 fn drop(&mut self) {
119 let _ = unistd::close(self.0);
120 }
121}
122
123impl AsRawFd for SignalFd {
124 fn as_raw_fd(&self) -> RawFd {
125 self.0
126 }
127}
128
129impl Iterator for SignalFd {
130 type Item = siginfo;
131
132 fn next(&mut self) -> Option<Self::Item> {
133 match self.read_signal() {
134 Ok(Some(sig)) => Some(sig),
135 Ok(None) | Err(_) => None,
136 }
137 }
138}
139
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use std::mem;
145 use libc;
146
147
148 #[test]
149 fn check_siginfo_size() {
150 assert_eq!(mem::size_of::<libc::signalfd_siginfo>(), SIGNALFD_SIGINFO_SIZE);
151 }
152
153 #[test]
154 fn create_signalfd() {
155 let mask = SigSet::empty();
156 let fd = SignalFd::new(&mask);
157 assert!(fd.is_ok());
158 }
159
160 #[test]
161 fn create_signalfd_with_opts() {
162 let mask = SigSet::empty();
163 let fd = SignalFd::with_flags(&mask, SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK);
164 assert!(fd.is_ok());
165 }
166
167 #[test]
168 fn read_empty_signalfd() {
169 let mask = SigSet::empty();
170 let mut fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
171
172 let res = fd.read_signal();
173 assert!(res.unwrap().is_none());
174 }
175}