chiark / gitweb /
daemon: wip
[hippotat.git] / server / daemon.rs
index 05252e704da2dc694f0f3b40834e601d093e36a1..3946eeb58f3255624431dd6ecc9fa59ad28f96b9 100644 (file)
@@ -6,8 +6,12 @@
 #![allow(dead_code)]
 #![allow(unused_macros)]
 
+use std::convert::TryInto;
+use std::ffi::{c_int, CStr};
 use std::io::IoSlice;
 use std::os::unix::io::RawFd;
+use std::slice;
+use std::str;
 
 use extend::ext;
 
@@ -18,7 +22,12 @@ use nix::sys::stat::*;
 use nix::sys::uio::*;
 use nix::sys::wait::*;
 
+use hippotat::prelude as prelude;
+use prelude::default;
+
 pub struct Daemoniser {
+  null_fd: RawFd,
+  st_wfd: RawFd,
 }
 
 fn crashv(ms: &[IoSlice<'_>]) -> ! {
@@ -28,22 +37,25 @@ fn crashv(ms: &[IoSlice<'_>]) -> ! {
   }
 }
   
-macro_rules! crashv { { $( $m:expr ),* $(,)? } => { {
-  let ms = [
+macro_rules! crashv { { $( $m:expr ),* $(,)? } => {
+  match [
     "hippotatd: ",
     $( $m, )*
     "\n",
-  ];
-  let ms = ms.map(|m| IoSlice::new(m.as_bytes()));
-  crashv(&ms)
-} } }
+  ] {
+    ms => {
+      let ms = ms.map(|m| IoSlice::new(m.as_bytes()));
+      crashv(&ms)
+    }
+  }
+} }
 
 macro_rules! cstr { { $b:tt } => {
-  CStr::from_bytes_with_nul(concat!($b b"\0"))
-    .unwrap_or_else(|| crashm("cstr not nul terminated?! bug!"))
+  CStr::from_bytes_with_nul($b)
+    .unwrap_or_else(|_| crashm("cstr not nul terminated?! bug!"))
 } }
 
-fn crash(m: &str) -> ! {
+fn crashm(m: &str) -> ! {
   crashv!(m)
 }
 fn crashe(m: &str, e: Errno) -> ! {
@@ -60,84 +72,107 @@ impl<T> nix::Result<T> {
   }
 }
 
-fn mdup2(oldfd: RawFd, newfd: RawFd, what: &str) {
+const ITOA_BUFL: usize = 12;
+fn c_itoa(value: c_int, buf: &mut [u8; ITOA_BUFL]) -> &str {
+  unsafe {
+    *buf = [b'.'; ITOA_BUFL];
+    libc::snprintf({ let buf: *mut u8 = buf.as_mut_ptr(); buf as *mut i8 },
+                   ITOA_BUFL-2,
+                   cstr!(b"%x\0").as_ptr(),
+                   value);
+  }
+  let s = buf.splitn(2, |&c| c == b'\0').next()
+    .unwrap_or_else(|| crashm("splitn no next"));
+  str::from_utf8(s).unwrap_or_else(|_| crashm("non-utf-8 from snprintf!"))
+}
+
+unsafe fn mdup2(oldfd: RawFd, newfd: RawFd, what: &str) {
   match dup2(oldfd, newfd) {
     Ok(got) if got == newfd => { },
-    Ok(_) => crash("dup2 gave wrong return value"),
+    Ok(_) => crashm("dup2 gave wrong return value"),
     Err(e) => crashv!("dup2 ", what, ": ", e.desc()),
   }
 }
-/*
-#[throws(&'static str)]
-unsafe fn handle_child_status(child: Pid, report_pipe: RawFd,
-                              wstatus: WaitStatus) -> ! {
-  let pid = wstatus.pid();
-  
 
-    
+unsafe fn parent(st_rfd: RawFd) -> ! {
+  let mut exitstatus = 0u8;
+  loop {
+    match read(st_rfd, slice::from_mut(&mut exitstatus)) {
+      Ok(0) => crashm("startup/daemonisation failed"),
+      Ok(1) => libc::_exit(exitstatus.into()),
+      Ok(_) => crashm("read startup: excess read!"),
+      Err(e) if e == Errno::EINTR => continue,
+      Err(e) => crashe("read startup signal pipe", e),
+    }
+  }
+}
+
+unsafe fn intermediate(child: Pid, report_pipe: RawFd) -> ! {
+  let mut wstatus: c_int = 0;
+
+  let r = libc::waitpid(child.as_raw(), &mut wstatus, 0);
+  if r == -1 { crashe("await child startup status",
+                      nix::errno::from_i32(errno())) }
+  if r != child.as_raw() { crashm("await child startup status: wrong pid") }
+
+  let cooked = WaitStatus::from_raw(child, wstatus)
+    .context("await child startup status: convert wait status");
+  match cooked {
+    WaitStatus::Exited(_, estatus) => {
+      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),
+      }
+    }
+
+    WaitStatus::Signaled(_, signal, coredump) => {
+      crashv!("startup failed: died due to signal: ", signal.as_str(),
+              if coredump { " (core dumped)" } else { "" });
+    },
+
+    _ => {
+      crashv!("child startup exit status was strange!  0x",
+              c_itoa(wstatus, &mut default()))
+    }
+  }
+}
 
 impl Daemoniser {
   pub fn phase1() -> Self {
     unsafe {
-      let null_fd = open(cstr!(b"/dev/null"), OFlag::O_RDWR, Mode::empty())
+      let null_fd = open(cstr!(b"/dev/null\0"), OFlag::O_RDWR, Mode::empty())
         .context("open /dev/null");
-      mdup2(null_fd, 0);
+      mdup2(null_fd, 0, "null onto stdin");
 
       let (st_rfd, st_wfd) = pipe().context("pipe");
 
-      mstch fork().context("fork (1)") {
+      match fork().context("fork (1)") {
+        ForkResult::Child => { }
         ForkResult::Parent { child: _ } => {
-          close(st_write).context("close st_write pipe");
-          let mut exitstatus = [0u8];
-          loop {
-            match read(st_read, &mut exitstatus) {
-              Ok(0) => crash("startup/daemonisation failed"),
-              Ok(1) => {},
-              Err(e) if e == Errno::EINTR => continue,
-              Err(e) => crashe("read startup signal pipe", en);
-            }
-          }
-          libc::_exit(exitstatus);
+          close(st_wfd).context("close st_wfd pipe");
+          parent(st_rfd)
         },
-
-        ForkResult::Child => { }
       }
 
-      close(st_read).context("close st_read pipe");
-
+      close(st_rfd).context("close st_rfd pipe");
       setsid().context("setsid");
 
       match fork().context("fork (2)") {
+        ForkResult::Child => { }
         ForkResult::Parent { child } => {
-          let wstatus = waitpid(child, None).context("waitpid startup");
-          match wstatus.pid() {
-            Some(got) if got == child => { },
-            Some(got) => crash("await child startup status: wrong pid"),
-            None => crash("await child startup status: no children?!"),
-          }
-
-          match wstatus {
-            WaitStatus::Exited(_, estatus) => {
-              let estatus: u8 = estatus.try_into()
-                .map_err(|_| "exit status out of range!")?;
-              match write(report_pipe, &estatus) {
-                Ok(1) => libc::_exit(0),
-                Ok(_) => crash("write child startup exit status: short write");
-                Err(e) => crashe("write child startup exit status", e);
-              },
-            }
-
-            WaitStatus::Signaled(pid, signal, coredump) => {
-              check_pid(pid);
-              crashv!("startup failed: died due to signal: ", signal.as_str(),
-                      if coredump { " (core dumped)" } else { "" });
-            },
-
-            _ => crashm("child startup exit status was strange!");
-            
+          intermediate(child, st_wfd)
+        },
+      }
 
+      Daemoniser {
+        null_fd,
+        st_wfd,
+      }
     }
   }
 }
 
-*/