| // 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::cmp::Ordering; |
| 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 crate::{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<T> = result::Result<T, Error>; |
| |
| #[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_rt_signal_num(num: c_int) -> bool { |
| num >= SIGRTMIN() && num <= SIGRTMAX() |
| } |
| |
| /// Registers `handler` as the signal handler of signum `num`. |
| /// |
| /// # Safety |
| /// |
| /// 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<()> { |
| 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(()) |
| } |
| |
| /// Registers `handler` as the signal handler for the real-time signal with signum `num`. |
| /// |
| /// The value of `num` must be within [`SIGRTMIN`, `SIGRTMAX`] range. |
| /// |
| /// # Safety |
| /// |
| /// 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_rt_signal_handler( |
| num: c_int, |
| handler: extern "C" fn(), |
| ) -> errno::Result<()> { |
| if !valid_rt_signal_num(num) { |
| return Err(errno::Error::new(EINVAL)); |
| } |
| |
| register_signal_handler(num, handler) |
| } |
| |
| /// 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_t> { |
| // 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<Vec<c_int>> { |
| 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); |
| match ret.cmp(&0) { |
| Ordering::Less => { |
| return Err(Error::CompareBlockedSignals(errno::Error::last())); |
| } |
| Ordering::Greater => { |
| 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_rt_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<T> Killable for JoinHandle<T> { |
| fn pthread_handle(&self) -> pthread_t { |
| self.as_pthread_t() |
| } |
| } |