Skip to main content

ant_core/node/process/
spawn.rs

1use std::path::Path;
2use std::process::Stdio;
3
4use tokio::process::{Child, Command};
5
6use crate::error::{Error, Result};
7
8/// Spawn a node process as a child of the current process (the daemon).
9///
10/// stdout and stderr are redirected to the given log directory.
11pub async fn spawn_node(
12    binary_path: &Path,
13    args: &[String],
14    env_vars: &[(String, String)],
15    log_dir: &Path,
16) -> Result<Child> {
17    tokio::fs::create_dir_all(log_dir)
18        .await
19        .map_err(|e| Error::ProcessSpawn(format!("Failed to create log directory: {e}")))?;
20
21    let stdout_path = log_dir.join("stdout.log");
22    let stderr_path = log_dir.join("stderr.log");
23
24    let stdout_file = std::fs::OpenOptions::new()
25        .create(true)
26        .append(true)
27        .open(&stdout_path)
28        .map_err(|e| Error::ProcessSpawn(format!("Failed to open stdout log: {e}")))?;
29    let stderr_file = std::fs::OpenOptions::new()
30        .create(true)
31        .append(true)
32        .open(&stderr_path)
33        .map_err(|e| Error::ProcessSpawn(format!("Failed to open stderr log: {e}")))?;
34
35    let mut cmd = Command::new(binary_path);
36    cmd.args(args)
37        .envs(env_vars.iter().map(|(k, v)| (k.as_str(), v.as_str())))
38        .stdin(Stdio::null())
39        .stdout(stdout_file)
40        .stderr(stderr_file)
41        // Nodes intentionally survive daemon restarts. The daemon re-discovers
42        // running nodes on startup via the registry and PID checks.
43        .kill_on_drop(false);
44
45    #[cfg(windows)]
46    {
47        const CREATE_NO_WINDOW: u32 = 0x08000000;
48        cmd.creation_flags(CREATE_NO_WINDOW);
49    }
50
51    let child = cmd
52        .spawn()
53        .map_err(|e| Error::ProcessSpawn(format!("Failed to spawn node process: {e}")))?;
54
55    Ok(child)
56}