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::Result;
21pub use libc::signalfd_siginfo as siginfo;
22
23use std::mem;
24use std::os::unix::io::{AsRawFd, RawFd, FromRawFd, OwnedFd, AsFd, BorrowedFd};
25
26libc_bitflags! {
27 pub struct SfdFlags: libc::c_int {
28 SFD_NONBLOCK;
29 SFD_CLOEXEC;
30 }
31}
32
33#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
34pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>();
35
36/// Creates a new file descriptor for reading signals.
37///
38/// **Important:** please read the module level documentation about signal discarding before using
39/// this function!
40///
41/// The `mask` parameter specifies the set of signals that can be accepted via this file descriptor.
42///
43/// A signal must be blocked on every thread in a process, otherwise it won't be visible from
44/// signalfd (the default handler will be invoked instead).
45///
46/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html)
47#[deprecated(since = "0.27.0", note = "Use SignalFd instead")]
48pub fn signalfd<F: AsFd>(fd: Option<F>, mask: &SigSet, flags: SfdFlags) -> Result<OwnedFd> {
49 _signalfd(fd, mask, flags)
50}
51
52fn _signalfd<F: AsFd>(fd: Option<F>, mask: &SigSet, flags: SfdFlags) -> Result<OwnedFd> {
53 let raw_fd: i32 = fd.map_or(default:-1, |x: F|x.as_fd().as_raw_fd());
54 unsafe {
55 Errno::result(libc::signalfd(
56 raw_fd,
57 mask.as_ref(),
58 flags.bits(),
59 )).map(|raw_fd: i32|FromRawFd::from_raw_fd(raw_fd))
60 }
61}
62
63/// A helper struct for creating, reading and closing a `signalfd` instance.
64///
65/// **Important:** please read the module level documentation about signal discarding before using
66/// this struct!
67///
68/// # Examples
69///
70/// ```
71/// # use nix::sys::signalfd::*;
72/// // Set the thread to block the SIGUSR1 signal, otherwise the default handler will be used
73/// let mut mask = SigSet::empty();
74/// mask.add(signal::SIGUSR1);
75/// mask.thread_block().unwrap();
76///
77/// // Signals are queued up on the file descriptor
78/// let mut sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
79///
80/// match sfd.read_signal() {
81/// // we caught a signal
82/// Ok(Some(sig)) => (),
83/// // there were no signals waiting (only happens when the SFD_NONBLOCK flag is set,
84/// // otherwise the read_signal call blocks)
85/// Ok(None) => (),
86/// Err(err) => (), // some error happend
87/// }
88/// ```
89#[derive(Debug)]
90pub struct SignalFd(OwnedFd);
91
92impl SignalFd {
93 pub fn new(mask: &SigSet) -> Result<SignalFd> {
94 Self::with_flags(mask, SfdFlags::empty())
95 }
96
97 pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> {
98 let fd = _signalfd(None::<OwnedFd>, mask, flags)?;
99
100 Ok(SignalFd(fd))
101 }
102
103 pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
104 _signalfd(Some(self.0.as_fd()), mask, SfdFlags::empty()).map(drop)
105 }
106
107 pub fn read_signal(&mut self) -> Result<Option<siginfo>> {
108 let mut buffer = mem::MaybeUninit::<siginfo>::uninit();
109
110 let size = mem::size_of_val(&buffer);
111 let res = Errno::result(unsafe {
112 libc::read(self.0.as_raw_fd(), buffer.as_mut_ptr() as *mut libc::c_void, size)
113 })
114 .map(|r| r as usize);
115 match res {
116 Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })),
117 Ok(_) => unreachable!("partial read on signalfd"),
118 Err(Errno::EAGAIN) => Ok(None),
119 Err(error) => Err(error),
120 }
121 }
122}
123
124impl AsFd for SignalFd {
125 fn as_fd(&self) -> BorrowedFd {
126 self.0.as_fd()
127 }
128}
129impl AsRawFd for SignalFd {
130 fn as_raw_fd(&self) -> RawFd {
131 self.0.as_raw_fd()
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: signalfd_siginfo)) => 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