signal_hook/low_level/pipe.rs
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
78use std::io::{Error, ErrorKind};
79use std::os::fd::{AsFd, BorrowedFd, OwnedFd};
80use std::os::unix::io::AsRawFd;
81
82use libc::{self, c_int};
83
84use crate::SigId;
85
86#[cfg(target_os = "aix")]
87const MSG_NOWAIT: i32 = libc::MSG_NONBLOCK;
88#[cfg(not(target_os = "aix"))]
89const MSG_NOWAIT: i32 = libc::MSG_DONTWAIT;
90
91#[derive(Copy, Clone)]
92pub(crate) enum WakeMethod {
93 Send,
94 Write,
95}
96
97struct WakeFd {
98 fd: OwnedFd,
99 method: WakeMethod,
100}
101
102impl WakeFd {
103 /// Sets close on exec and nonblock on the inner file descriptor.
104 fn set_flags(&self) -> Result<(), Error> {
105 unsafe {
106 let flags = libc::fcntl(self.fd.as_raw_fd(), libc::F_GETFL, 0);
107 if flags == -1 {
108 return Err(Error::last_os_error());
109 }
110 let flags = flags | libc::O_NONBLOCK | libc::O_CLOEXEC;
111 if libc::fcntl(self.fd.as_raw_fd(), libc::F_SETFL, flags) == -1 {
112 return Err(Error::last_os_error());
113 }
114 }
115 Ok(())
116 }
117
118 fn wake(&self) {
119 wake(self.fd.as_fd(), self.method);
120 }
121}
122
123impl AsFd for WakeFd {
124 fn as_fd(&self) -> BorrowedFd<'_> {
125 self.fd.as_fd()
126 }
127}
128
129impl Drop for WakeFd {
130 fn drop(&mut self) {
131 unsafe {
132 libc::close(self.fd.as_raw_fd());
133 }
134 }
135}
136
137pub(crate) fn wake(pipe: BorrowedFd<'_>, method: WakeMethod) {
138 unsafe {
139 // This writes some data into the pipe.
140 //
141 // There are two tricks:
142 // * First, the crazy cast. The first part turns reference into pointer. The second part
143 // turns pointer to u8 into a pointer to void, which is what write requires.
144 // * Second, we ignore errors, on purpose. We don't have any means to handling them. The
145 // two conceivable errors are EBADFD, if someone passes a non-existent file descriptor or
146 // if it is closed. The second is EAGAIN, in which case the pipe is full ‒ there were
147 // many signals, but the reader didn't have time to read the data yet. It'll still get
148 // woken up, so not fitting another letter in it is fine.
149 let data = b"X" as *const _ as *const _;
150 match method {
151 WakeMethod::Write => libc::write(pipe.as_raw_fd(), data, 1),
152 WakeMethod::Send => libc::send(pipe.as_raw_fd(), data, 1, MSG_NOWAIT),
153 };
154 }
155}
156
157/// Registers a write to a self-pipe whenever there's the signal.
158///
159/// In this case, the pipe is taken as the `RawFd`. It'll be closed on deregistration. Effectively,
160/// the function takes ownership of the file descriptor. This includes feeling free to set arbitrary
161/// flags on it, including file status flags (that are shared across file descriptors created by
162/// `dup`).
163///
164/// Note that passing the wrong file descriptor won't cause UB, but can still lead to severe bugs ‒
165/// like data corruptions in files. Prefer using [`register`] if possible.
166///
167/// Also, it is perfectly legal for multiple writes to be collated together (if not consumed) and
168/// to generate spurious wakeups (but will not generate spurious *bytes* in the pipe).
169///
170/// # Internal details
171///
172/// Internally, it *currently* does following. Note that this is *not* part of the stability
173/// guarantees and may change if necessary.
174///
175/// * If the file descriptor can be used with [`send`][libc::send], it'll be used together with
176/// [`MSG_DONTWAIT`][libc::MSG_DONTWAIT]. This is tested by sending `0` bytes of data (depending
177/// on the socket type, this might wake the read end with an empty message).
178/// * If it is not possible, the [`O_NONBLOCK`][libc::O_NONBLOCK] will be set on the file
179/// descriptor and [`write`][libc::write] will be used instead.
180pub fn register_raw(signal: c_int, pipe: OwnedFd) -> Result<SigId, Error> {
181 let res = unsafe { libc::send(pipe.as_raw_fd(), &[] as *const _, 0, MSG_NOWAIT) };
182 let fd = match (res, Error::last_os_error().kind()) {
183 (0, _) | (-1, ErrorKind::WouldBlock) => WakeFd {
184 fd: pipe,
185 method: WakeMethod::Send,
186 },
187 _ => {
188 let fd = WakeFd {
189 fd: pipe,
190 method: WakeMethod::Write,
191 };
192 fd.set_flags()?;
193 fd
194 }
195 };
196 let action = move || fd.wake();
197 unsafe { super::register(signal, action) }
198}
199
200/// Registers a write to a self-pipe whenever there's the signal.
201///
202/// The ownership of pipe is taken and will be closed whenever the created action is unregistered.
203///
204/// Note that if you want to register the same pipe for multiple signals, there's `try_clone`
205/// method on many unix socket primitives.
206///
207/// See [`register_raw`] for further details.
208pub fn register<P>(signal: c_int, pipe: P) -> Result<SigId, Error>
209where
210 P: Into<OwnedFd> + 'static,
211{
212 register_raw(signal, pipe.into())
213}
214
215#[cfg(test)]
216mod tests {
217 use std::io::Read;
218 use std::os::fd::FromRawFd;
219 use std::os::unix::net::{UnixDatagram, UnixStream};
220
221 use super::*;
222
223 // Note: multiple tests share the SIGUSR1 signal. This is fine, we only need to know the signal
224 // arrives. It's OK to arrive multiple times, from multiple tests.
225 fn wakeup() {
226 crate::low_level::raise(libc::SIGUSR1).unwrap();
227 }
228
229 #[test]
230 fn register_with_socket() -> Result<(), Error> {
231 let (mut read, write) = UnixStream::pair()?;
232 register(libc::SIGUSR1, write)?;
233 wakeup();
234 let mut buff = [0; 1];
235 read.read_exact(&mut buff)?;
236 assert_eq!(b"X", &buff);
237 Ok(())
238 }
239
240 #[test]
241 #[cfg(not(target_os = "haiku"))]
242 fn register_dgram_socket() -> Result<(), Error> {
243 let (read, write) = UnixDatagram::pair()?;
244 register(libc::SIGUSR1, write)?;
245 wakeup();
246 let mut buff = [0; 1];
247 // The attempt to detect if it is socket can generate an empty message. Therefore, do a few
248 // retries.
249 for _ in 0..3 {
250 let len = read.recv(&mut buff)?;
251 if len == 1 && &buff == b"X" {
252 return Ok(());
253 }
254 }
255 panic!("Haven't received the right data");
256 }
257
258 #[test]
259 fn register_with_pipe() -> Result<(), Error> {
260 let mut fds = [0; 2];
261 unsafe { assert_eq!(0, libc::pipe(fds.as_mut_ptr())) };
262 register_raw(libc::SIGUSR1, unsafe { OwnedFd::from_raw_fd(fds[1]) })?;
263 wakeup();
264 let mut buff = [0; 1];
265 unsafe { assert_eq!(1, libc::read(fds[0], buff.as_mut_ptr() as *mut _, 1)) }
266 assert_eq!(b"X", &buff);
267 Ok(())
268 }
269}