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 5 use std::fs::File; 6 use std::mem; 7 use std::os::raw::c_int; 8 use std::os::unix::io::AsRawFd; 9 use std::os::unix::io::FromRawFd; 10 use std::os::unix::io::RawFd; 11 use std::result; 12 13 use libc::c_void; 14 use libc::read; 15 use libc::signalfd; 16 use libc::signalfd_siginfo; 17 use libc::EAGAIN; 18 use libc::SFD_CLOEXEC; 19 use libc::SFD_NONBLOCK; 20 use log::error; 21 use remain::sorted; 22 use thiserror::Error; 23 24 use super::signal; 25 use super::Error as ErrnoError; 26 use super::RawDescriptor; 27 use crate::descriptor::AsRawDescriptor; 28 29 #[sorted] 30 #[derive(Error, Debug)] 31 pub 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 50 pub 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. 56 pub struct SignalFd { 57 signalfd: File, 58 signal: c_int, 59 } 60 61 impl 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. new(signal: c_int) -> Result<SignalFd>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. read(&self) -> Result<Option<signalfd_siginfo>>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 127 impl AsRawFd for SignalFd { as_raw_fd(&self) -> RawFd128 fn as_raw_fd(&self) -> RawFd { 129 self.signalfd.as_raw_fd() 130 } 131 } 132 133 impl AsRawDescriptor for SignalFd { as_raw_descriptor(&self) -> RawDescriptor134 fn as_raw_descriptor(&self) -> RawDescriptor { 135 self.signalfd.as_raw_descriptor() 136 } 137 } 138 139 impl Drop for SignalFd { drop(&mut self)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)] 151 mod 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] new()164 fn new() { 165 SignalFd::new(SIGRTMIN()).unwrap(); 166 } 167 168 #[test] read()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] drop()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 } 204