use std::path::Path;
use anyhow::{Context, Result};
pub fn daemonize(log_path: &Path) -> Result<()> {
use nix::unistd::{fork, setsid, ForkResult};
use std::fs::File;
use std::io::Read;
use std::os::fd::FromRawFd;
use std::os::unix::io::AsRawFd;
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")?;
nix::unistd::dup2(devnull.as_raw_fd(), 0).context("dup2 stdin failed")?;
let logfile = File::options()
.create(true)
.append(true)
.open(log_path)
.with_context(|| format!("failed to open daemon log: {}", log_path.display()))?;
nix::unistd::dup2(logfile.as_raw_fd(), 1).context("dup2 stdout failed")?;
nix::unistd::dup2(logfile.as_raw_fd(), 2).context("dup2 stderr failed")?;
Ok(())
}