• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
set_flags(&self) -> Result<(), Error>102     fn set_flags(&self) -> Result<(), Error> {
103         unsafe {
104             let flags = libc::fcntl(self.as_raw_fd(), libc::F_GETFL, 0);
105             if flags == -1 {
106                 return Err(Error::last_os_error());
107             }
108             let flags = flags | libc::O_NONBLOCK | libc::O_CLOEXEC;
109             if libc::fcntl(self.as_raw_fd(), libc::F_SETFL, flags) == -1 {
110                 return Err(Error::last_os_error());
111             }
112         }
113         Ok(())
114     }
wake(&self)115     fn wake(&self) {
116         wake(self.fd, self.method);
117     }
118 }
119 
120 impl AsRawFd for WakeFd {
as_raw_fd(&self) -> RawFd121     fn as_raw_fd(&self) -> RawFd {
122         self.fd
123     }
124 }
125 
126 impl Drop for WakeFd {
drop(&mut self)127     fn drop(&mut self) {
128         unsafe {
129             libc::close(self.fd);
130         }
131     }
132 }
133 
wake(pipe: RawFd, method: WakeMethod)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 = b"X" as *const _ as *const _;
147         match method {
148             WakeMethod::Write => libc::write(pipe, data, 1),
149             WakeMethod::Send => libc::send(pipe, data, 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.
register_raw(signal: c_int, pipe: RawFd) -> Result<SigId, Error>177 pub fn register_raw(signal: c_int, pipe: RawFd) -> Result<SigId, Error> {
178     let res = unsafe { libc::send(pipe, &[] as *const _, 0, MSG_NOWAIT) };
179     let fd = 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 {
186                 fd: pipe,
187                 method: WakeMethod::Write,
188             };
189             fd.set_flags()?;
190             fd
191         }
192     };
193     let action = 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.
register<P>(signal: c_int, pipe: P) -> Result<SigId, Error> where P: IntoRawFd + 'static,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.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.
wakeup()221     fn wakeup() {
222         crate::low_level::raise(libc::SIGUSR1).unwrap();
223     }
224 
225     #[test]
register_with_socket() -> Result<(), Error>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"))]
register_dgram_socket() -> Result<(), Error>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]
register_with_pipe() -> Result<(), Error>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