use std::path::{Path, PathBuf};
use std::process::Command;
use super::{BootAutostartError, UnitPath};
const TASK_NAME: &str = "runpm-daemon";
fn cmd_quote(s: &str) -> String {
let escaped = s.replace('"', "\"\"");
format!("\"{escaped}\"")
}
pub fn render_unit(daemon_binary: &Path) -> String {
let bin = daemon_binary.to_string_lossy();
let tr = cmd_quote(&format!("{bin} start"));
format!(
"schtasks /Create /SC ONLOGON /TN {tn} /TR {tr} /RL HIGHEST /F",
tn = cmd_quote(TASK_NAME),
)
}
pub fn unit_path() -> PathBuf {
PathBuf::from(format!(r"\Task Scheduler\{TASK_NAME}"))
}
pub fn install(daemon_binary: &Path) -> Result<UnitPath, BootAutostartError> {
let bin = daemon_binary.to_string_lossy().into_owned();
let tr = format!("{bin} start");
let status = Command::new("schtasks")
.args([
"/Create", "/SC", "ONLOGON", "/TN", TASK_NAME, "/TR", &tr, "/RL", "HIGHEST", "/F",
])
.status()
.map_err(|e| BootAutostartError::InitSystem(format!("schtasks /Create failed: {e}")))?;
if !status.success() {
return Err(BootAutostartError::InitSystem(format!(
"schtasks /Create exited non-zero ({status})"
)));
}
Ok(UnitPath(unit_path()))
}
pub fn uninstall() -> Result<(), BootAutostartError> {
let status = Command::new("schtasks")
.args(["/Delete", "/TN", TASK_NAME, "/F"])
.status()
.map_err(|e| BootAutostartError::InitSystem(format!("schtasks /Delete failed: {e}")))?;
if !status.success() {
eprintln!("warning: schtasks /Delete returned non-zero ({status:?}) (already removed?)");
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cmd_quote_doubles_embedded_quotes() {
assert_eq!(
cmd_quote(r#"C:\path with "quotes"\runpm.exe"#),
"\"C:\\path with \"\"quotes\"\"\\runpm.exe\""
);
}
}