use std::path::Path;
use std::process::Stdio;
use anyhow::{Context, Result};
use tokio::fs::OpenOptions;
use tokio::process::{Child, Command};
fn respawn_args(verb: &[&str], addr: &str, config_path: Option<&Path>) -> Result<Vec<String>> {
let mut args: Vec<String> = verb.iter().map(|s| (*s).to_owned()).collect();
args.push("--device".to_owned());
args.push(addr.to_owned());
if let Some(p) = config_path {
args.push("--config".to_owned());
args.push(p.to_str().context("config path is not valid UTF-8")?.to_owned());
}
Ok(args)
}
async fn open_log(log_path: &Path) -> Result<std::fs::File> {
if let Some(parent) = log_path.parent() {
tokio::fs::create_dir_all(parent).await.context("creating log directory")?;
}
let mut open_opts = OpenOptions::new();
open_opts.create(true).write(true).truncate(true);
#[cfg(unix)]
open_opts.mode(0o600);
Ok(open_opts
.open(log_path)
.await
.with_context(|| format!("opening log file: {}", log_path.display()))?
.into_std()
.await)
}
pub async fn respawn_self(
verb: &[&str],
addr: &str,
config_path: Option<&Path>,
log_path: &Path,
capture_stdout: bool,
detach: bool,
) -> Result<Child> {
let args = respawn_args(verb, addr, config_path)?;
let log_file = open_log(log_path).await?;
let exe = std::env::current_exe().context("resolving current executable path")?;
let mut cmd = Command::new(exe);
cmd.args(args);
cmd.stdin(Stdio::null());
cmd.stdout(if capture_stdout {
Stdio::from(log_file.try_clone().context("duplicating log file handle")?)
} else {
Stdio::null()
});
cmd.stderr(Stdio::from(log_file));
#[cfg(unix)]
if detach {
cmd.process_group(0);
}
cmd.spawn().context("spawning detached subprocess")
}
#[cfg(test)]
mod tests;