blob: 8f5bc6a922290c989e837f67a155883b03189a4c [file] [log] [blame]
// 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()
}
}