chiark / gitweb /
daemon: wip
[hippotat.git] / server / daemon.rs
index 3946eeb58f3255624431dd6ecc9fa59ad28f96b9..14cd1c2c1fe8a97287cc36fa2bf0d1931b2dc0cc 100644 (file)
@@ -12,6 +12,7 @@ use std::io::IoSlice;
 use std::os::unix::io::RawFd;
 use std::slice;
 use std::str;
+use std::thread::panicking;
 
 use extend::ext;
 
@@ -19,6 +20,7 @@ use nix::errno::*;
 use nix::fcntl::*;
 use nix::unistd::*;
 use nix::sys::stat::*;
+use nix::sys::signal::*;
 use nix::sys::uio::*;
 use nix::sys::wait::*;
 
@@ -26,6 +28,8 @@ use hippotat::prelude as prelude;
 use prelude::default;
 
 pub struct Daemoniser {
+  drop_bomb: Option<()>,
+  intermediate_pid: Pid,
   null_fd: RawFd,
   st_wfd: RawFd,
 }
@@ -94,6 +98,14 @@ unsafe fn mdup2(oldfd: RawFd, newfd: RawFd, what: &str) {
   }
 }
 
+unsafe fn write_status(st_wfd: RawFd, estatus: u8) {
+  match write(st_wfd, slice::from_ref(&estatus)) {
+    Ok(1) => {}
+    Ok(_) => crashm("write child startup exit status: short write"),
+    Err(e) => crashe("write child startup exit status", e),
+  }
+}
+
 unsafe fn parent(st_rfd: RawFd) -> ! {
   let mut exitstatus = 0u8;
   loop {
@@ -107,7 +119,7 @@ unsafe fn parent(st_rfd: RawFd) -> ! {
   }
 }
 
-unsafe fn intermediate(child: Pid, report_pipe: RawFd) -> ! {
+unsafe fn intermediate(child: Pid, st_wfd: RawFd) -> ! {
   let mut wstatus: c_int = 0;
 
   let r = libc::waitpid(child.as_raw(), &mut wstatus, 0);
@@ -122,11 +134,8 @@ unsafe fn intermediate(child: Pid, report_pipe: RawFd) -> ! {
       let estatus: u8 = estatus.try_into()
         .unwrap_or_else(|_| crashm(
           "await child startup status: exit status out of range!"));
-      match write(report_pipe, slice::from_ref(&estatus)) {
-        Ok(1) => libc::_exit(0),
-        Ok(_) => crashm("write child startup exit status: short write"),
-        Err(e) => crashe("write child startup exit status", e),
-      }
+      write_status(st_wfd, estatus);
+      libc::_exit(0);
     }
 
     WaitStatus::Signaled(_, signal, coredump) => {
@@ -160,6 +169,7 @@ impl Daemoniser {
 
       close(st_rfd).context("close st_rfd pipe");
       setsid().context("setsid");
+      let intermediate_pid = Pid::this();
 
       match fork().context("fork (2)") {
         ForkResult::Child => { }
@@ -169,10 +179,43 @@ impl Daemoniser {
       }
 
       Daemoniser {
+        drop_bomb: Some(()),
+        intermediate_pid,
         null_fd,
         st_wfd,
       }
     }
   }
+
+  pub fn complete(mut self) {
+    unsafe {
+      mdup2(self.null_fd, 1, "null over stdin");
+
+      if Pid::parent() != self.intermediate_pid {
+        crashm(
+          "startup complete, but our parent is no longer the intermediate?");
+      }
+      kill(self.intermediate_pid, Some(Signal::SIGKILL))
+        .context("kill intermediate (after startup complete)");
+
+      write_status(self.st_wfd, 0);
+      mdup2(self.null_fd, 2, "null over stderrr");
+
+      self.drop_bomb.take();
+    }
+  }
 }
 
+impl Drop for Daemoniser {
+  fn drop(&mut self) {
+    if let Some(()) = self.drop_bomb.take() {
+      if panicking() {
+        // We will crash in due course, having printed some messages
+        // to stderr, presumably.
+        return
+      } else {
+        panic!("Daemonizer object dropped unexpectedly, startup failed");
+      }
+    }
+  }
+}