1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
use std::io::Error;
use std::sync::Arc;
use signal_hook::SigId;
use super::Handle;
impl Handle {
/// Installs a signal handler to invoke the reopening when a certain signal comes.
///
/// # Features
///
/// This is available only with the `signals` feature enabled.
///
/// # Notes
///
/// * Under the hood, this uses the [`signal-hook`](https://crates.io/signal-hook) crate, so
/// the same signal can be shared with other actions (to eg. also reload a configuration).
/// * The same restrictions, errors and panics as in the case of
/// [`signal_hook::register`](https://docs.rs/signal-hook/*/signal_hook/fn.register.html)
/// apply.
/// * This installs a signal handler. Signal handlers are program-global entities, so you may
/// be careful.
/// * If there are multiple handles for the same signal, they share their signal handler ‒ only
/// the first one for each signal registers one.
/// * Upon signal registration, the original handler is stored and called in chain from our own
/// signal handler.
/// * A single handle can be used for multiple signals.
/// * To unregister a handle from a signal handle, use the returned `SigId` and the
/// [`signal_hook::unregister`](https://docs.rs/signal-hook/*/signal_hook/fn.unregister.html).
pub fn register_signal(&self, signal: libc::c_int) -> Result<SigId, Error> {
signal_hook::flag::register(signal, Arc::clone(&self.0))
}
}
#[cfg(all(test, not(windows)))] // Not testing on windows, very limited signal support
mod tests {
use std::io::Read;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use std::time::Duration;
use super::*;
struct Fake(Arc<AtomicUsize>);
impl Read for Fake {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, Error> {
Ok(0) // Pretend it got closed… doesn't matter what we return here.
}
}
#[test]
fn signal_sent() {
let opened_times = Arc::new(AtomicUsize::new(0));
let opened_times_cp = Arc::clone(&opened_times);
let mut reopen = crate::Reopen::new(Box::new(move || {
opened_times_cp.fetch_add(1, Ordering::Relaxed);
Ok(Fake(Arc::clone(&opened_times_cp)))
}))
.unwrap();
assert_eq!(1, opened_times.load(Ordering::Relaxed));
let mut buf = [0];
assert_eq!(0, reopen.read(&mut buf).unwrap());
assert_eq!(1, opened_times.load(Ordering::Relaxed));
// Don't register sooner, in case some other test uses the signal.
reopen.handle().register_signal(libc::SIGHUP).unwrap();
// Now send us a signal
unsafe { libc::kill(libc::getpid(), libc::SIGHUP) };
// Wait a little for the signal to propagate, as it might arrive into another thread. The
// second here is not guaranteed to work, this is only a hack for tests.
thread::sleep(Duration::from_secs(1));
assert_eq!(0, reopen.read(&mut buf).unwrap());
// It got reopened
assert_eq!(2, opened_times.load(Ordering::Relaxed));
}
}