// 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 libc::{ c_int, pthread_kill, pthread_sigmask, pthread_t, sigaction, sigaddset, sigemptyset, siginfo_t, sigismember, sigpending, sigset_t, sigtimedwait, timespec, EAGAIN, EINTR, EINVAL, SA_RESTART, SIG_BLOCK, SIG_UNBLOCK, }; use std::fmt::{self, Display}; use std::io; use std::mem; use std::os::unix::thread::JoinHandleExt; use std::ptr::{null, null_mut}; use std::result; use std::thread::JoinHandle; use {errno, errno_result}; #[derive(Debug)] pub enum Error { /// Couldn't create a sigset. CreateSigset(errno::Error), /// The wrapped signal has already been blocked. SignalAlreadyBlocked(c_int), /// Failed to check if the requested signal is in the blocked set already. CompareBlockedSignals(errno::Error), /// The signal could not be blocked. BlockSignal(errno::Error), /// The signal mask could not be retrieved. RetrieveSignalMask(i32), /// The signal could not be unblocked. UnblockSignal(errno::Error), /// Failed to wait for given signal. ClearWaitPending(errno::Error), /// Failed to get pending signals. ClearGetPending(errno::Error), /// Failed to check if given signal is in the set of pending signals. ClearCheckPending(errno::Error), } impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Error::*; match self { CreateSigset(e) => write!(f, "couldn't create a sigset: {}", e), SignalAlreadyBlocked(num) => write!(f, "signal {} already blocked", num), CompareBlockedSignals(e) => write!( f, "failed to check whether requested signal is in the blocked set: {}", e, ), BlockSignal(e) => write!(f, "signal could not be blocked: {}", e), RetrieveSignalMask(errno) => write!( f, "failed to retrieve signal mask: {}", io::Error::from_raw_os_error(*errno), ), UnblockSignal(e) => write!(f, "signal could not be unblocked: {}", e), ClearWaitPending(e) => write!(f, "failed to wait for given signal: {}", e), ClearGetPending(e) => write!(f, "failed to get pending signals: {}", e), ClearCheckPending(e) => write!( f, "failed to check whether given signal is in the pending set: {}", e, ), } } } pub type SignalResult = result::Result; #[link(name = "c")] extern "C" { fn __libc_current_sigrtmin() -> c_int; fn __libc_current_sigrtmax() -> c_int; } /// Returns the minimum (inclusive) real-time signal number. #[allow(non_snake_case)] pub fn SIGRTMIN() -> c_int { unsafe { __libc_current_sigrtmin() } } /// Returns the maximum (inclusive) real-time signal number. #[allow(non_snake_case)] pub fn SIGRTMAX() -> c_int { unsafe { __libc_current_sigrtmax() } } fn valid_signal_num(num: c_int) -> bool { num >= SIGRTMIN() && num <= SIGRTMAX() } /// Registers `handler` as the signal handler of signum `num`. /// /// The value of `num` must be within [`SIGRTMIN`, `SIGRTMAX`] range. /// /// This is considered unsafe because the given handler will be called asynchronously, interrupting /// whatever the thread was doing and therefore must only do async-signal-safe operations. pub unsafe fn register_signal_handler( num: c_int, handler: extern "C" fn() -> (), ) -> errno::Result<()> { if !valid_signal_num(num) { return Err(errno::Error::new(EINVAL)); } let mut sigact: sigaction = mem::zeroed(); sigact.sa_flags = SA_RESTART; sigact.sa_sigaction = handler as *const () as usize; let ret = sigaction(num, &sigact, null_mut()); if ret < 0 { return errno_result(); } Ok(()) } /// Creates `sigset` from an array of signal numbers. /// /// This is a helper function used when we want to manipulate signals. pub fn create_sigset(signals: &[c_int]) -> errno::Result { // sigset will actually be initialized by sigemptyset below. let mut sigset: sigset_t = unsafe { mem::zeroed() }; // Safe - return value is checked. let ret = unsafe { sigemptyset(&mut sigset) }; if ret < 0 { return errno_result(); } for signal in signals { // Safe - return value is checked. let ret = unsafe { sigaddset(&mut sigset, *signal) }; if ret < 0 { return errno_result(); } } Ok(sigset) } /// Retrieves the signal mask of the current thread as a vector of c_ints. pub fn get_blocked_signals() -> SignalResult> { let mut mask = Vec::new(); // Safe - return values are checked. unsafe { let mut old_sigset: sigset_t = mem::zeroed(); let ret = pthread_sigmask(SIG_BLOCK, null(), &mut old_sigset as *mut sigset_t); if ret < 0 { return Err(Error::RetrieveSignalMask(ret)); } for num in 0..=SIGRTMAX() { if sigismember(&old_sigset, num) > 0 { mask.push(num); } } } Ok(mask) } /// Masks given signal. /// /// If signal is already blocked the call will fail with Error::SignalAlreadyBlocked /// result. pub fn block_signal(num: c_int) -> SignalResult<()> { let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?; // Safe - return values are checked. unsafe { let mut old_sigset: sigset_t = mem::zeroed(); let ret = pthread_sigmask(SIG_BLOCK, &sigset, &mut old_sigset as *mut sigset_t); if ret < 0 { return Err(Error::BlockSignal(errno::Error::last())); } let ret = sigismember(&old_sigset, num); if ret < 0 { return Err(Error::CompareBlockedSignals(errno::Error::last())); } else if ret > 0 { return Err(Error::SignalAlreadyBlocked(num)); } } Ok(()) } /// Unmasks given signal. pub fn unblock_signal(num: c_int) -> SignalResult<()> { let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?; // Safe - return value is checked. let ret = unsafe { pthread_sigmask(SIG_UNBLOCK, &sigset, null_mut()) }; if ret < 0 { return Err(Error::UnblockSignal(errno::Error::last())); } Ok(()) } /// Clears pending signal. pub fn clear_signal(num: c_int) -> SignalResult<()> { let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?; while { // This is safe as we are rigorously checking return values // of libc calls. unsafe { let mut siginfo: siginfo_t = mem::zeroed(); let ts = timespec { tv_sec: 0, tv_nsec: 0, }; // Attempt to consume one instance of pending signal. If signal // is not pending, the call will fail with EAGAIN or EINTR. let ret = sigtimedwait(&sigset, &mut siginfo, &ts); if ret < 0 { let e = errno::Error::last(); match e.errno() { EAGAIN | EINTR => {} _ => { return Err(Error::ClearWaitPending(errno::Error::last())); } } } // This sigset will be actually filled with `sigpending` call. let mut chkset: sigset_t = mem::zeroed(); // See if more instances of the signal are pending. let ret = sigpending(&mut chkset); if ret < 0 { return Err(Error::ClearGetPending(errno::Error::last())); } let ret = sigismember(&chkset, num); if ret < 0 { return Err(Error::ClearCheckPending(errno::Error::last())); } // This is do-while loop condition. ret != 0 } } {} Ok(()) } /// Trait for threads that can be signalled via `pthread_kill`. /// /// Note that this is only useful for signals between SIGRTMIN and SIGRTMAX because these are /// guaranteed to not be used by the C runtime. /// /// This is marked unsafe because the implementation of this trait must guarantee that the returned /// pthread_t is valid and has a lifetime at least that of the trait object. pub unsafe trait Killable { fn pthread_handle(&self) -> pthread_t; /// Sends the signal `num` to this killable thread. /// /// The value of `num` must be within [`SIGRTMIN`, `SIGRTMAX`] range. fn kill(&self, num: c_int) -> errno::Result<()> { if !valid_signal_num(num) { return Err(errno::Error::new(EINVAL)); } // Safe because we ensure we are using a valid pthread handle, a valid signal number, and // check the return result. let ret = unsafe { pthread_kill(self.pthread_handle(), num) }; if ret < 0 { return errno_result(); } Ok(()) } } // Safe because we fulfill our contract of returning a genuine pthread handle. unsafe impl Killable for JoinHandle { fn pthread_handle(&self) -> pthread_t { self.as_pthread_t() } }