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:
parent
efd1536eb7
commit
5e5eed5c3b
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue