| // Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use std::fmt::{self, Display}; |
| use std::fs::File; |
| use std::mem; |
| use std::os::raw::c_int; |
| use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; |
| use std::result; |
| |
| use libc::{c_void, read, signalfd, signalfd_siginfo}; |
| use libc::{EAGAIN, SFD_CLOEXEC, SFD_NONBLOCK}; |
| |
| use crate::{errno, signal, AsRawDescriptor, RawDescriptor}; |
| |
| #[derive(Debug)] |
| pub enum Error { |
| /// Failed to construct sigset when creating signalfd. |
| CreateSigset(errno::Error), |
| /// Failed to create a new signalfd. |
| CreateSignalFd(errno::Error), |
| /// Failed to block the signal when creating signalfd. |
| CreateBlockSignal(signal::Error), |
| /// Unable to read from signalfd. |
| SignalFdRead(errno::Error), |
| /// Signalfd could be read, but didn't return a full siginfo struct. |
| /// This wraps the number of bytes that were actually read. |
| SignalFdPartialRead(usize), |
| } |
| |
| impl Display for Error { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| use self::Error::*; |
| |
| match self { |
| CreateSigset(e) => write!( |
| f, |
| "failed to construct sigset when creating signalfd: {}", |
| e, |
| ), |
| CreateSignalFd(e) => write!(f, "failed to create a new signalfd: {}", e), |
| CreateBlockSignal(e) => write!( |
| f, |
| "failed to block the signal when creating signalfd: {}", |
| e, |
| ), |
| SignalFdRead(e) => write!(f, "unable to read from signalfd: {}", e), |
| SignalFdPartialRead(read) => write!( |
| f, |
| "signalfd failed to return a full siginfo struct, read only {} bytes", |
| read, |
| ), |
| } |
| } |
| } |
| |
| pub type Result<T> = result::Result<T, Error>; |
| |
| /// A safe wrapper around a Linux signalfd (man 2 signalfd). |
| /// |
| /// A signalfd can be used for non-synchronous signals (such as SIGCHLD) so that |
| /// signals can be processed without the use of a signal handler. |
| pub struct SignalFd { |
| signalfd: File, |
| signal: c_int, |
| } |
| |
| impl SignalFd { |
| /// Creates a new SignalFd for the given signal, blocking the normal handler |
| /// for the signal as well. Since we mask out the normal handler, this is |
| /// a risky operation - signal masking will persist across fork and even |
| /// **exec** so the user of SignalFd should think long and hard about |
| /// when to mask signals. |
| pub fn new(signal: c_int) -> Result<SignalFd> { |
| let sigset = signal::create_sigset(&[signal]).map_err(Error::CreateSigset)?; |
| |
| // This is safe as we check the return value and know that fd is valid. |
| let fd = unsafe { signalfd(-1, &sigset, SFD_CLOEXEC | SFD_NONBLOCK) }; |
| if fd < 0 { |
| return Err(Error::CreateSignalFd(errno::Error::last())); |
| } |
| |
| // Mask out the normal handler for the signal. |
| signal::block_signal(signal).map_err(Error::CreateBlockSignal)?; |
| |
| // This is safe because we checked fd for success and know the |
| // kernel gave us an fd that we own. |
| unsafe { |
| Ok(SignalFd { |
| signalfd: File::from_raw_fd(fd), |
| signal, |
| }) |
| } |
| } |
| |
| /// Read a siginfo struct from the signalfd, if available. |
| pub fn read(&self) -> Result<Option<signalfd_siginfo>> { |
| // signalfd_siginfo doesn't have a default, so just zero it. |
| let mut siginfo: signalfd_siginfo = unsafe { mem::zeroed() }; |
| let siginfo_size = mem::size_of::<signalfd_siginfo>(); |
| |
| // This read is safe since we've got the space allocated for a |
| // single signalfd_siginfo, and that's exactly how much we're |
| // reading. Handling of EINTR is not required since SFD_NONBLOCK |
| // was specified. signalfds will always read in increments of |
| // sizeof(signalfd_siginfo); see man 2 signalfd. |
| let ret = unsafe { |
| read( |
| self.signalfd.as_raw_fd(), |
| &mut siginfo as *mut signalfd_siginfo as *mut c_void, |
| siginfo_size, |
| ) |
| }; |
| |
| if ret < 0 { |
| let err = errno::Error::last(); |
| if err.errno() == EAGAIN { |
| Ok(None) |
| } else { |
| Err(Error::SignalFdRead(err)) |
| } |
| } else if ret == (siginfo_size as isize) { |
| Ok(Some(siginfo)) |
| } else { |
| Err(Error::SignalFdPartialRead(ret as usize)) |
| } |
| } |
| } |
| |
| impl AsRawFd for SignalFd { |
| fn as_raw_fd(&self) -> RawFd { |
| self.signalfd.as_raw_fd() |
| } |
| } |
| |
| impl AsRawDescriptor for SignalFd { |
| fn as_raw_descriptor(&self) -> RawDescriptor { |
| self.signalfd.as_raw_descriptor() |
| } |
| } |
| |
| impl Drop for SignalFd { |
| fn drop(&mut self) { |
| // This is thread-safe and safe in the sense that we're doing what |
| // was promised - unmasking the signal when we go out of scope. |
| let res = signal::unblock_signal(self.signal); |
| if let Err(e) = res { |
| error!("signalfd failed to unblock signal {}: {}", self.signal, e); |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| use crate::signal::SIGRTMIN; |
| use libc::{pthread_sigmask, raise, sigismember, sigset_t}; |
| use std::mem; |
| use std::ptr::null; |
| |
| #[test] |
| fn new() { |
| SignalFd::new(SIGRTMIN()).unwrap(); |
| } |
| |
| #[test] |
| fn read() { |
| let sigid = SIGRTMIN() + 1; |
| let sigrt_fd = SignalFd::new(sigid).unwrap(); |
| |
| let ret = unsafe { raise(sigid) }; |
| assert_eq!(ret, 0); |
| |
| let siginfo = sigrt_fd.read().unwrap().unwrap(); |
| assert_eq!(siginfo.ssi_signo, sigid as u32); |
| } |
| |
| #[test] |
| fn drop() { |
| let sigid = SIGRTMIN() + 2; |
| |
| let sigrt_fd = SignalFd::new(sigid).unwrap(); |
| unsafe { |
| let mut sigset: sigset_t = mem::zeroed(); |
| pthread_sigmask(0, null(), &mut sigset as *mut sigset_t); |
| assert_eq!(sigismember(&sigset, sigid), 1); |
| } |
| |
| mem::drop(sigrt_fd); |
| |
| // The signal should no longer be masked. |
| unsafe { |
| let mut sigset: sigset_t = mem::zeroed(); |
| pthread_sigmask(0, null(), &mut sigset as *mut sigset_t); |
| assert_eq!(sigismember(&sigset, sigid), 0); |
| } |
| } |
| } |