Skip to content

Commit

Permalink
Add signal::sigaction_current()
Browse files Browse the repository at this point in the history
Provide a way to query the currently installed sigaction.

The decision to add `sigaction_current()` instead of just exposing the
`sigaction_inner()` function was to avoid any confusion over the
semantics of passing in a `None` `sigaction` argument (eg: someone
thinking that it meant remove or reset the action).

This builds towards nix-rust#2172.
  • Loading branch information
Chris Pick committed Nov 14, 2023
1 parent 6616ba5 commit 0ddb10c
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 6 deletions.
32 changes: 26 additions & 6 deletions src/sys/signal.rs
Expand Up @@ -849,6 +849,16 @@ impl SigAction {
}
}

unsafe fn sigaction_inner(signal: Signal, sigaction: Option<&SigAction>) -> Result<SigAction> {
let mut oldact = mem::MaybeUninit::<libc::sigaction>::uninit();

let res = libc::sigaction(signal as libc::c_int,
sigaction.map_or(ptr::null(), |sigaction| &sigaction.sigaction as *const libc::sigaction),
oldact.as_mut_ptr());

Errno::result(res).map(|_| SigAction { sigaction: oldact.assume_init() })
}

/// Changes the action taken by a process on receipt of a specific signal.
///
/// `signal` can be any signal except `SIGKILL` or `SIGSTOP`. On success, it returns the previous
Expand All @@ -867,13 +877,23 @@ impl SigAction {
/// pointer is valid. In that case, this function effectively dereferences a
/// raw pointer of unknown provenance.
pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> {
let mut oldact = mem::MaybeUninit::<libc::sigaction>::uninit();

let res = libc::sigaction(signal as libc::c_int,
&sigaction.sigaction as *const libc::sigaction,
oldact.as_mut_ptr());
sigaction_inner(signal, Some(sigaction))
}

Errno::result(res).map(|_| SigAction { sigaction: oldact.assume_init() })
/// Gets the current action a process will take on receipt of a specific signal.
///
/// `signal` can be any signal except `SIGKILL` or `SIGSTOP`. On success, it returns the current
/// action for the given signal. The current action will always remain in place, unchanged.
///
/// # Safety
///
/// There is no guarantee that the old signal handler was installed
/// correctly. If it was installed by this crate, it will be. But if it was
/// installed by, for example, C code, then there is no guarantee its function
/// pointer is valid. In that case, this function effectively dereferences a
/// raw pointer of unknown provenance.
pub unsafe fn sigaction_current(signal: Signal) -> Result<SigAction> {
sigaction_inner(signal, None)
}

/// Signal management (see [signal(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html))
Expand Down
42 changes: 42 additions & 0 deletions test/sys/test_signal.rs
Expand Up @@ -35,6 +35,48 @@ fn test_old_sigaction_flags() {
unsafe { sigaction(SIGINT, &oact) }.unwrap();
}

#[test]
fn test_current_sigaction() {
let _m = crate::SIGNAL_MTX.lock();

let oact = unsafe {
sigaction(
SIGINT,
&SigAction::new(
SigHandler::SigDfl,
SaFlags::empty(),
SigSet::empty(),
),
)
}
.unwrap();

assert_eq!(
unsafe { sigaction_current(SIGINT) }.unwrap().handler(),
SigHandler::SigDfl
);

unsafe {
sigaction(
SIGINT,
&SigAction::new(
SigHandler::SigIgn,
SaFlags::empty(),
SigSet::empty(),
),
)
}
.unwrap();

assert_eq!(
unsafe { sigaction_current(SIGINT) }.unwrap().handler(),
SigHandler::SigIgn
);

// restore original
unsafe { sigaction(SIGINT, &oact) }.unwrap();
}

#[test]
fn test_sigprocmask_noop() {
sigprocmask(SigmaskHow::SIG_BLOCK, None, None)
Expand Down

0 comments on commit 0ddb10c

Please sign in to comment.