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