diff --git a/src/setup.rs b/src/setup.rs index e6ab143..ad436ec 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -42,6 +42,17 @@ fn write_atomic(path: &std::path::Path, body: String) -> std::io::Result<()> { /// 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. +/// The launcher is started by systemd (`ExecStart=`) and by the terminal, so it must be executable. +fn make_executable(path: &std::path::Path) { + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let _ = std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o755)); + } + #[cfg(not(unix))] + let _ = path; +} + fn systemd_quote(p: &std::path::Path) -> String { let mut out = String::from("\""); for c in p.display().to_string().chars() { @@ -826,11 +837,7 @@ impl Plan { std::fs::create_dir_all(&bin).map_err(|e| format!("建立 {} 失敗:{e}", bin.display()))?; let launcher = bin.join("anybot-run"); write_atomic(&launcher, launcher_script(self.role, exe, &self.env)).map_err(|e| format!("寫入 {} 失敗:{e}", launcher.display()))?; - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - let _ = std::fs::set_permissions(&launcher, std::fs::Permissions::from_mode(0o755)); - } + make_executable(&launcher); let units = home.join(".config/systemd/user"); std::fs::create_dir_all(&units).map_err(|e| e.to_string())?; // An older setup (deploy/linux/hands.sh) used another service name. @@ -1004,6 +1011,9 @@ fn refresh_service() -> i32 { eprintln!("✗ 寫入 {} 失敗:{e}", launcher.display()); return 1; } + // systemd starts it directly: a launcher written without the execute bit fails the unit + // with status=203/EXEC and the service spins in a restart loop. + make_executable(&launcher); match systemctl(&["restart", UNIT]) { Ok(_) => { println!("已更新並重新啟動 AnyBot 服務。");