@@ -2,7 +2,7 @@ use std::{ |
| 2 | 2 | error::Error, |
| 3 | 3 | fmt, fs, |
| 4 | 4 | path::{Path, PathBuf}, |
| 5 | | - process::Command, |
| 5 | + process::{Command, Stdio}, |
| 6 | 6 | }; |
| 7 | 7 | |
| 8 | 8 | use directories::BaseDirs; |
@@ -79,9 +79,10 @@ impl CommandRunner for SystemCommandRunner { |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | 81 | fn status(&mut self, program: &str, args: &[String]) -> Result<bool, ServiceError> { |
| 82 | | - let status = |
| 83 | | - Command::new(program) |
| 82 | + let status = Command::new(program) |
| 84 | 83 | .args(args) |
| 84 | + .stdout(Stdio::null()) |
| 85 | + .stderr(Stdio::null()) |
| 85 | 86 | .status() |
| 86 | 87 | .map_err(|err| ServiceError::Command { |
| 87 | 88 | program: program.to_string(), |
@@ -179,17 +180,33 @@ impl ServiceInstaller for MacLaunchAgent { |
| 179 | 180 | })?; |
| 180 | 181 | } |
| 181 | 182 | write_file(&path, &mac_launch_agent_plist(config))?; |
| 182 | | - let _ = runner.run("launchctl", &["unload".to_string(), path_string(&path)]); |
| 183 | + let domain = mac_launchctl_gui_domain()?; |
| 184 | + let service_target = mac_launchctl_service_target(&domain); |
| 185 | + if mac_launch_agent_loaded(runner, &service_target)? { |
| 186 | + runner.run( |
| 187 | + "launchctl", |
| 188 | + &["bootout".to_string(), service_target.clone()], |
| 189 | + )?; |
| 190 | + } |
| 183 | 191 | runner.run( |
| 184 | 192 | "launchctl", |
| 185 | | - &["load".to_string(), "-w".to_string(), path_string(&path)], |
| 193 | + &["bootstrap".to_string(), domain.clone(), path_string(&path)], |
| 194 | + )?; |
| 195 | + runner.run("launchctl", &["enable".to_string(), service_target.clone()])?; |
| 196 | + runner.run( |
| 197 | + "launchctl", |
| 198 | + &["kickstart".to_string(), "-k".to_string(), service_target], |
| 186 | 199 | ) |
| 187 | 200 | } |
| 188 | 201 | |
| 189 | 202 | fn uninstall(&self, runner: &mut dyn CommandRunner) -> Result<(), ServiceError> { |
| 190 | 203 | let path = Self::plist_path()?; |
| 204 | + let domain = mac_launchctl_gui_domain()?; |
| 205 | + let service_target = mac_launchctl_service_target(&domain); |
| 206 | + if mac_launch_agent_loaded(runner, &service_target)? { |
| 207 | + runner.run("launchctl", &["bootout".to_string(), service_target])?; |
| 208 | + } |
| 191 | 209 | if path.exists() { |
| 192 | | - let _ = runner.run("launchctl", &["unload".to_string(), path_string(&path)]); |
| 193 | 210 | fs::remove_file(&path).map_err(|err| ServiceError::Write { |
| 194 | 211 | path: path.clone(), |
| 195 | 212 | reason: err.to_string(), |
@@ -203,10 +220,9 @@ impl ServiceInstaller for MacLaunchAgent { |
| 203 | 220 | if !path.exists() { |
| 204 | 221 | return Ok(ServiceStatus::NotInstalled); |
| 205 | 222 | } |
| 206 | | - if runner.status( |
| 207 | | - "launchctl", |
| 208 | | - &["list".to_string(), SERVICE_LABEL.to_string()], |
| 209 | | - )? { |
| 223 | + let domain = mac_launchctl_gui_domain()?; |
| 224 | + let service_target = mac_launchctl_service_target(&domain); |
| 225 | + if mac_launch_agent_loaded(runner, &service_target)? { |
| 210 | 226 | Ok(ServiceStatus::Installed) |
| 211 | 227 | } else { |
| 212 | 228 | Ok(ServiceStatus::NotInstalled) |
@@ -214,6 +230,42 @@ impl ServiceInstaller for MacLaunchAgent { |
| 214 | 230 | } |
| 215 | 231 | } |
| 216 | 232 | |
| 233 | +#[cfg(target_os = "macos")] |
| 234 | +fn mac_launchctl_gui_domain() -> Result<String, ServiceError> { |
| 235 | + let output = Command::new("id") |
| 236 | + .arg("-u") |
| 237 | + .output() |
| 238 | + .map_err(|err| ServiceError::Command { |
| 239 | + program: "id".to_string(), |
| 240 | + reason: err.to_string(), |
| 241 | + })?; |
| 242 | + if !output.status.success() { |
| 243 | + return Err(ServiceError::Command { |
| 244 | + program: "id".to_string(), |
| 245 | + reason: format!("exited with status {}", output.status), |
| 246 | + }); |
| 247 | + } |
| 248 | + |
| 249 | + let uid = String::from_utf8_lossy(&output.stdout).trim().to_string(); |
| 250 | + Ok(format!("gui/{uid}")) |
| 251 | +} |
| 252 | + |
| 253 | +#[cfg(target_os = "macos")] |
| 254 | +fn mac_launchctl_service_target(domain: &str) -> String { |
| 255 | + format!("{domain}/{SERVICE_LABEL}") |
| 256 | +} |
| 257 | + |
| 258 | +#[cfg(target_os = "macos")] |
| 259 | +fn mac_launch_agent_loaded( |
| 260 | + runner: &mut dyn CommandRunner, |
| 261 | + service_target: &str, |
| 262 | +) -> Result<bool, ServiceError> { |
| 263 | + runner.status( |
| 264 | + "launchctl", |
| 265 | + &["print".to_string(), service_target.to_string()], |
| 266 | + ) |
| 267 | +} |
| 268 | + |
| 217 | 269 | #[cfg(target_os = "linux")] |
| 218 | 270 | #[derive(Debug)] |
| 219 | 271 | struct LinuxSystemdUser; |