use double-fork for reload

To ensure the new process' parent is pid 1, so systemd won't
complain about supervising a process it does not own.

Fixes the following log spam on reloads:
Apr 25 10:50:54 deb-dev systemd[1]: proxmox-backup.service: Supervising process 1625 which is not our child. We'll most likely not notice when it exits.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2019-04-25 11:00:02 +00:00
parent efd1536eb7
commit 5e5eed5c3b
1 changed files with 50 additions and 7 deletions

View File

@ -11,6 +11,8 @@ use tokio::prelude::*;
use crate::server; use crate::server;
use crate::tools::{fd_change_cloexec, self}; use crate::tools::{fd_change_cloexec, self};
use crate::tools::read::*;
use crate::tools::write::*;
// Unfortunately FnBox is nightly-only and Box<FnOnce> is unusable, so just use Box<Fn>... // Unfortunately FnBox is nightly-only and Box<FnOnce> is unusable, so just use Box<Fn>...
pub type BoxedStoreFunc = Box<dyn FnMut() -> Result<String, Error> + UnwindSafe + Send>; pub type BoxedStoreFunc = Box<dyn FnMut() -> Result<String, Error> + UnwindSafe + Send>;
@ -86,15 +88,43 @@ impl Reloader {
new_args.push(CString::new(arg.as_bytes())?); new_args.push(CString::new(arg.as_bytes())?);
} }
// Synchronisation pipe:
let (pin, pout) = super::pipe()?;
// Start ourselves in the background: // Start ourselves in the background:
use nix::unistd::{fork, ForkResult}; use nix::unistd::{fork, ForkResult};
match fork() { match fork() {
Ok(ForkResult::Child) => { Ok(ForkResult::Child) => {
// At this point we call pre-exec helpers. We must be certain that if they fail for // Double fork so systemd can supervise us without nagging...
// whatever reason we can still call `_exit()`, so use catch_unwind. match fork() {
match std::panic::catch_unwind(move || self.do_exec(exe, new_args)) { Ok(ForkResult::Child) => {
Ok(_) => eprintln!("do_exec returned unexpectedly!"), std::mem::drop(pin);
Err(_) => eprintln!("panic in re-exec"), // At this point we call pre-exec helpers. We must be certain that if they fail for
// whatever reason we can still call `_exit()`, so use catch_unwind.
match std::panic::catch_unwind(move || {
let mut pout = unsafe {
std::fs::File::from_raw_fd(pout.into_raw_fd())
};
let pid = nix::unistd::Pid::this();
if let Err(e) = pout.write_value(&pid.as_raw()) {
log::error!("failed to send new server PID to parent: {}", e);
unsafe {
libc::_exit(-1);
}
}
std::mem::drop(pout);
self.do_exec(exe, new_args)
})
{
Ok(_) => eprintln!("do_exec returned unexpectedly!"),
Err(_) => eprintln!("panic in re-exec"),
}
}
Ok(ForkResult::Parent { child }) => {
std::mem::drop((pin, pout));
log::debug!("forked off a new server (second pid: {})", child);
}
Err(e) => log::error!("fork() failed, restart delayed: {}", e),
} }
// No matter how we managed to get here, this is the time where we bail out quickly: // No matter how we managed to get here, this is the time where we bail out quickly:
unsafe { unsafe {
@ -102,14 +132,27 @@ impl Reloader {
} }
} }
Ok(ForkResult::Parent { child }) => { Ok(ForkResult::Parent { child }) => {
eprintln!("forked off a new server (pid: {})", child); log::debug!("forked off a new server (first pid: {}), waiting for 2nd pid", child);
std::mem::drop(pout);
let mut pin = unsafe {
std::fs::File::from_raw_fd(pin.into_raw_fd())
};
let child = nix::unistd::Pid::from_raw(match pin.read_value() {
Ok(v) => v,
Err(e) => {
log::error!("failed to receive pid of double-forked child process: {}", e);
// systemd will complain but won't kill the service...
return Ok(());
}
});
if let Err(e) = systemd_notify(SystemdNotify::MainPid(child)) { if let Err(e) = systemd_notify(SystemdNotify::MainPid(child)) {
log::error!("failed to notify systemd about the new main pid: {}", e); log::error!("failed to notify systemd about the new main pid: {}", e);
} }
Ok(()) Ok(())
} }
Err(e) => { Err(e) => {
eprintln!("fork() failed, restart delayed: {}", e); log::error!("fork() failed, restart delayed: {}", e);
Ok(()) Ok(())
} }
} }