/rust/registry/src/index.crates.io-6f17d22bba15001f/nix-0.26.2/src/sys/signalfd.rs
Line | Count | Source (jump to first uncovered line) |
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) |
49 | 0 | pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> { |
50 | 0 | unsafe { |
51 | 0 | Errno::result(libc::signalfd( |
52 | 0 | fd as libc::c_int, |
53 | 0 | mask.as_ref(), |
54 | 0 | flags.bits(), |
55 | 0 | )) |
56 | 0 | } |
57 | 0 | } |
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 | 0 | #[derive(Debug, Eq, Hash, PartialEq)] |
86 | | pub struct SignalFd(RawFd); |
87 | | |
88 | | impl SignalFd { |
89 | 0 | pub fn new(mask: &SigSet) -> Result<SignalFd> { |
90 | 0 | Self::with_flags(mask, SfdFlags::empty()) |
91 | 0 | } |
92 | | |
93 | 0 | pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> { |
94 | 0 | let fd = signalfd(SIGNALFD_NEW, mask, flags)?; |
95 | | |
96 | 0 | Ok(SignalFd(fd)) |
97 | 0 | } |
98 | | |
99 | 0 | pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> { |
100 | 0 | signalfd(self.0, mask, SfdFlags::empty()).map(drop) |
101 | 0 | } |
102 | | |
103 | 0 | pub fn read_signal(&mut self) -> Result<Option<siginfo>> { |
104 | 0 | let mut buffer = mem::MaybeUninit::<siginfo>::uninit(); |
105 | 0 |
|
106 | 0 | let size = mem::size_of_val(&buffer); |
107 | 0 | let res = Errno::result(unsafe { |
108 | 0 | libc::read(self.0, buffer.as_mut_ptr() as *mut libc::c_void, size) |
109 | 0 | }) |
110 | 0 | .map(|r| r as usize); |
111 | 0 | match res { |
112 | 0 | Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })), |
113 | 0 | Ok(_) => unreachable!("partial read on signalfd"), |
114 | 0 | Err(Errno::EAGAIN) => Ok(None), |
115 | 0 | Err(error) => Err(error), |
116 | | } |
117 | 0 | } |
118 | | } |
119 | | |
120 | | impl Drop for SignalFd { |
121 | 0 | fn drop(&mut self) { |
122 | 0 | let e = unistd::close(self.0); |
123 | 0 | if !std::thread::panicking() && e == Err(Errno::EBADF) { |
124 | 0 | panic!("Closing an invalid file descriptor!"); |
125 | 0 | }; |
126 | 0 | } |
127 | | } |
128 | | |
129 | | impl AsRawFd for SignalFd { |
130 | 0 | fn as_raw_fd(&self) -> RawFd { |
131 | 0 | self.0 |
132 | 0 | } |
133 | | } |
134 | | |
135 | | impl Iterator for SignalFd { |
136 | | type Item = siginfo; |
137 | | |
138 | 0 | fn next(&mut self) -> Option<Self::Item> { |
139 | 0 | match self.read_signal() { |
140 | 0 | Ok(Some(sig)) => Some(sig), |
141 | 0 | Ok(None) | Err(_) => None, |
142 | | } |
143 | 0 | } |
144 | | } |
145 | | |
146 | | #[cfg(test)] |
147 | | mod 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 | | } |