Setup quotes service paths and writes launcher and unit files atomically
This commit is contained in:
parent
7391e3a101
commit
dabb4113c2
35
src/setup.rs
35
src/setup.rs
|
|
@ -30,6 +30,33 @@ const DIM: Color = Color::DarkGrey;
|
||||||
const SPIN: [&str; 10] = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
const SPIN: [&str; 10] = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
||||||
const UNIT: &str = "anybot.service";
|
const UNIT: &str = "anybot.service";
|
||||||
|
|
||||||
|
/// Write a file all at once, as far as the filesystem allows: tmp file + rename. A Ctrl+C in
|
||||||
|
/// the middle of installing used to leave a half-written launcher/unit behind, and systemd
|
||||||
|
/// then ran (or refused to run) a broken service with no easy way to see why.
|
||||||
|
fn write_atomic(path: &std::path::Path, body: String) -> std::io::Result<()> {
|
||||||
|
let tmp = path.with_extension("anybot-tmp");
|
||||||
|
std::fs::write(&tmp, body)?;
|
||||||
|
std::fs::rename(&tmp, path)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Quote a path for a systemd ExecStart line: a home folder with spaces (or a quote sign)
|
||||||
|
/// made the unit fail to start with no way to see why.
|
||||||
|
fn systemd_quote(p: &std::path::Path) -> String {
|
||||||
|
let mut out = String::from("\"");
|
||||||
|
for c in p.display().to_string().chars() {
|
||||||
|
match c {
|
||||||
|
'"' | '$' | '\\' | '`' => {
|
||||||
|
out.push('\\');
|
||||||
|
out.push(c);
|
||||||
|
}
|
||||||
|
c => out.push(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.push('"');
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
enum Role {
|
enum Role {
|
||||||
Hands,
|
Hands,
|
||||||
|
|
@ -766,7 +793,7 @@ impl Plan {
|
||||||
let bin = home.join(".local/bin");
|
let bin = home.join(".local/bin");
|
||||||
std::fs::create_dir_all(&bin).map_err(|e| format!("建立 {} 失敗:{e}", bin.display()))?;
|
std::fs::create_dir_all(&bin).map_err(|e| format!("建立 {} 失敗:{e}", bin.display()))?;
|
||||||
let launcher = bin.join("anybot-run");
|
let launcher = bin.join("anybot-run");
|
||||||
std::fs::write(&launcher, launcher_script(self.role, &exe, &self.env)).map_err(|e| format!("寫入 {} 失敗:{e}", launcher.display()))?;
|
write_atomic(&launcher, launcher_script(self.role, &exe, &self.env)).map_err(|e| format!("寫入 {} 失敗:{e}", launcher.display()))?;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
@ -785,9 +812,9 @@ impl Plan {
|
||||||
};
|
};
|
||||||
let unit = format!(
|
let unit = format!(
|
||||||
"[Unit]\nDescription=AnyBot({what})\nAfter=network-online.target graphical-session.target\nWants=network-online.target\n\n[Service]\nExecStart={}\nRestart=always\nRestartSec=5\n\n[Install]\nWantedBy=default.target\n",
|
"[Unit]\nDescription=AnyBot({what})\nAfter=network-online.target graphical-session.target\nWants=network-online.target\n\n[Service]\nExecStart={}\nRestart=always\nRestartSec=5\n\n[Install]\nWantedBy=default.target\n",
|
||||||
launcher.display()
|
systemd_quote(&launcher)
|
||||||
);
|
);
|
||||||
std::fs::write(units.join(UNIT), unit).map_err(|e| e.to_string())?;
|
write_atomic(&units.join(UNIT), unit).map_err(|e| e.to_string())?;
|
||||||
systemctl(&["daemon-reload"])?;
|
systemctl(&["daemon-reload"])?;
|
||||||
systemctl(&["enable", UNIT])?;
|
systemctl(&["enable", UNIT])?;
|
||||||
systemctl(&["restart", UNIT])?;
|
systemctl(&["restart", UNIT])?;
|
||||||
|
|
@ -815,7 +842,7 @@ fn refresh_service() -> i32 {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Err(e) = std::fs::write(&launcher, launcher_script(role, &exe, &env)) {
|
if let Err(e) = write_atomic(&launcher, launcher_script(role, &exe, &env)) {
|
||||||
eprintln!("✗ 寫入 {} 失敗:{e}", launcher.display());
|
eprintln!("✗ 寫入 {} 失敗:{e}", launcher.display());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue