diff --git a/src/lib.rs b/src/lib.rs index 0f535ed..1c58fb2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,22 +73,43 @@ static INIT_LOCK: Mutex<()> = Mutex::new(()); /// ``` /// /// # Warning -/// On Unix, the handler registration for `SIGINT`, (`SIGTERM` and `SIGHUP` if termination feature is enabled) or `SA_SIGINFO` -/// posix signal handlers will fail if a signal handler is already present. On Windows, multiple handler routines are allowed, -/// but they are called on a last-registered, first-called basis until the signal is handled. +/// On Unix, the handler registration for `SIGINT`, (`SIGTERM` and `SIGHUP` if termination feature +/// is enabled) or `SA_SIGINFO` posix signal handlers will be overwritten. On Windows, multiple +/// handler routines are allowed, but they are called on a last-registered, first-called basis +/// until the signal is handled. +/// +/// ctrlc::try_set_handler will error (on Unix) if another signal handler exists for the same +/// signal(s) that ctrlc is trying to attach the handler to. /// /// On Unix, signal dispositions and signal handlers are inherited by child processes created via /// `fork(2)` on, but not by child processes created via `execve(2)`. /// Signal handlers are not inherited on Windows. /// /// # Errors -/// Will return an error if another `ctrlc::set_handler()` handler exists or if a -/// system error occurred while setting the handler. +/// Will return an error if a system error occurred while setting the handler. /// /// # Panics /// Any panic in the handler will not be caught and will cause the signal handler thread to stop. -/// pub fn set_handler(user_handler: F) -> Result<(), Error> +where + F: FnMut() + 'static + Send, +{ + init_and_set_handler(user_handler, true) +} + +/// The same as ctrlc::set_handler but errors if a handler already exists for the signal(s). +/// +/// # Errors +/// Will return an error if another handler exists or if a system error occurred while setting the +/// handler. +pub fn try_set_handler(user_handler: F) -> Result<(), Error> +where + F: FnMut() + 'static + Send, +{ + init_and_set_handler(user_handler, false) +} + +fn init_and_set_handler(user_handler: F, overwrite: bool) -> Result<(), Error> where F: FnMut() + 'static + Send, { @@ -96,7 +117,7 @@ where let _guard = INIT_LOCK.lock().unwrap(); if !INIT.load(Ordering::Relaxed) { - set_handler_inner(user_handler)?; + set_handler_inner(user_handler, overwrite)?; INIT.store(true, Ordering::Release); return Ok(()); } @@ -105,12 +126,12 @@ where Err(Error::MultipleHandlers) } -fn set_handler_inner(mut user_handler: F) -> Result<(), Error> +fn set_handler_inner(mut user_handler: F, overwrite: bool) -> Result<(), Error> where F: FnMut() + 'static + Send, { unsafe { - match platform::init_os_handler() { + match platform::init_os_handler(overwrite) { Ok(_) => {} Err(err) => { return Err(err.into()); diff --git a/src/platform/unix/mod.rs b/src/platform/unix/mod.rs index ba9531c..d8960cd 100644 --- a/src/platform/unix/mod.rs +++ b/src/platform/unix/mod.rs @@ -83,7 +83,7 @@ fn pipe2(flags: nix::fcntl::OFlag) -> nix::Result<(RawFd, RawFd)> { /// Will return an error if a system error occurred. /// #[inline] -pub unsafe fn init_os_handler() -> Result<(), Error> { +pub unsafe fn init_os_handler(overwrite: bool) -> Result<(), Error> { use nix::fcntl; use nix::sys::signal; @@ -113,7 +113,7 @@ pub unsafe fn init_os_handler() -> Result<(), Error> { Ok(old) => old, Err(e) => return Err(close_pipe(e)), }; - if sigint_old.handler() != signal::SigHandler::SigDfl { + if !overwrite && sigint_old.handler() != signal::SigHandler::SigDfl { signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap(); return Err(close_pipe(nix::Error::EEXIST)); } @@ -127,7 +127,7 @@ pub unsafe fn init_os_handler() -> Result<(), Error> { return Err(close_pipe(e)); } }; - if sigterm_old.handler() != signal::SigHandler::SigDfl { + if !overwrite && sigterm_old.handler() != signal::SigHandler::SigDfl { signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap(); signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap(); return Err(close_pipe(nix::Error::EEXIST)); @@ -140,7 +140,7 @@ pub unsafe fn init_os_handler() -> Result<(), Error> { return Err(close_pipe(e)); } }; - if sighup_old.handler() != signal::SigHandler::SigDfl { + if !overwrite && sighup_old.handler() != signal::SigHandler::SigDfl { signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap(); signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap(); signal::sigaction(signal::Signal::SIGHUP, &sighup_old).unwrap(); diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 9e3279a..5f701f7 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -41,7 +41,7 @@ unsafe extern "system" fn os_handler(_: u32) -> BOOL { /// Will return an error if a system error occurred. /// #[inline] -pub unsafe fn init_os_handler() -> Result<(), Error> { +pub unsafe fn init_os_handler(_overwrite: bool) -> Result<(), Error> { SEMAPHORE = CreateSemaphoreA(ptr::null_mut(), 0, MAX_SEM_COUNT, ptr::null()); if SEMAPHORE == 0 { return Err(io::Error::last_os_error()); diff --git a/tests/main/issue_97.rs b/tests/main/issue_97.rs index f4b794c..9791732 100644 --- a/tests/main/issue_97.rs +++ b/tests/main/issue_97.rs @@ -16,7 +16,7 @@ use test_signal_hook::run_signal_hook; fn expect_multiple_handlers() { #[cfg(not(windows))] - match ctrlc::set_handler(|| {}) { + match ctrlc::try_set_handler(|| {}) { Err(ctrlc::Error::MultipleHandlers) => {} _ => panic!("Expected Error::MultipleHandlers"), } diff --git a/tests/main/mod.rs b/tests/main/mod.rs index 7b09c08..94914d4 100644 --- a/tests/main/mod.rs +++ b/tests/main/mod.rs @@ -11,9 +11,6 @@ mod harness; use harness::{platform, run_harness}; -mod test_signal_hook; -use test_signal_hook::run_signal_hook; - use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -42,7 +39,6 @@ fn test_set_handler() { fn tests() { run_tests!(test_set_handler); - run_tests!(run_signal_hook); } fn main() {