tenseleyflow/rcal / 4367c8f

Browse files

Modernize macOS reminder service control

Authored by espadonne
SHA
4367c8f2bd99b423088986553ce64514973aef22
Parents
fe2634a
Tree
393b544

1 changed file

StatusFile+-
M src/services.rs 68 16
src/services.rsmodified
@@ -2,7 +2,7 @@ use std::{
22
     error::Error,
33
     fmt, fs,
44
     path::{Path, PathBuf},
5
-    process::Command,
5
+    process::{Command, Stdio},
66
 };
77
 
88
 use directories::BaseDirs;
@@ -79,9 +79,10 @@ impl CommandRunner for SystemCommandRunner {
7979
     }
8080
 
8181
     fn status(&mut self, program: &str, args: &[String]) -> Result<bool, ServiceError> {
82
-        let status =
83
-            Command::new(program)
82
+        let status = Command::new(program)
8483
             .args(args)
84
+            .stdout(Stdio::null())
85
+            .stderr(Stdio::null())
8586
             .status()
8687
             .map_err(|err| ServiceError::Command {
8788
                 program: program.to_string(),
@@ -179,17 +180,33 @@ impl ServiceInstaller for MacLaunchAgent {
179180
             })?;
180181
         }
181182
         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
+        }
183191
         runner.run(
184192
             "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],
186199
         )
187200
     }
188201
 
189202
     fn uninstall(&self, runner: &mut dyn CommandRunner) -> Result<(), ServiceError> {
190203
         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
+        }
191209
         if path.exists() {
192
-            let _ = runner.run("launchctl", &["unload".to_string(), path_string(&path)]);
193210
             fs::remove_file(&path).map_err(|err| ServiceError::Write {
194211
                 path: path.clone(),
195212
                 reason: err.to_string(),
@@ -203,10 +220,9 @@ impl ServiceInstaller for MacLaunchAgent {
203220
         if !path.exists() {
204221
             return Ok(ServiceStatus::NotInstalled);
205222
         }
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)? {
210226
             Ok(ServiceStatus::Installed)
211227
         } else {
212228
             Ok(ServiceStatus::NotInstalled)
@@ -214,6 +230,42 @@ impl ServiceInstaller for MacLaunchAgent {
214230
     }
215231
 }
216232
 
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
+
217269
 #[cfg(target_os = "linux")]
218270
 #[derive(Debug)]
219271
 struct LinuxSystemdUser;