#![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;
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<'_>]) -> ! {
}
}
-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) -> ! {
}
}
-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,
+ }
}
}
}
-*/