blob: b6689c796c7a14563e708c13440cd31974dd62d3 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![allow(dead_code)]
use crate::lock::RwLock;
use crate::task::{WaitQueue, Waiter};
use crate::types::*;
use std::collections::VecDeque;
use std::sync::Arc;
use zerocopy::{AsBytes, FromBytes};
/// `SignalActions` contains a `sigaction_t` for each valid signal.
#[derive(Debug)]
pub struct SignalActions {
actions: RwLock<[sigaction_t; Signal::NUM_SIGNALS as usize + 1]>,
}
impl SignalActions {
/// Returns a collection of `sigaction_t`s that contains default values for each signal.
pub fn default() -> Arc<SignalActions> {
Arc::new(SignalActions {
actions: RwLock::new([sigaction_t::default(); Signal::NUM_SIGNALS as usize + 1]),
})
}
/// Returns the `sigaction_t` that is currently set for `signal`.
pub fn get(&self, signal: Signal) -> sigaction_t {
// This is safe, since the actions always contain a value for each signal.
self.actions.read()[signal.number() as usize]
}
/// Update the action for `signal`. Returns the previously configured action.
pub fn set(&self, signal: Signal, new_action: sigaction_t) -> sigaction_t {
let mut actions = self.actions.write();
let old_action = actions[signal.number() as usize];
actions[signal.number() as usize] = new_action;
old_action
}
pub fn fork(&self) -> Arc<SignalActions> {
Arc::new(SignalActions { actions: RwLock::new(self.actions.read().clone()) })
}
pub fn reset_for_exec(&self) {
for action in self.actions.write().iter_mut() {
if action.sa_handler != SIG_DFL && action.sa_handler != SIG_IGN {
action.sa_handler = SIG_DFL;
}
}
}
}
/// Per-task signal handling state.
#[derive(Default)]
pub struct SignalState {
/// The queue of signals for a given task.
///
/// There may be more than one instance of a real-time signal in the queue, but for standard
/// signals there is only ever one instance of any given signal.
queue: VecDeque<SignalInfo>,
/// Wait queue for signalfd. Signaled whenever a signal is added to the queue.
pub signalfd_wait: WaitQueue,
// See https://man7.org/linux/man-pages/man2/sigaltstack.2.html
pub alt_stack: Option<sigaltstack_t>,
/// The signal mask of the task.
// See https://man7.org/linux/man-pages/man2/rt_sigprocmask.2.html
pub mask: sigset_t,
/// The waiter that the task is currently sleeping on, if any.
pub waiter: Option<Arc<Waiter>>,
}
impl SignalState {
/// Sets the signal mask of the state, and returns the old signal mask.
pub fn set_signal_mask(&mut self, signal_mask: u64) -> u64 {
let old_mask = self.mask;
self.mask = signal_mask & !(SIGSTOP.mask() | SIGKILL.mask());
old_mask
}
pub fn enqueue(&mut self, siginfo: SignalInfo) {
if siginfo.signal.is_real_time() || !self.has_queued(siginfo.signal) {
self.queue.push_back(siginfo.clone());
self.signalfd_wait.notify_all();
}
}
/// Finds the next queued signal where the given function returns true, removes it from the
/// queue, and returns it.
pub fn take_next_where<F>(&mut self, predicate: F) -> Option<SignalInfo>
where
F: Fn(&SignalInfo) -> bool,
{
// Find the first non-blocked signal
let index = self.queue.iter().position(predicate)?;
self.queue.remove(index)
}
/// Returns whether any signals are pending (queued and not blocked).
pub fn is_any_pending(&self) -> bool {
self.is_any_allowed_by_mask(self.mask)
}
/// Returns whether any signals are queued and not blocked by the given mask.
pub fn is_any_allowed_by_mask(&self, mask: sigset_t) -> bool {
self.queue.iter().any(|sig| !sig.signal.is_in_set(mask))
}
/// Iterates over queued signals with the given number.
fn iter_queued_by_number(&self, signal: Signal) -> impl Iterator<Item = &SignalInfo> {
self.queue.iter().filter(move |info| info.signal == signal)
}
/// Tests whether a signal with the given number is in the queue.
pub fn has_queued(&self, signal: Signal) -> bool {
self.iter_queued_by_number(signal).next().is_some()
}
#[cfg(test)]
pub fn queued_count(&self, signal: Signal) -> usize {
self.iter_queued_by_number(signal).count()
}
}
#[derive(Clone, Debug, Default, PartialEq, AsBytes, FromBytes)]
#[repr(C)]
pub struct SignalInfoHeader {
pub signo: u32,
pub errno: i32,
pub code: i32,
pub _pad: i32,
}
pub const SI_HEADER_SIZE: usize = std::mem::size_of::<SignalInfoHeader>();
#[derive(Clone, Debug, PartialEq)]
pub struct SignalInfo {
pub signal: Signal,
pub errno: i32,
pub code: i32,
pub detail: SignalDetail,
pub force: bool,
}
impl SignalInfo {
pub fn default(signal: Signal) -> Self {
Self::new(signal, SI_KERNEL, SignalDetail::default())
}
pub fn new(signal: Signal, code: u32, detail: SignalDetail) -> Self {
Self { signal, errno: 0, code: code as i32, detail, force: false }
}
// TODO(tbodt): Add a bound requiring siginfo_t to be FromBytes. This will help ensure the
// Linux side won't get an invalid siginfo_t.
pub fn as_siginfo_bytes(&self) -> [u8; std::mem::size_of::<siginfo_t>()] {
macro_rules! make_siginfo {
($self:ident $(, $sifield:ident, $value:expr)?) => {
struct_with_union_into_bytes!(siginfo_t {
__bindgen_anon_1.__bindgen_anon_1.si_signo: $self.signal.number() as i32,
__bindgen_anon_1.__bindgen_anon_1.si_errno: $self.errno,
__bindgen_anon_1.__bindgen_anon_1.si_code: $self.code,
$(
__bindgen_anon_1.__bindgen_anon_1._sifields.$sifield: $value,
)?
})
};
}
match self.detail {
SignalDetail::None => make_siginfo!(self),
SignalDetail::SigChld { pid, uid, status } => make_siginfo!(
self,
_sigchld,
__sifields__bindgen_ty_4 {
_pid: pid,
_uid: uid,
_status: status,
..Default::default()
}
),
SignalDetail::Raw { data } => {
let header = SignalInfoHeader {
signo: self.signal.number(),
errno: self.errno,
code: self.code,
_pad: 0,
};
let mut array: [u8; SI_MAX_SIZE as usize] = [0; SI_MAX_SIZE as usize];
header.write_to(&mut array[..SI_HEADER_SIZE]);
array[SI_HEADER_SIZE..SI_MAX_SIZE as usize].copy_from_slice(&data);
array
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum SignalDetail {
None,
SigChld { pid: pid_t, uid: uid_t, status: i32 },
Raw { data: [u8; SI_MAX_SIZE as usize - SI_HEADER_SIZE] },
}
impl Default for SignalDetail {
fn default() -> Self {
Self::None
}
}
#[cfg(test)]
mod test {
use super::*;
use std::convert::TryFrom;
#[::fuchsia::test]
fn test_signal() {
assert!(Signal::try_from(UncheckedSignal::from(0)).is_err());
assert!(Signal::try_from(UncheckedSignal::from(1)).is_ok());
assert!(Signal::try_from(UncheckedSignal::from(Signal::NUM_SIGNALS)).is_ok());
assert!(Signal::try_from(UncheckedSignal::from(Signal::NUM_SIGNALS + 1)).is_err());
assert!(!SIGCHLD.is_real_time());
assert!(Signal::try_from(UncheckedSignal::from(uapi::SIGRTMIN + 12))
.unwrap()
.is_real_time());
assert_eq!(format!("{}", SIGPWR), "signal 30: SIGPWR");
assert_eq!(
format!("{}", Signal::try_from(UncheckedSignal::from(uapi::SIGRTMIN + 10)).unwrap()),
"signal 42: SIGRTMIN+10"
);
}
#[::fuchsia::test]
fn test_siginfo_bytes() {
let mut sigchld_bytes =
vec![17, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, 200, 1, 0, 0, 2];
sigchld_bytes.resize(std::mem::size_of::<siginfo_t>(), 0);
assert_eq!(
&SignalInfo::new(
SIGCHLD,
CLD_EXITED,
SignalDetail::SigChld { pid: 123, uid: 456, status: 2 }
)
.as_siginfo_bytes(),
sigchld_bytes.as_slice()
);
}
}