tools: daemon: add a default signalfd helper
Proxy and daemon for now just want to handle reload via `SIGHUP`, so provide a helper creating the signalfd stream doing that - this is simply a filtered stream which passes the remaining signals through, so it can be used exactly like the signalfd stream could before to add more signals. Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
		@ -5,6 +5,9 @@ use std::os::unix::ffi::OsStrExt;
 | 
			
		||||
use std::panic::UnwindSafe;
 | 
			
		||||
 | 
			
		||||
use failure::*;
 | 
			
		||||
use tokio::prelude::*;
 | 
			
		||||
 | 
			
		||||
use crate::tools::signalfd::{SigSet, SignalFd};
 | 
			
		||||
 | 
			
		||||
// Unfortunately FnBox is nightly-only and Box<FnOnce> is unusable, so just use Box<Fn>...
 | 
			
		||||
pub type BoxedStoreFunc = Box<dyn Fn() -> Result<String, Error> + UnwindSafe + Send>;
 | 
			
		||||
@ -113,3 +116,55 @@ impl ReexecStore {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Provide a default signal handler for daemons (daemon & proxy).
 | 
			
		||||
/// When the first `SIGHUP` is received, the `reexec_store`'s `fork_restart` method will be
 | 
			
		||||
/// triggered. Any further `SIGHUP` is "passed through".
 | 
			
		||||
pub fn default_signalfd_stream<F>(
 | 
			
		||||
    reexec_store: ReexecStore,
 | 
			
		||||
    before_reload: F,
 | 
			
		||||
) -> Result<impl Stream<Item = nix::sys::signalfd::siginfo, Error = Error>, Error>
 | 
			
		||||
where
 | 
			
		||||
    F: FnOnce() -> Result<(), Error>,
 | 
			
		||||
{
 | 
			
		||||
    use nix::sys::signal::{SigmaskHow, Signal, sigprocmask};
 | 
			
		||||
 | 
			
		||||
    // Block SIGHUP for *all* threads and use it for a signalfd handler:
 | 
			
		||||
    let mut sigs = SigSet::empty();
 | 
			
		||||
    sigs.add(Signal::SIGHUP);
 | 
			
		||||
    sigprocmask(SigmaskHow::SIG_BLOCK, Some(&sigs), None)?;
 | 
			
		||||
 | 
			
		||||
    let sigfdstream = SignalFd::new(&sigs)?;
 | 
			
		||||
    let mut reexec_store = Some(reexec_store);
 | 
			
		||||
    let mut before_reload = Some(before_reload);
 | 
			
		||||
 | 
			
		||||
    Ok(sigfdstream
 | 
			
		||||
        .filter_map(move |si| {
 | 
			
		||||
            // FIXME: logging should be left to the user of this:
 | 
			
		||||
            eprintln!("received signal: {}", si.ssi_signo);
 | 
			
		||||
 | 
			
		||||
            if si.ssi_signo == Signal::SIGHUP as u32 {
 | 
			
		||||
                // The firs time this happens we will try to start a new process which should take
 | 
			
		||||
                // over.
 | 
			
		||||
                if let Some(reexec_store) = reexec_store.take() {
 | 
			
		||||
                    if let Err(e) = (before_reload.take().unwrap())() {
 | 
			
		||||
                        return Some(Err(e));
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    match reexec_store.fork_restart() {
 | 
			
		||||
                        Ok(_) => return None,
 | 
			
		||||
                        Err(e) => return Some(Err(e)),
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // pass the rest through:
 | 
			
		||||
            Some(Ok(si))
 | 
			
		||||
        })
 | 
			
		||||
        // filter_map cannot produce errors, so we create Result<> items instead, iow:
 | 
			
		||||
        //   before: Stream<Item = siginfo, Error>
 | 
			
		||||
        //   after:  Stream<Item = Result<siginfo, Error>, Error>.
 | 
			
		||||
        // use and_then to lift out the wrapped result:
 | 
			
		||||
        .and_then(|si_res| si_res)
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user