#[cfg(unix)]
use std::os::unix::process::CommandExt;
#[cfg(windows)]
use std::os::windows::process::CommandExt;
use std::{
io,
path::Path,
process::{
Command,
Stdio,
},
};
#[cfg(unix)]
pub fn spawn_daemon(
executable: &Path,
workspace_id: &str,
args: &[&str],
) -> io::Result<()> {
let mut cmd = Command::new(executable);
cmd
.arg("launch-daemon")
.arg(workspace_id)
.arg("--daemonize")
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.process_group(0);
let child = cmd.spawn()?;
let ws_id = workspace_id.to_string();
let child_pid = child.id() as i64;
otel::event!(
"daemon_spawn_started",
"workspace_id" = ws_id,
"child_pid" = child_pid
);
Ok(())
}
#[cfg(windows)]
pub fn spawn_daemon(
executable: &Path,
workspace_id: &str,
args: &[&str],
) -> io::Result<()> {
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
const DETACHED_PROCESS: u32 = 0x00000008;
const CREATE_NO_WINDOW: u32 = 0x08000000;
let mut cmd = Command::new(executable);
cmd
.arg("launch-daemon")
.arg(workspace_id)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.creation_flags(
CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS | CREATE_NO_WINDOW,
);
let child = cmd.spawn()?;
let ws_id = workspace_id.to_string();
let child_pid = child.id() as i64;
otel::event!(
"daemon_spawn_started",
"workspace_id" = ws_id,
"child_pid" = child_pid
);
Ok(())
}
#[cfg(unix)]
pub fn daemonize() -> io::Result<()> {
use std::os::unix::io::AsRawFd;
let pid = unsafe { libc::fork() };
if pid < 0 {
return Err(io::Error::last_os_error());
}
if pid > 0 {
std::process::exit(0);
}
if unsafe { libc::setsid() } < 0 {
return Err(io::Error::last_os_error());
}
let pid = unsafe { libc::fork() };
if pid < 0 {
return Err(io::Error::last_os_error());
}
if pid > 0 {
std::process::exit(0);
}
if unsafe { libc::chdir(c"/".as_ptr()) } < 0 {
return Err(io::Error::last_os_error());
}
let null_fd = std::fs::File::open("/dev/null")?;
let null_raw = null_fd.as_raw_fd();
unsafe {
libc::dup2(null_raw, libc::STDIN_FILENO);
libc::dup2(null_raw, libc::STDOUT_FILENO);
libc::dup2(null_raw, libc::STDERR_FILENO);
}
Ok(())
}
#[cfg(windows)]
pub fn daemonize() -> io::Result<()> {
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(unix)]
fn test_spawn_daemon_creates_command() {
let executable = Path::new("/usr/bin/test");
let workspace_id = "test-workspace";
let result = spawn_daemon(executable, workspace_id, &[]);
assert!(result.is_err());
}
}