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