use std::os::unix::io::RawFd;
use std::slice;
use std::str;
+use std::thread::panicking;
use extend::ext;
use nix::fcntl::*;
use nix::unistd::*;
use nix::sys::stat::*;
+use nix::sys::signal::*;
use nix::sys::uio::*;
use nix::sys::wait::*;
use prelude::default;
pub struct Daemoniser {
+ drop_bomb: Option<()>,
+ intermediate_pid: Pid,
null_fd: RawFd,
st_wfd: RawFd,
}
}
}
+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 {
}
}
-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);
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) => {
close(st_rfd).context("close st_rfd pipe");
setsid().context("setsid");
+ let intermediate_pid = Pid::this();
match fork().context("fork (2)") {
ForkResult::Child => { }
}
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");
+ }
+ }
+ }
+}