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