1 | //! Module with the self-pipe pattern. |
2 | //! |
3 | //! One of the common patterns around signals is to have a pipe with both ends in the same program. |
4 | //! Whenever there's a signal, the signal handler writes one byte of garbage data to the write end, |
5 | //! unless the pipe's already full. The application then can handle the read end. |
6 | //! |
7 | //! This has two advantages. First, the real signal action moves outside of the signal handler |
8 | //! where there are a lot less restrictions. Second, it fits nicely in all kinds of asynchronous |
9 | //! loops and has less chance of race conditions. |
10 | //! |
11 | //! This module offers premade functions for the write end (and doesn't insist that it must be a |
12 | //! pipe ‒ anything that can be written to is fine ‒ sockets too, therefore `UnixStream::pair` is a |
13 | //! good candidate). |
14 | //! |
15 | //! If you want to integrate with some asynchronous library, plugging streams from `mio-uds` or |
16 | //! `tokio-uds` libraries should work. |
17 | //! |
18 | //! If it looks too low-level for your needs, the [`iterator`][crate::iterator] module contains some |
19 | //! higher-lever interface that also uses a self-pipe pattern under the hood. |
20 | //! |
21 | //! # Correct order of handling |
22 | //! |
23 | //! A care needs to be taken to avoid race conditions, especially when handling the same signal in |
24 | //! a loop. Specifically, another signal might come when the action for the previous signal is |
25 | //! being taken. The correct order is first to clear the content of the pipe (read some/all data |
26 | //! from it) and then take the action. This way a spurious wakeup can happen (the pipe could wake |
27 | //! up even when no signal came after the signal was taken, because ‒ it arrived between cleaning |
28 | //! the pipe and taking the action). Note that some OS primitives (eg. `select`) suffer from |
29 | //! spurious wakeups themselves (they can claim a FD is readable when it is not true) and blocking |
30 | //! `read` might return prematurely (with eg. `EINTR`). |
31 | //! |
32 | //! The reverse order of first taking the action and then clearing the pipe might lose signals, |
33 | //! which is usually worse. |
34 | //! |
35 | //! This is not a problem with blocking on reading from the pipe (because both the blocking and |
36 | //! cleaning is the same action), but in case of asynchronous handling it matters. |
37 | //! |
38 | //! If you want to combine setting some flags with a self-pipe pattern, the flag needs to be set |
39 | //! first, then the pipe written. On the read end, first the pipe needs to be cleaned, then the |
40 | //! flag and then the action taken. This is what the [`SignalsInfo`][crate::iterator::SignalsInfo] |
41 | //! structure does internally. |
42 | //! |
43 | //! # Write collating |
44 | //! |
45 | //! While unlikely if handled correctly, it is possible the write end is full when a signal comes. |
46 | //! In such case the signal handler simply does nothing. If the write end is full, the read end is |
47 | //! readable and therefore will wake up. On the other hand, blocking in the signal handler would |
48 | //! definitely be a bad idea. |
49 | //! |
50 | //! However, this also means the number of bytes read from the end might be lower than the number |
51 | //! of signals that arrived. This should not generally be a problem, since the OS already collates |
52 | //! signals of the same kind together. |
53 | //! |
54 | //! # Examples |
55 | //! |
56 | //! This example waits for at last one `SIGUSR1` signal to come before continuing (and |
57 | //! terminating). It sends the signal to itself, so it correctly terminates. |
58 | //! |
59 | //! ```rust |
60 | //! use std::io::{Error, Read}; |
61 | //! use std::os::unix::net::UnixStream; |
62 | //! |
63 | //! use signal_hook::consts::SIGUSR1; |
64 | //! use signal_hook::low_level::{pipe, raise}; |
65 | //! |
66 | //! fn main() -> Result<(), Error> { |
67 | //! let (mut read, write) = UnixStream::pair()?; |
68 | //! pipe::register(SIGUSR1, write)?; |
69 | //! // This will write into the pipe write end through the signal handler |
70 | //! raise(SIGUSR1).unwrap(); |
71 | //! let mut buff = [0]; |
72 | //! read.read_exact(&mut buff)?; |
73 | //! println!("Happily terminating" ); |
74 | //! Ok(()) |
75 | //! } |
76 | |
77 | use std::io::{Error, ErrorKind}; |
78 | use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; |
79 | |
80 | use libc::{self, c_int}; |
81 | |
82 | use crate::SigId; |
83 | |
84 | #[cfg (target_os = "aix" )] |
85 | const MSG_NOWAIT: i32 = libc::MSG_NONBLOCK; |
86 | #[cfg (not(target_os = "aix" ))] |
87 | const MSG_NOWAIT: i32 = libc::MSG_DONTWAIT; |
88 | |
89 | #[derive (Copy, Clone)] |
90 | pub(crate) enum WakeMethod { |
91 | Send, |
92 | Write, |
93 | } |
94 | |
95 | struct WakeFd { |
96 | fd: RawFd, |
97 | method: WakeMethod, |
98 | } |
99 | |
100 | impl WakeFd { |
101 | /// Sets close on exec and nonblock on the inner file descriptor. |
102 | fn set_flags(&self) -> Result<(), Error> { |
103 | unsafe { |
104 | let flags: i32 = libc::fcntl(self.as_raw_fd(), cmd:libc::F_GETFL, 0); |
105 | if flags == -1 { |
106 | return Err(Error::last_os_error()); |
107 | } |
108 | let flags: i32 = flags | libc::O_NONBLOCK | libc::O_CLOEXEC; |
109 | if libc::fcntl(self.as_raw_fd(), cmd:libc::F_SETFL, flags) == -1 { |
110 | return Err(Error::last_os_error()); |
111 | } |
112 | } |
113 | Ok(()) |
114 | } |
115 | fn wake(&self) { |
116 | wake(self.fd, self.method); |
117 | } |
118 | } |
119 | |
120 | impl AsRawFd for WakeFd { |
121 | fn as_raw_fd(&self) -> RawFd { |
122 | self.fd |
123 | } |
124 | } |
125 | |
126 | impl Drop for WakeFd { |
127 | fn drop(&mut self) { |
128 | unsafe { |
129 | libc::close(self.fd); |
130 | } |
131 | } |
132 | } |
133 | |
134 | pub(crate) fn wake(pipe: RawFd, method: WakeMethod) { |
135 | unsafe { |
136 | // This writes some data into the pipe. |
137 | // |
138 | // There are two tricks: |
139 | // * First, the crazy cast. The first part turns reference into pointer. The second part |
140 | // turns pointer to u8 into a pointer to void, which is what write requires. |
141 | // * Second, we ignore errors, on purpose. We don't have any means to handling them. The |
142 | // two conceivable errors are EBADFD, if someone passes a non-existent file descriptor or |
143 | // if it is closed. The second is EAGAIN, in which case the pipe is full ‒ there were |
144 | // many signals, but the reader didn't have time to read the data yet. It'll still get |
145 | // woken up, so not fitting another letter in it is fine. |
146 | let data: *const c_void = b"X" as *const _ as *const _; |
147 | match method { |
148 | WakeMethod::Write => libc::write(fd:pipe, buf:data, count:1), |
149 | WakeMethod::Send => libc::send(socket:pipe, buf:data, len:1, MSG_NOWAIT), |
150 | }; |
151 | } |
152 | } |
153 | |
154 | /// Registers a write to a self-pipe whenever there's the signal. |
155 | /// |
156 | /// In this case, the pipe is taken as the `RawFd`. It'll be closed on deregistration. Effectively, |
157 | /// the function takes ownership of the file descriptor. This includes feeling free to set arbitrary |
158 | /// flags on it, including file status flags (that are shared across file descriptors created by |
159 | /// `dup`). |
160 | /// |
161 | /// Note that passing the wrong file descriptor won't cause UB, but can still lead to severe bugs ‒ |
162 | /// like data corruptions in files. Prefer using [`register`] if possible. |
163 | /// |
164 | /// Also, it is perfectly legal for multiple writes to be collated together (if not consumed) and |
165 | /// to generate spurious wakeups (but will not generate spurious *bytes* in the pipe). |
166 | /// |
167 | /// # Internal details |
168 | /// |
169 | /// Internally, it *currently* does following. Note that this is *not* part of the stability |
170 | /// guarantees and may change if necessary. |
171 | /// |
172 | /// * If the file descriptor can be used with [`send`][libc::send], it'll be used together with |
173 | /// [`MSG_DONTWAIT`][libc::MSG_DONTWAIT]. This is tested by sending `0` bytes of data (depending |
174 | /// on the socket type, this might wake the read end with an empty message). |
175 | /// * If it is not possible, the [`O_NONBLOCK`][libc::O_NONBLOCK] will be set on the file |
176 | /// descriptor and [`write`][libc::write] will be used instead. |
177 | pub fn register_raw(signal: c_int, pipe: RawFd) -> Result<SigId, Error> { |
178 | let res: isize = unsafe { libc::send(socket:pipe, &[] as *const _, len:0, MSG_NOWAIT) }; |
179 | let fd: WakeFd = match (res, Error::last_os_error().kind()) { |
180 | (0, _) | (-1, ErrorKind::WouldBlock) => WakeFd { |
181 | fd: pipe, |
182 | method: WakeMethod::Send, |
183 | }, |
184 | _ => { |
185 | let fd: WakeFd = WakeFd { |
186 | fd: pipe, |
187 | method: WakeMethod::Write, |
188 | }; |
189 | fd.set_flags()?; |
190 | fd |
191 | } |
192 | }; |
193 | let action: impl Fn() = move || fd.wake(); |
194 | unsafe { super::register(signal, action) } |
195 | } |
196 | |
197 | /// Registers a write to a self-pipe whenever there's the signal. |
198 | /// |
199 | /// The ownership of pipe is taken and will be closed whenever the created action is unregistered. |
200 | /// |
201 | /// Note that if you want to register the same pipe for multiple signals, there's `try_clone` |
202 | /// method on many unix socket primitives. |
203 | /// |
204 | /// See [`register_raw`] for further details. |
205 | pub fn register<P>(signal: c_int, pipe: P) -> Result<SigId, Error> |
206 | where |
207 | P: IntoRawFd + 'static, |
208 | { |
209 | register_raw(signal, pipe:pipe.into_raw_fd()) |
210 | } |
211 | |
212 | #[cfg (test)] |
213 | mod tests { |
214 | use std::io::Read; |
215 | use std::os::unix::net::{UnixDatagram, UnixStream}; |
216 | |
217 | use super::*; |
218 | |
219 | // Note: multiple tests share the SIGUSR1 signal. This is fine, we only need to know the signal |
220 | // arrives. It's OK to arrive multiple times, from multiple tests. |
221 | fn wakeup() { |
222 | crate::low_level::raise(libc::SIGUSR1).unwrap(); |
223 | } |
224 | |
225 | #[test ] |
226 | fn register_with_socket() -> Result<(), Error> { |
227 | let (mut read, write) = UnixStream::pair()?; |
228 | register(libc::SIGUSR1, write)?; |
229 | wakeup(); |
230 | let mut buff = [0; 1]; |
231 | read.read_exact(&mut buff)?; |
232 | assert_eq!(b"X" , &buff); |
233 | Ok(()) |
234 | } |
235 | |
236 | #[test ] |
237 | #[cfg (not(target_os = "haiku" ))] |
238 | fn register_dgram_socket() -> Result<(), Error> { |
239 | let (read, write) = UnixDatagram::pair()?; |
240 | register(libc::SIGUSR1, write)?; |
241 | wakeup(); |
242 | let mut buff = [0; 1]; |
243 | // The attempt to detect if it is socket can generate an empty message. Therefore, do a few |
244 | // retries. |
245 | for _ in 0..3 { |
246 | let len = read.recv(&mut buff)?; |
247 | if len == 1 && &buff == b"X" { |
248 | return Ok(()); |
249 | } |
250 | } |
251 | panic!("Haven't received the right data" ); |
252 | } |
253 | |
254 | #[test ] |
255 | fn register_with_pipe() -> Result<(), Error> { |
256 | let mut fds = [0; 2]; |
257 | unsafe { assert_eq!(0, libc::pipe(fds.as_mut_ptr())) }; |
258 | register_raw(libc::SIGUSR1, fds[1])?; |
259 | wakeup(); |
260 | let mut buff = [0; 1]; |
261 | unsafe { assert_eq!(1, libc::read(fds[0], buff.as_mut_ptr() as *mut _, 1)) } |
262 | assert_eq!(b"X" , &buff); |
263 | Ok(()) |
264 | } |
265 | } |
266 | |