base/sys/linux/
signalfd.rs

1// Copyright 2017 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::fs::File;
6use std::mem;
7use std::os::raw::c_int;
8use std::os::unix::io::AsRawFd;
9use std::os::unix::io::FromRawFd;
10use std::os::unix::io::RawFd;
11use std::result;
12
13use libc::c_void;
14use libc::read;
15use libc::signalfd;
16use libc::signalfd_siginfo;
17use libc::EAGAIN;
18use libc::SFD_CLOEXEC;
19use libc::SFD_NONBLOCK;
20use log::error;
21use remain::sorted;
22use thiserror::Error;
23
24use super::signal;
25use super::Error as ErrnoError;
26use super::RawDescriptor;
27use crate::descriptor::AsRawDescriptor;
28
29#[sorted]
30#[derive(Error, Debug)]
31pub enum Error {
32    /// Failed to block the signal when creating signalfd.
33    #[error("failed to block the signal when creating signalfd: {0}")]
34    CreateBlockSignal(signal::Error),
35    /// Failed to create a new signalfd.
36    #[error("failed to create a new signalfd: {0}")]
37    CreateSignalFd(ErrnoError),
38    /// Failed to construct sigset when creating signalfd.
39    #[error("failed to construct sigset when creating signalfd: {0}")]
40    CreateSigset(ErrnoError),
41    /// Signalfd could be read, but didn't return a full siginfo struct.
42    /// This wraps the number of bytes that were actually read.
43    #[error("signalfd failed to return a full siginfo struct, read only {0} bytes")]
44    SignalFdPartialRead(usize),
45    /// Unable to read from signalfd.
46    #[error("unable to read from signalfd: {0}")]
47    SignalFdRead(ErrnoError),
48}
49
50pub type Result<T> = result::Result<T, Error>;
51
52/// A safe wrapper around a Linux signalfd (man 2 signalfd).
53///
54/// A signalfd can be used for non-synchronous signals (such as SIGCHLD) so that
55/// signals can be processed without the use of a signal handler.
56pub struct SignalFd {
57    signalfd: File,
58    signal: c_int,
59}
60
61impl SignalFd {
62    /// Creates a new SignalFd for the given signal, blocking the normal handler
63    /// for the signal as well. Since we mask out the normal handler, this is
64    /// a risky operation - signal masking will persist across fork and even
65    /// **exec** so the user of SignalFd should think long and hard about
66    /// when to mask signals.
67    pub fn new(signal: c_int) -> Result<SignalFd> {
68        let sigset = signal::create_sigset(&[signal]).map_err(Error::CreateSigset)?;
69
70        // SAFETY:
71        // This is safe as we check the return value and know that fd is valid.
72        let fd = unsafe { signalfd(-1, &sigset, SFD_CLOEXEC | SFD_NONBLOCK) };
73        if fd < 0 {
74            return Err(Error::CreateSignalFd(ErrnoError::last()));
75        }
76
77        // Mask out the normal handler for the signal.
78        signal::block_signal(signal).map_err(Error::CreateBlockSignal)?;
79
80        // SAFETY:
81        // This is safe because we checked fd for success and know the
82        // kernel gave us an fd that we own.
83        unsafe {
84            Ok(SignalFd {
85                signalfd: File::from_raw_fd(fd),
86                signal,
87            })
88        }
89    }
90
91    /// Read a siginfo struct from the signalfd, if available.
92    pub fn read(&self) -> Result<Option<signalfd_siginfo>> {
93        // SAFETY:
94        // signalfd_siginfo doesn't have a default, so just zero it.
95        let mut siginfo: signalfd_siginfo = unsafe { mem::zeroed() };
96        let siginfo_size = mem::size_of::<signalfd_siginfo>();
97
98        // SAFETY:
99        // This read is safe since we've got the space allocated for a
100        // single signalfd_siginfo, and that's exactly how much we're
101        // reading. Handling of EINTR is not required since SFD_NONBLOCK
102        // was specified. signalfds will always read in increments of
103        // sizeof(signalfd_siginfo); see man 2 signalfd.
104        let ret = unsafe {
105            read(
106                self.signalfd.as_raw_fd(),
107                &mut siginfo as *mut signalfd_siginfo as *mut c_void,
108                siginfo_size,
109            )
110        };
111
112        if ret < 0 {
113            let err = ErrnoError::last();
114            if err.errno() == EAGAIN {
115                Ok(None)
116            } else {
117                Err(Error::SignalFdRead(err))
118            }
119        } else if ret == (siginfo_size as isize) {
120            Ok(Some(siginfo))
121        } else {
122            Err(Error::SignalFdPartialRead(ret as usize))
123        }
124    }
125}
126
127impl AsRawFd for SignalFd {
128    fn as_raw_fd(&self) -> RawFd {
129        self.signalfd.as_raw_fd()
130    }
131}
132
133impl AsRawDescriptor for SignalFd {
134    fn as_raw_descriptor(&self) -> RawDescriptor {
135        self.signalfd.as_raw_descriptor()
136    }
137}
138
139impl Drop for SignalFd {
140    fn drop(&mut self) {
141        // This is thread-safe and safe in the sense that we're doing what
142        // was promised - unmasking the signal when we go out of scope.
143        let res = signal::unblock_signal(self.signal);
144        if let Err(e) = res {
145            error!("signalfd failed to unblock signal {}: {}", self.signal, e);
146        }
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use std::mem;
153    use std::ptr::null;
154
155    use libc::pthread_sigmask;
156    use libc::raise;
157    use libc::sigismember;
158    use libc::sigset_t;
159
160    use super::super::signal::SIGRTMIN;
161    use super::*;
162
163    #[test]
164    fn new() {
165        SignalFd::new(SIGRTMIN()).unwrap();
166    }
167
168    #[test]
169    fn read() {
170        let sigid = SIGRTMIN() + 1;
171        let sigrt_fd = SignalFd::new(sigid).unwrap();
172
173        // SAFETY: Safe because sigid is valid and return value is checked.
174        let ret = unsafe { raise(sigid) };
175        assert_eq!(ret, 0);
176
177        let siginfo = sigrt_fd.read().unwrap().unwrap();
178        assert_eq!(siginfo.ssi_signo, sigid as u32);
179    }
180
181    #[test]
182    fn drop() {
183        let sigid = SIGRTMIN() + 2;
184
185        let sigrt_fd = SignalFd::new(sigid).unwrap();
186        // SAFETY: Safe because sigset and sigid are valid and return value is checked.
187        unsafe {
188            let mut sigset: sigset_t = mem::zeroed();
189            pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
190            assert_eq!(sigismember(&sigset, sigid), 1);
191        }
192
193        mem::drop(sigrt_fd);
194
195        // The signal should no longer be masked.
196        // SAFETY: Safe because sigset and sigid are valid and return value is checked.
197        unsafe {
198            let mut sigset: sigset_t = mem::zeroed();
199            pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
200            assert_eq!(sigismember(&sigset, sigid), 0);
201        }
202    }
203}