From: Ian Jackson Date: Sun, 25 Sep 2022 13:17:51 +0000 (+0100) Subject: daemon: wip X-Git-Tag: hippotat/1.0.0~63 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=3d8947f50cba9a1ee6e85f4b3c02efb71e4f3b16;p=hippotat.git daemon: wip Signed-off-by: Ian Jackson --- diff --git a/server/daemon.rs b/server/daemon.rs index 05252e7..3946eeb 100644 --- a/server/daemon.rs +++ b/server/daemon.rs @@ -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 nix::Result { } } -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, + } } } } -*/