Coverage Report

Created: 2024-05-20 06:38

/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
}