nix/sys/
signalfd.rs

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;
21
22/// Information of a received signal, the return type of [`SignalFd::read_signal()`].
23pub use libc::signalfd_siginfo as siginfo;
24
25use std::mem;
26use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
27
28libc_bitflags! {
29    pub struct SfdFlags: libc::c_int {
30        SFD_NONBLOCK;
31        SFD_CLOEXEC;
32    }
33}
34
35#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
36pub 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#[deprecated(since = "0.27.0", note = "Use SignalFd instead")]
50pub fn signalfd<F: AsFd>(
51    fd: Option<F>,
52    mask: &SigSet,
53    flags: SfdFlags,
54) -> Result<OwnedFd> {
55    _signalfd(fd, mask, flags)
56}
57
58fn _signalfd<F: AsFd>(
59    fd: Option<F>,
60    mask: &SigSet,
61    flags: SfdFlags,
62) -> Result<OwnedFd> {
63    let raw_fd = fd.map_or(-1, |x| x.as_fd().as_raw_fd());
64    unsafe {
65        Errno::result(libc::signalfd(raw_fd, mask.as_ref(), flags.bits()))
66            .map(|raw_fd| FromRawFd::from_raw_fd(raw_fd))
67    }
68}
69
70/// A helper struct for creating, reading and closing a `signalfd` instance.
71///
72/// **Important:** please read the module level documentation about signal discarding before using
73/// this struct!
74///
75/// # Examples
76///
77/// ```
78/// # use nix::sys::signalfd::*;
79/// // Set the thread to block the SIGUSR1 signal, otherwise the default handler will be used
80/// let mut mask = SigSet::empty();
81/// mask.add(signal::SIGUSR1);
82/// mask.thread_block().unwrap();
83///
84/// // Signals are queued up on the file descriptor
85/// let mut sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
86///
87/// match sfd.read_signal() {
88///     // we caught a signal
89///     Ok(Some(sig)) => (),
90///     // there were no signals waiting (only happens when the SFD_NONBLOCK flag is set,
91///     // otherwise the read_signal call blocks)
92///     Ok(None) => (),
93///     Err(err) => (), // some error happend
94/// }
95/// ```
96#[derive(Debug)]
97pub struct SignalFd(OwnedFd);
98
99impl SignalFd {
100    pub fn new(mask: &SigSet) -> Result<SignalFd> {
101        Self::with_flags(mask, SfdFlags::empty())
102    }
103
104    pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> {
105        let fd = _signalfd(None::<OwnedFd>, mask, flags)?;
106
107        Ok(SignalFd(fd))
108    }
109
110    pub fn set_mask(&self, mask: &SigSet) -> Result<()> {
111        self.update(mask, SfdFlags::empty())
112    }
113
114    pub fn read_signal(&self) -> Result<Option<siginfo>> {
115        let mut buffer = mem::MaybeUninit::<siginfo>::uninit();
116
117        let size = mem::size_of_val(&buffer);
118        let res = Errno::result(unsafe {
119            libc::read(self.0.as_raw_fd(), buffer.as_mut_ptr().cast(), size)
120        })
121        .map(|r| r as usize);
122        match res {
123            Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })),
124            Ok(_) => unreachable!("partial read on signalfd"),
125            Err(Errno::EAGAIN) => Ok(None),
126            Err(error) => Err(error),
127        }
128    }
129
130    /// Constructs a `SignalFd` wrapping an existing `OwnedFd`.
131    ///
132    /// # Safety
133    ///
134    /// `OwnedFd` is a valid `SignalFd`.
135    pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
136        Self(fd)
137    }
138
139    fn update(&self, mask: &SigSet, flags: SfdFlags) -> Result<()> {
140        let raw_fd = self.0.as_raw_fd();
141        unsafe {
142            Errno::result(libc::signalfd(raw_fd, mask.as_ref(), flags.bits()))
143                .map(drop)
144        }
145    }
146}
147
148impl AsFd for SignalFd {
149    fn as_fd(&self) -> BorrowedFd {
150        self.0.as_fd()
151    }
152}
153impl AsRawFd for SignalFd {
154    fn as_raw_fd(&self) -> RawFd {
155        self.0.as_raw_fd()
156    }
157}
158
159impl From<SignalFd> for OwnedFd {
160    fn from(value: SignalFd) -> Self {
161        value.0 
162    }
163}
164
165impl Iterator for SignalFd {
166    type Item = siginfo;
167
168    fn next(&mut self) -> Option<Self::Item> {
169        match self.read_signal() {
170            Ok(Some(sig)) => Some(sig),
171            Ok(None) | Err(_) => None,
172        }
173    }
174}