mod cond;
mod state;
pub use self::cond::Condition;
pub use self::state::{Action, Origin, SetActionError, TrapState};
use self::state::{EnterSubshellOption, GrandState};
use crate::Env;
use crate::signal;
use crate::source::Location;
#[cfg(doc)]
use crate::system::Concurrent;
use crate::system::{Disposition, Errno, Signals};
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::pin::Pin;
use std::rc::Rc;
pub trait SignalSystem: Signals {
fn get_disposition(&self, signal: signal::Number) -> Result<Disposition, Errno>;
fn set_disposition(
&self,
signal: signal::Number,
disposition: Disposition,
) -> impl Future<Output = Result<Disposition, Errno>> + use<Self>;
}
impl<S: SignalSystem> SignalSystem for Rc<S> {
#[inline]
fn get_disposition(&self, signal: signal::Number) -> Result<Disposition, Errno> {
(self as &S).get_disposition(signal)
}
#[inline]
fn set_disposition(
&self,
signal: signal::Number,
disposition: Disposition,
) -> impl Future<Output = Result<Disposition, Errno>> + use<S> {
(self as &S).set_disposition(signal, disposition)
}
}
#[must_use]
pub struct Iter<'a> {
inner: std::collections::btree_map::Iter<'a, Condition, GrandState>,
}
impl<'a> Iterator for Iter<'a> {
type Item = (&'a Condition, &'a TrapState, Option<&'a TrapState>);
fn next(&mut self) -> Option<(&'a Condition, &'a TrapState, Option<&'a TrapState>)> {
self.inner
.next()
.map(|(cond, state)| (cond, state.current_state(), state.parent_state()))
}
}
#[derive(Clone, Debug, Default)]
pub struct TrapSet {
traps: BTreeMap<Condition, GrandState>,
}
impl TrapSet {
pub fn get_state<C: Into<Condition>>(
&self,
cond: C,
) -> (Option<&TrapState>, Option<&TrapState>) {
self.get_state_impl(cond.into())
}
fn get_state_impl(&self, cond: Condition) -> (Option<&TrapState>, Option<&TrapState>) {
match self.traps.get(&cond) {
None => (None, None),
Some(state) => (Some(state.current_state()), state.parent_state()),
}
}
pub fn peek_state<S: SignalSystem, C: Into<Condition>>(
&mut self,
system: &S,
cond: C,
) -> Result<&TrapState, Errno> {
self.peek_state_impl(system, cond.into())
}
fn peek_state_impl<S: SignalSystem>(
&mut self,
system: &S,
cond: Condition,
) -> Result<&TrapState, Errno> {
let entry = self.traps.entry(cond);
let state = GrandState::insert_from_system_if_vacant(system, entry)?;
Ok(state.parent_state().unwrap_or(state.current_state()))
}
pub async fn set_action<S: SignalSystem, C: Into<Condition>>(
&mut self,
system: &S,
cond: C,
action: Action,
origin: Location,
override_ignore: bool,
) -> Result<(), SetActionError> {
self.set_action_impl(system, cond.into(), action, origin, override_ignore)
.await
}
async fn set_action_impl<S: SignalSystem>(
&mut self,
system: &S,
cond: Condition,
action: Action,
origin: Location,
override_ignore: bool,
) -> Result<(), SetActionError> {
if let Condition::Signal(signal) = cond {
if signal == S::SIGKILL {
return Err(SetActionError::SIGKILL);
}
if signal == S::SIGSTOP {
return Err(SetActionError::SIGSTOP);
}
}
self.clear_parent_states();
let entry = self.traps.entry(cond);
GrandState::set_action(system, entry, action, origin, override_ignore).await
}
fn clear_parent_states(&mut self) {
for state in self.traps.values_mut() {
state.clear_parent_state();
}
}
#[inline]
pub fn iter(&self) -> Iter<'_> {
let inner = self.traps.iter();
Iter { inner }
}
pub async fn enter_subshell<S: SignalSystem>(
&mut self,
system: &S,
ignore_sigint_sigquit: bool,
keep_internal_dispositions_for_stoppers: bool,
) {
self.clear_parent_states();
for (&cond, state) in &mut self.traps {
let option = match cond {
Condition::Exit => EnterSubshellOption::ClearInternalDisposition,
Condition::Signal(signal) =>
{
#[allow(clippy::if_same_then_else)]
if signal == S::SIGCHLD {
EnterSubshellOption::KeepInternalDisposition
} else if ignore_sigint_sigquit && (signal == S::SIGINT || signal == S::SIGQUIT)
{
EnterSubshellOption::Ignore
} else if keep_internal_dispositions_for_stoppers
&& (signal == S::SIGTSTP || signal == S::SIGTTIN || signal == S::SIGTTOU)
&& state.internal_disposition() != Disposition::Default
{
EnterSubshellOption::Ignore
} else {
EnterSubshellOption::ClearInternalDisposition
}
}
};
state.enter_subshell(system, cond, option).await.ok();
}
if ignore_sigint_sigquit {
for signal in [S::SIGINT, S::SIGQUIT] {
match self.traps.entry(Condition::Signal(signal)) {
Entry::Vacant(vacant) => {
GrandState::ignore(system, vacant).await.unwrap_or_default()
}
Entry::Occupied(_) => {}
}
}
}
}
pub fn catch_signal(&mut self, signal: signal::Number) {
if let Some(state) = self.traps.get_mut(&Condition::Signal(signal)) {
state.mark_as_caught();
}
}
pub fn take_signal_if_caught(&mut self, signal: signal::Number) -> Option<&TrapState> {
self.traps
.get_mut(&Condition::Signal(signal))
.and_then(|state| state.handle_if_caught())
}
pub fn take_caught_signal(&mut self) -> Option<(signal::Number, &TrapState)> {
self.traps.iter_mut().find_map(|(&cond, state)| match cond {
Condition::Signal(signal) => state.handle_if_caught().map(|trap| (signal, trap)),
_ => None,
})
}
async fn set_internal_disposition<S: SignalSystem>(
&mut self,
signal: signal::Number,
disposition: Disposition,
system: &S,
) -> Result<(), Errno> {
let entry = self.traps.entry(Condition::Signal(signal));
GrandState::set_internal_disposition(system, entry, disposition).await
}
pub async fn enable_internal_disposition_for_sigchld<S: SignalSystem>(
&mut self,
system: &S,
) -> Result<(), Errno> {
self.set_internal_disposition(S::SIGCHLD, Disposition::Catch, system)
.await
}
pub async fn enable_internal_dispositions_for_terminators<S: SignalSystem>(
&mut self,
system: &S,
) -> Result<(), Errno> {
self.set_internal_disposition(S::SIGINT, Disposition::Catch, system)
.await?;
self.set_internal_disposition(S::SIGTERM, Disposition::Ignore, system)
.await?;
self.set_internal_disposition(S::SIGQUIT, Disposition::Ignore, system)
.await
}
pub async fn enable_internal_dispositions_for_stoppers<S: SignalSystem>(
&mut self,
system: &S,
) -> Result<(), Errno> {
self.set_internal_disposition(S::SIGTSTP, Disposition::Ignore, system)
.await?;
self.set_internal_disposition(S::SIGTTIN, Disposition::Ignore, system)
.await?;
self.set_internal_disposition(S::SIGTTOU, Disposition::Ignore, system)
.await
}
pub async fn disable_internal_dispositions_for_terminators<S: SignalSystem>(
&mut self,
system: &S,
) -> Result<(), Errno> {
self.set_internal_disposition(S::SIGINT, Disposition::Default, system)
.await?;
self.set_internal_disposition(S::SIGTERM, Disposition::Default, system)
.await?;
self.set_internal_disposition(S::SIGQUIT, Disposition::Default, system)
.await
}
pub async fn disable_internal_dispositions_for_stoppers<S: SignalSystem>(
&mut self,
system: &S,
) -> Result<(), Errno> {
self.set_internal_disposition(S::SIGTSTP, Disposition::Default, system)
.await?;
self.set_internal_disposition(S::SIGTTIN, Disposition::Default, system)
.await?;
self.set_internal_disposition(S::SIGTTOU, Disposition::Default, system)
.await
}
pub async fn disable_internal_dispositions<S: SignalSystem>(
&mut self,
system: &S,
) -> Result<(), Errno> {
self.set_internal_disposition(S::SIGCHLD, Disposition::Default, system)
.await?;
self.disable_internal_dispositions_for_terminators(system)
.await?;
self.disable_internal_dispositions_for_stoppers(system)
.await
}
}
impl<'a> IntoIterator for &'a TrapSet {
type Item = (&'a Condition, &'a TrapState, Option<&'a TrapState>);
type IntoIter = Iter<'a>;
#[inline(always)]
fn into_iter(self) -> Iter<'a> {
self.iter()
}
}
type PinFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
pub struct RunSignalTrapIfCaught<S>(
pub for<'a> fn(
env: &'a mut Env<S>,
signal: signal::Number,
) -> PinFuture<'a, Option<crate::semantics::Result>>,
);
impl<S> Clone for RunSignalTrapIfCaught<S> {
fn clone(&self) -> Self {
*self
}
}
impl<S> Copy for RunSignalTrapIfCaught<S> {}
impl<S> std::fmt::Debug for RunSignalTrapIfCaught<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("RunSignalTrapIfCaught")
.field(&self.0)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::job::ProcessState;
use crate::system::SendSignal as _;
use crate::system::r#virtual::{
SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGIOT,
SIGKILL, SIGPIPE, SIGPROF, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTERM, SIGTRAP, SIGTSTP,
SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH, SIGXCPU, SIGXFSZ,
};
use crate::test_helper::in_virtual_system;
use futures_util::FutureExt as _;
use std::cell::RefCell;
use std::collections::HashMap;
use std::future::ready;
use std::ops::RangeInclusive;
#[derive(Default)]
pub struct DummySystem(pub RefCell<HashMap<signal::Number, Disposition>>);
impl Signals for DummySystem {
const SIGABRT: signal::Number = SIGABRT;
const SIGALRM: signal::Number = SIGALRM;
const SIGBUS: signal::Number = SIGBUS;
const SIGCHLD: signal::Number = SIGCHLD;
const SIGCLD: Option<signal::Number> = None;
const SIGCONT: signal::Number = SIGCONT;
const SIGEMT: Option<signal::Number> = None;
const SIGFPE: signal::Number = SIGFPE;
const SIGHUP: signal::Number = SIGHUP;
const SIGILL: signal::Number = SIGILL;
const SIGINFO: Option<signal::Number> = None;
const SIGINT: signal::Number = SIGINT;
const SIGIO: Option<signal::Number> = None;
const SIGIOT: signal::Number = SIGIOT;
const SIGKILL: signal::Number = SIGKILL;
const SIGLOST: Option<signal::Number> = None;
const SIGPIPE: signal::Number = SIGPIPE;
const SIGPOLL: Option<signal::Number> = None;
const SIGPROF: signal::Number = SIGPROF;
const SIGPWR: Option<signal::Number> = None;
const SIGQUIT: signal::Number = SIGQUIT;
const SIGSEGV: signal::Number = SIGSEGV;
const SIGSTKFLT: Option<signal::Number> = None;
const SIGSTOP: signal::Number = SIGSTOP;
const SIGSYS: signal::Number = SIGSYS;
const SIGTERM: signal::Number = SIGTERM;
const SIGTHR: Option<signal::Number> = None;
const SIGTRAP: signal::Number = SIGTRAP;
const SIGTSTP: signal::Number = SIGTSTP;
const SIGTTIN: signal::Number = SIGTTIN;
const SIGTTOU: signal::Number = SIGTTOU;
const SIGURG: signal::Number = SIGURG;
const SIGUSR1: signal::Number = SIGUSR1;
const SIGUSR2: signal::Number = SIGUSR2;
const SIGVTALRM: signal::Number = SIGVTALRM;
const SIGWINCH: signal::Number = SIGWINCH;
const SIGXCPU: signal::Number = SIGXCPU;
const SIGXFSZ: signal::Number = SIGXFSZ;
fn sigrt_range(&self) -> Option<RangeInclusive<signal::Number>> {
None
}
}
impl SignalSystem for DummySystem {
fn get_disposition(&self, signal: signal::Number) -> Result<Disposition, Errno> {
Ok(self.0.borrow().get(&signal).copied().unwrap_or_default())
}
fn set_disposition(
&self,
signal: signal::Number,
disposition: Disposition,
) -> impl Future<Output = Result<Disposition, Errno>> + use<> {
ready(Ok(self
.0
.borrow_mut()
.insert(signal, disposition)
.unwrap_or_default()))
}
}
#[test]
fn default_trap() {
let trap_set = TrapSet::default();
assert_eq!(trap_set.get_state(SIGCHLD), (None, None));
}
#[test]
fn setting_trap_for_two_signals() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let origin_1 = Location::dummy("foo");
let result = trap_set
.set_action(&system, SIGUSR1, Action::Ignore, origin_1.clone(), false)
.now_or_never()
.unwrap();
assert_eq!(result, Ok(()));
let command = Action::Command("echo".into());
let origin_2 = Location::dummy("bar");
let result = trap_set
.set_action(&system, SIGUSR2, command.clone(), origin_2.clone(), false)
.now_or_never()
.unwrap();
assert_eq!(result, Ok(()));
assert_eq!(
trap_set.get_state(SIGUSR1),
(
Some(&TrapState {
action: Action::Ignore,
origin: Origin::User(origin_1),
pending: false
}),
None
)
);
assert_eq!(
trap_set.get_state(SIGUSR2),
(
Some(&TrapState {
action: command,
origin: Origin::User(origin_2),
pending: false
}),
None
)
);
assert_eq!(system.0.borrow()[&SIGUSR1], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGUSR2], Disposition::Catch);
}
#[test]
fn setting_trap_for_sigkill() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let origin = Location::dummy("origin");
let result = trap_set
.set_action(&system, SIGKILL, Action::Ignore, origin, false)
.now_or_never()
.unwrap();
assert_eq!(result, Err(SetActionError::SIGKILL));
assert_eq!(trap_set.get_state(SIGKILL), (None, None));
assert_eq!(system.0.borrow().get(&SIGKILL), None);
}
#[test]
fn setting_trap_for_sigstop() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let origin = Location::dummy("origin");
let result = trap_set
.set_action(&system, SIGSTOP, Action::Ignore, origin, false)
.now_or_never()
.unwrap();
assert_eq!(result, Err(SetActionError::SIGSTOP));
assert_eq!(trap_set.get_state(SIGSTOP), (None, None));
assert_eq!(system.0.borrow().get(&SIGSTOP), None);
}
#[test]
fn peeking_state_with_default_inherited_disposition() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let result = trap_set.peek_state(&system, SIGCHLD);
assert_eq!(
result,
Ok(&TrapState {
action: Action::Default,
origin: Origin::Inherited,
pending: false
})
);
}
#[test]
fn peeking_state_with_inherited_disposition_of_ignore() {
let system = DummySystem::default();
system.0.borrow_mut().insert(SIGCHLD, Disposition::Ignore);
let mut trap_set = TrapSet::default();
let result = trap_set.peek_state(&system, SIGCHLD);
assert_eq!(
result,
Ok(&TrapState {
action: Action::Ignore,
origin: Origin::Inherited,
pending: false
})
);
}
#[test]
fn peeking_state_with_parent_state() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let origin = Location::dummy("foo");
let command = Action::Command("echo".into());
trap_set
.set_action(&system, SIGUSR1, command.clone(), origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
let result = trap_set.peek_state(&system, SIGUSR1);
assert_eq!(
result,
Ok(&TrapState {
action: command,
origin: Origin::User(origin),
pending: false
})
);
}
#[test]
fn basic_iteration() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let origin_1 = Location::dummy("foo");
trap_set
.set_action(&system, SIGUSR1, Action::Ignore, origin_1.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
let command = Action::Command("echo".into());
let origin_2 = Location::dummy("bar");
trap_set
.set_action(&system, SIGUSR2, command.clone(), origin_2.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
let mut i = trap_set.iter();
let first = i.next().unwrap();
assert_eq!(first.0, &Condition::Signal(SIGUSR1));
assert_eq!(first.1.action, Action::Ignore);
assert_eq!(first.1.origin, Origin::User(origin_1));
assert_eq!(first.2, None);
let second = i.next().unwrap();
assert_eq!(second.0, &Condition::Signal(SIGUSR2));
assert_eq!(second.1.action, command);
assert_eq!(second.1.origin, Origin::User(origin_2));
assert_eq!(first.2, None);
assert_eq!(i.next(), None);
}
#[test]
fn iteration_after_entering_subshell() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let origin_1 = Location::dummy("foo");
trap_set
.set_action(&system, SIGUSR1, Action::Ignore, origin_1.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
let command = Action::Command("echo".into());
let origin_2 = Location::dummy("bar");
trap_set
.set_action(&system, SIGUSR2, command.clone(), origin_2.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
let mut i = trap_set.iter();
let first = i.next().unwrap();
assert_eq!(first.0, &Condition::Signal(SIGUSR1));
assert_eq!(first.1.action, Action::Ignore);
assert_eq!(first.1.origin, Origin::User(origin_1));
assert_eq!(first.2, None);
let second = i.next().unwrap();
assert_eq!(second.0, &Condition::Signal(SIGUSR2));
assert_eq!(second.1.action, Action::Default);
assert_eq!(second.1.origin, Origin::Subshell);
assert_eq!(second.2.unwrap().action, command);
assert_eq!(second.2.unwrap().origin, Origin::User(origin_2));
assert_eq!(i.next(), None);
}
#[test]
fn iteration_after_setting_trap_in_subshell() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let origin_1 = Location::dummy("foo");
let command = Action::Command("echo".into());
trap_set
.set_action(&system, SIGUSR1, command, origin_1, false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
let origin_2 = Location::dummy("bar");
let command = Action::Command("ls".into());
trap_set
.set_action(&system, SIGUSR2, command.clone(), origin_2.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
let mut i = trap_set.iter();
let first = i.next().unwrap();
assert_eq!(first.0, &Condition::Signal(SIGUSR1));
assert_eq!(first.1.action, Action::Default);
assert_eq!(first.1.origin, Origin::Subshell);
assert_eq!(first.2, None);
let second = i.next().unwrap();
assert_eq!(second.0, &Condition::Signal(SIGUSR2));
assert_eq!(second.1.action, command);
assert_eq!(second.1.origin, Origin::User(origin_2));
assert_eq!(second.2, None);
assert_eq!(i.next(), None);
}
#[test]
fn entering_subshell_resets_command_traps() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let action = Action::Command("".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGCHLD, action.clone(), origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
assert_eq!(
trap_set.get_state(SIGCHLD),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
Some(&TrapState {
action,
origin: Origin::User(origin),
pending: false
})
)
);
assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Default);
}
#[test]
fn entering_subshell_keeps_ignore_traps() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGCHLD, Action::Ignore, origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
assert_eq!(
trap_set.get_state(SIGCHLD),
(
Some(&TrapState {
action: Action::Ignore,
origin: Origin::User(origin),
pending: false
}),
None
)
);
assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Ignore);
}
#[test]
fn entering_subshell_with_internal_disposition_for_sigchld() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let action = Action::Command("".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGCHLD, action.clone(), origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
assert_eq!(
trap_set.get_state(SIGCHLD),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
Some(&TrapState {
action,
origin: Origin::User(origin),
pending: false
})
)
);
assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Catch);
}
#[test]
fn entering_subshell_with_internal_disposition_for_sigint() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let action = Action::Command("".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGINT, action.clone(), origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
assert_eq!(
trap_set.get_state(SIGINT),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
Some(&TrapState {
action,
origin: Origin::User(origin),
pending: false
})
)
);
assert_eq!(system.0.borrow()[&SIGINT], Disposition::Default);
}
#[test]
fn entering_subshell_with_internal_disposition_for_sigterm() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let action = Action::Command("".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGTERM, action.clone(), origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
assert_eq!(
trap_set.get_state(SIGTERM),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
Some(&TrapState {
action,
origin: Origin::User(origin),
pending: false
})
)
);
assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Default);
}
#[test]
fn entering_subshell_with_internal_disposition_for_sigquit() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let action = Action::Command("".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGQUIT, action.clone(), origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
assert_eq!(
trap_set.get_state(SIGQUIT),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
Some(&TrapState {
action,
origin: Origin::User(origin),
pending: false
})
)
);
assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Default);
}
#[test]
fn entering_subshell_with_internal_disposition_for_sigtstp() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let action = Action::Command("".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGTSTP, action.clone(), origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
assert_eq!(
trap_set.get_state(SIGTSTP),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
Some(&TrapState {
action,
origin: Origin::User(origin),
pending: false
})
)
);
assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Default);
}
#[test]
fn entering_subshell_with_internal_disposition_for_sigttin() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let action = Action::Command("".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGTTIN, action.clone(), origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
assert_eq!(
trap_set.get_state(SIGTTIN),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
Some(&TrapState {
action,
origin: Origin::User(origin),
pending: false
})
)
);
assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Default);
}
#[test]
fn entering_subshell_with_internal_disposition_for_sigttou() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let action = Action::Command("".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGTTOU, action.clone(), origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
assert_eq!(
trap_set.get_state(SIGTTOU),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
Some(&TrapState {
action,
origin: Origin::User(origin),
pending: false
})
)
);
assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Default);
}
#[test]
fn setting_trap_after_entering_subshell_clears_parent_states() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let origin_1 = Location::dummy("foo");
let command = Action::Command("echo 1".into());
trap_set
.set_action(&system, SIGUSR1, command, origin_1, false)
.now_or_never()
.unwrap()
.unwrap();
let origin_2 = Location::dummy("bar");
let command = Action::Command("echo 2".into());
trap_set
.set_action(&system, SIGUSR2, command, origin_2, false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
let command = Action::Command("echo 9".into());
let origin_3 = Location::dummy("qux");
trap_set
.set_action(&system, SIGUSR1, command.clone(), origin_3.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(
trap_set.get_state(SIGUSR1),
(
Some(&TrapState {
action: command,
origin: Origin::User(origin_3),
pending: false
}),
None
)
);
assert_eq!(
trap_set.get_state(SIGUSR2),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
None
)
);
assert_eq!(system.0.borrow()[&SIGUSR1], Disposition::Catch);
assert_eq!(system.0.borrow()[&SIGUSR2], Disposition::Default);
}
#[test]
fn entering_nested_subshell_clears_parent_states() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let origin_1 = Location::dummy("foo");
let command = Action::Command("echo 1".into());
trap_set
.set_action(&system, SIGUSR1, command, origin_1, false)
.now_or_never()
.unwrap()
.unwrap();
let origin_2 = Location::dummy("bar");
let command = Action::Command("echo 2".into());
trap_set
.set_action(&system, SIGUSR2, command, origin_2, false)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
trap_set
.enter_subshell(&system, false, false)
.now_or_never()
.unwrap();
assert_eq!(
trap_set.get_state(SIGUSR1),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
None
)
);
assert_eq!(
trap_set.get_state(SIGUSR2),
(
Some(&TrapState {
action: Action::Default,
origin: Origin::Subshell,
pending: false
}),
None
)
);
assert_eq!(system.0.borrow()[&SIGUSR1], Disposition::Default);
assert_eq!(system.0.borrow()[&SIGUSR2], Disposition::Default);
}
#[test]
fn ignoring_sigint_on_entering_subshell_with_action_set() {
in_virtual_system(|mut env, state| async move {
env.traps
.set_action(
&env.system,
SIGINT,
Action::Command("".into()),
Location::dummy(""),
false,
)
.await
.unwrap();
env.system.kill(env.main_pid, Some(SIGINT)).await.unwrap();
env.traps.enter_subshell(&env.system, true, false).await;
let state = state.borrow();
let process = &state.processes[&env.main_pid];
assert_eq!(process.disposition(SIGINT), Disposition::Ignore);
assert_eq!(process.state(), ProcessState::Running);
})
}
#[test]
fn ignoring_sigquit_on_entering_subshell_with_action_set() {
in_virtual_system(|mut env, state| async move {
env.traps
.set_action(
&env.system,
SIGQUIT,
Action::Command("".into()),
Location::dummy(""),
false,
)
.await
.unwrap();
env.system.kill(env.main_pid, Some(SIGQUIT)).await.unwrap();
env.traps.enter_subshell(&env.system, true, false).await;
let state = state.borrow();
let process = &state.processes[&env.main_pid];
assert_eq!(process.disposition(SIGQUIT), Disposition::Ignore);
assert_eq!(process.state(), ProcessState::Running);
})
}
#[test]
fn ignoring_sigint_and_sigquit_on_entering_subshell_without_action_set() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
trap_set
.enter_subshell(&system, true, false)
.now_or_never()
.unwrap();
assert_eq!(system.0.borrow()[&SIGINT], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
}
#[test]
fn keeping_stopper_internal_dispositions_ignored() {
in_virtual_system(|mut env, state| async move {
for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
env.traps
.set_action(
&env.system,
signal,
Action::Command("".into()),
Location::dummy(""),
false,
)
.await
.unwrap();
}
env.traps
.enable_internal_dispositions_for_stoppers(&env.system)
.await
.unwrap();
for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
env.system.kill(env.main_pid, Some(signal)).await.unwrap();
}
env.traps.enter_subshell(&env.system, false, true).await;
let state = state.borrow();
let process = &state.processes[&env.main_pid];
assert_eq!(process.disposition(SIGTSTP), Disposition::Ignore);
assert_eq!(process.disposition(SIGTTIN), Disposition::Ignore);
assert_eq!(process.disposition(SIGTTOU), Disposition::Ignore);
assert_eq!(process.state(), ProcessState::Running);
})
}
#[test]
fn no_stopper_internal_dispositions_enabled_to_be_kept_ignored() {
in_virtual_system(|mut env, state| async move {
for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
env.traps
.set_action(
&env.system,
signal,
Action::Command("".into()),
Location::dummy(""),
false,
)
.await
.unwrap();
}
env.traps.enter_subshell(&env.system, false, true).await;
let state = state.borrow();
let process = &state.processes[&env.main_pid];
assert_eq!(process.disposition(SIGTSTP), Disposition::Default);
assert_eq!(process.disposition(SIGTTIN), Disposition::Default);
assert_eq!(process.disposition(SIGTTOU), Disposition::Default);
})
}
#[test]
fn catching_signal() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let command = Action::Command("echo INT".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGINT, command, origin, false)
.now_or_never()
.unwrap()
.unwrap();
let command = Action::Command("echo TERM".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGTERM, command, origin, false)
.now_or_never()
.unwrap()
.unwrap();
trap_set.catch_signal(SIGCHLD);
trap_set.catch_signal(SIGINT);
let trap_state = trap_set.get_state(SIGINT).0.unwrap();
assert!(trap_state.pending, "trap_state = {trap_state:?}");
let trap_state = trap_set.get_state(SIGTERM).0.unwrap();
assert!(!trap_state.pending, "trap_state = {trap_state:?}");
}
#[test]
fn taking_signal_if_caught() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
let command = Action::Command("echo INT".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGINT, command, origin, false)
.now_or_never()
.unwrap()
.unwrap();
let result = trap_set.take_signal_if_caught(SIGINT);
assert_eq!(result, None);
trap_set.catch_signal(SIGINT);
let result = trap_set.take_signal_if_caught(SIGINT);
assert!(!result.unwrap().pending);
let result = trap_set.take_signal_if_caught(SIGINT);
assert_eq!(result, None);
}
#[test]
fn taking_caught_signal() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
assert_eq!(trap_set.take_caught_signal(), None);
let command = Action::Command("echo INT".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGINT, command, origin, false)
.now_or_never()
.unwrap()
.unwrap();
let command = Action::Command("echo TERM".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGTERM, command, origin, false)
.now_or_never()
.unwrap()
.unwrap();
let command = Action::Command("echo USR1".into());
let origin = Location::dummy("origin");
trap_set
.set_action(&system, SIGUSR1, command, origin, false)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(trap_set.take_caught_signal(), None);
trap_set.catch_signal(SIGINT);
trap_set.catch_signal(SIGUSR1);
let result = trap_set.take_caught_signal().unwrap();
match result.0 {
SIGINT => {
assert_eq!(result.1.action, Action::Command("echo INT".into()));
assert!(!result.1.pending);
let result = trap_set.take_caught_signal().unwrap();
assert_eq!(result.0, SIGUSR1);
assert_eq!(result.1.action, Action::Command("echo USR1".into()));
assert!(!result.1.pending);
}
SIGUSR1 => {
assert_eq!(result.1.action, Action::Command("echo USR1".into()));
assert!(!result.1.pending);
let result = trap_set.take_caught_signal().unwrap();
assert_eq!(result.0, SIGINT);
assert_eq!(result.1.action, Action::Command("echo INT".into()));
assert!(!result.1.pending);
}
_ => panic!("wrong signal: {result:?}"),
}
assert_eq!(trap_set.take_caught_signal(), None);
}
#[test]
fn enabling_internal_disposition_for_sigchld() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Catch);
}
#[test]
fn enabling_internal_dispositions_for_terminators() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(system.0.borrow()[&SIGINT], Disposition::Catch);
assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
}
#[test]
fn enabling_internal_dispositions_for_stoppers() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Ignore);
}
#[test]
fn disabling_internal_dispositions_for_initially_defaulted_signals() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.disable_internal_dispositions(&system)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Default);
assert_eq!(system.0.borrow()[&SIGINT], Disposition::Default);
assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Default);
assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Default);
assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Default);
assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Default);
assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Default);
}
fn ignore_signals(system: &mut DummySystem) {
system.0.borrow_mut().extend(
[SIGCHLD, SIGINT, SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU]
.into_iter()
.map(|signal| (signal, Disposition::Ignore)),
)
}
#[test]
fn disabling_internal_dispositions_for_initially_ignored_signals() {
let mut system = DummySystem::default();
ignore_signals(&mut system);
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.disable_internal_dispositions(&system)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGINT], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Ignore);
}
#[test]
fn disabling_internal_dispositions_after_enabling_twice() {
let mut system = DummySystem::default();
ignore_signals(&mut system);
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.disable_internal_dispositions(&system)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGINT], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Ignore);
}
#[test]
fn disabling_internal_dispositions_without_enabling() {
let mut system = DummySystem::default();
ignore_signals(&mut system);
let mut trap_set = TrapSet::default();
trap_set
.disable_internal_dispositions(&system)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGINT], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Ignore);
}
#[test]
fn reenabling_internal_dispositions() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.disable_internal_dispositions(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Catch);
assert_eq!(system.0.borrow()[&SIGINT], Disposition::Catch);
assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Ignore);
assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Ignore);
}
#[test]
fn setting_trap_to_ignore_after_enabling_internal_disposition() {
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
let origin = Location::dummy("origin");
let result = trap_set
.set_action(&system, SIGCHLD, Action::Ignore, origin, false)
.now_or_never()
.unwrap();
assert_eq!(result, Ok(()));
assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Catch);
}
#[test]
fn resetting_trap_from_ignore_no_override_after_enabling_internal_dispositions() {
let mut system = DummySystem::default();
ignore_signals(&mut system);
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
for signal in [SIGCHLD, SIGINT] {
let origin = Location::dummy("origin");
let result = trap_set
.set_action(&system, signal, Action::Default, origin, false)
.now_or_never()
.unwrap();
assert_eq!(result, Err(SetActionError::InitiallyIgnored));
assert_eq!(system.0.borrow()[&signal], Disposition::Catch);
}
for signal in [SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU] {
let origin = Location::dummy("origin");
let result = trap_set
.set_action(&system, signal, Action::Default, origin, false)
.now_or_never()
.unwrap();
assert_eq!(result, Err(SetActionError::InitiallyIgnored));
assert_eq!(system.0.borrow()[&signal], Disposition::Ignore);
}
}
#[test]
fn resetting_trap_from_ignore_override_after_enabling_internal_dispositions() {
let mut system = DummySystem::default();
ignore_signals(&mut system);
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_stoppers(&system)
.now_or_never()
.unwrap()
.unwrap();
for signal in [SIGCHLD, SIGINT] {
let origin = Location::dummy("origin");
let result = trap_set
.set_action(&system, signal, Action::Ignore, origin.clone(), true)
.now_or_never()
.unwrap();
assert_eq!(result, Ok(()));
assert_eq!(
trap_set.get_state(signal),
(
Some(&TrapState {
action: Action::Ignore,
origin: Origin::User(origin),
pending: false
}),
None
)
);
assert_eq!(system.0.borrow()[&signal], Disposition::Catch);
}
for signal in [SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU] {
let origin = Location::dummy("origin");
let result = trap_set
.set_action(&system, signal, Action::Ignore, origin.clone(), true)
.now_or_never()
.unwrap();
assert_eq!(result, Ok(()));
assert_eq!(
trap_set.get_state(signal),
(
Some(&TrapState {
action: Action::Ignore,
origin: Origin::User(origin),
pending: false
}),
None
)
);
assert_eq!(system.0.borrow()[&signal], Disposition::Ignore);
}
}
#[test]
fn disabling_internal_disposition_with_ignore_trap() {
let signals = [SIGCHLD, SIGINT, SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU];
let system = DummySystem::default();
let mut trap_set = TrapSet::default();
trap_set
.enable_internal_disposition_for_sigchld(&system)
.now_or_never()
.unwrap()
.unwrap();
trap_set
.enable_internal_dispositions_for_terminators(&system)
.now_or_never()
.unwrap()
.unwrap();
let origin = Location::dummy("origin");
for signal in signals {
trap_set
.set_action(&system, signal, Action::Ignore, origin.clone(), false)
.now_or_never()
.unwrap()
.unwrap();
}
trap_set
.disable_internal_dispositions(&system)
.now_or_never()
.unwrap()
.unwrap();
for signal in signals {
assert_eq!(
trap_set.get_state(signal),
(
Some(&TrapState {
action: Action::Ignore,
origin: Origin::User(origin.clone()),
pending: false
}),
None
)
);
assert_eq!(system.0.borrow()[&signal], Disposition::Ignore);
}
}
}