]> git.proxmox.com Git - proxmox.git/commitdiff
use double-fork for reload
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Thu, 25 Apr 2019 11:00:02 +0000 (11:00 +0000)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Thu, 25 Apr 2019 11:02:12 +0000 (11:02 +0000)
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>
src/tools/daemon.rs

index 9dc3dc8e663cd75d3066892b9d944941aa9e5aa3..af71828b2b793df10f67a15f93590b7229686057 100644 (file)
@@ -11,6 +11,8 @@ use tokio::prelude::*;
 
 use crate::server;
 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>...
 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())?);
         }
 
+        // Synchronisation pipe:
+        let (pin, pout) = super::pipe()?;
+
         // Start ourselves in the background:
         use nix::unistd::{fork, ForkResult};
         match fork() {
             Ok(ForkResult::Child) => {
-                // 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 || self.do_exec(exe, new_args)) {
-                    Ok(_) => eprintln!("do_exec returned unexpectedly!"),
-                    Err(_) => eprintln!("panic in re-exec"),
+                // Double fork so systemd can supervise us without nagging...
+                match fork() {
+                    Ok(ForkResult::Child) => {
+                        std::mem::drop(pin);
+                        // 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:
                 unsafe {
@@ -102,14 +132,27 @@ impl Reloader {
                 }
             }
             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)) {
                     log::error!("failed to notify systemd about the new main pid: {}", e);
                 }
                 Ok(())
             }
             Err(e) => {
-                eprintln!("fork() failed, restart delayed: {}", e);
+                log::error!("fork() failed, restart delayed: {}", e);
                 Ok(())
             }
         }