use std::path::Path;
use anyhow::{Context, Result};
pub fn daemonize(log_path: &Path) -> Result<()> {
use nix::unistd::{ForkResult, fork, setsid};
use std::fs::File;
use std::io::Read;
use std::os::fd::{AsFd, AsRawFd, FromRawFd, OwnedFd};
if let Some(parent) = log_path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("failed to create log directory: {}", parent.display()))?;
}
let (pipe_read, pipe_write) = nix::unistd::pipe().context("failed to create pipe")?;
let pipe_read_fd = pipe_read.as_raw_fd();
match unsafe { fork() }.context("first fork failed")? {
ForkResult::Parent { .. } => {
drop(pipe_write);
let mut reader = unsafe { File::from_raw_fd(pipe_read_fd) };
std::mem::forget(pipe_read);
let mut buf = String::new();
let _ = reader.read_to_string(&mut buf);
let pid_str = buf.trim();
if pid_str.is_empty() {
eprintln!("orchestratord daemonized");
} else {
eprintln!("orchestratord daemonized (PID {pid_str})");
}
std::process::exit(0);
}
ForkResult::Child => {
drop(pipe_read);
}
}
setsid().context("setsid failed")?;
match unsafe { fork() }.context("second fork failed")? {
ForkResult::Parent { .. } => {
drop(pipe_write);
std::process::exit(0);
}
ForkResult::Child => {}
}
{
let pid = std::process::id();
let pid_bytes = pid.to_string().into_bytes();
let _ = nix::unistd::write(&pipe_write, &pid_bytes);
drop(pipe_write);
}
let devnull = File::open("/dev/null").context("failed to open /dev/null")?;
let mut stdin_fd = unsafe { OwnedFd::from_raw_fd(0) };
nix::unistd::dup2(devnull.as_fd(), &mut stdin_fd).context("dup2 stdin failed")?;
std::mem::forget(stdin_fd);
let logfile = File::options()
.create(true)
.append(true)
.open(log_path)
.with_context(|| format!("failed to open daemon log: {}", log_path.display()))?;
let mut stdout_fd = unsafe { OwnedFd::from_raw_fd(1) };
nix::unistd::dup2(logfile.as_fd(), &mut stdout_fd).context("dup2 stdout failed")?;
std::mem::forget(stdout_fd);
let mut stderr_fd = unsafe { OwnedFd::from_raw_fd(2) };
nix::unistd::dup2(logfile.as_fd(), &mut stderr_fd).context("dup2 stderr failed")?;
std::mem::forget(stderr_fd);
Ok(())
}