fix: macOS use launchctl bootout/bootstrap instead of deprecated load #42
@@ -1080,14 +1080,23 @@ fn install_service_macos() -> Result<(), String> {
|
|||||||
std::fs::write(PLIST_DEST, plist)
|
std::fs::write(PLIST_DEST, plist)
|
||||||
.map_err(|e| format!("failed to write {}: {}", PLIST_DEST, e))?;
|
.map_err(|e| format!("failed to write {}: {}", PLIST_DEST, e))?;
|
||||||
|
|
||||||
// Load the service first so numa is listening before DNS redirect
|
// Modern launchctl API: explicitly tear down any existing in-memory
|
||||||
|
// state, then bootstrap fresh from the on-disk plist. The deprecated
|
||||||
|
// `load -w` returns exit 0 even when it cannot actually reload (label
|
||||||
|
// already in launchd state), silently leaving the daemon running a
|
||||||
|
// stale binary path after `numa install` rewrites the plist on disk —
|
||||||
|
// which is exactly what `brew upgrade numa` does.
|
||||||
|
let _ = std::process::Command::new("launchctl")
|
||||||
|
.args(["bootout", "system", PLIST_DEST])
|
||||||
|
.status();
|
||||||
|
|
||||||
let status = std::process::Command::new("launchctl")
|
let status = std::process::Command::new("launchctl")
|
||||||
.args(["load", "-w", PLIST_DEST])
|
.args(["bootstrap", "system", PLIST_DEST])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|e| format!("failed to run launchctl: {}", e))?;
|
.map_err(|e| format!("failed to run launchctl: {}", e))?;
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
return Err("launchctl load failed".to_string());
|
return Err("launchctl bootstrap failed".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for numa to be ready before redirecting DNS
|
// Wait for numa to be ready before redirecting DNS
|
||||||
@@ -1100,7 +1109,7 @@ fn install_service_macos() -> Result<(), String> {
|
|||||||
if !api_up {
|
if !api_up {
|
||||||
// Service failed to start — don't redirect DNS to a dead endpoint
|
// Service failed to start — don't redirect DNS to a dead endpoint
|
||||||
let _ = std::process::Command::new("launchctl")
|
let _ = std::process::Command::new("launchctl")
|
||||||
.args(["unload", PLIST_DEST])
|
.args(["bootout", "system", PLIST_DEST])
|
||||||
.status();
|
.status();
|
||||||
return Err(
|
return Err(
|
||||||
"numa service did not start (port 53 may be in use). Service unloaded.".to_string(),
|
"numa service did not start (port 53 may be in use). Service unloaded.".to_string(),
|
||||||
@@ -1128,22 +1137,25 @@ fn uninstall_service_macos() -> Result<(), String> {
|
|||||||
eprintln!(" warning: failed to restore system DNS: {}", e);
|
eprintln!(" warning: failed to restore system DNS: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove plist first so service won't restart on boot even if unload fails
|
// Bootout the service from launchd's in-memory state BEFORE removing
|
||||||
if let Err(e) = std::fs::remove_file(PLIST_DEST) {
|
// the plist. The modern API needs the file path as the specifier;
|
||||||
if e.kind() != std::io::ErrorKind::NotFound {
|
// doing this in the wrong order would leave the service loaded in
|
||||||
return Err(format!("failed to remove {}: {}", PLIST_DEST, e));
|
// memory until reboot. (Deprecated `unload -w` had the same issue.)
|
||||||
|
let bootout_status = std::process::Command::new("launchctl")
|
||||||
|
.args(["bootout", "system", PLIST_DEST])
|
||||||
|
.status();
|
||||||
|
if let Ok(s) = bootout_status {
|
||||||
|
if !s.success() {
|
||||||
|
eprintln!(
|
||||||
|
" warning: launchctl bootout returned non-zero (service may not have been loaded)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unload the service
|
// Remove plist so the service won't restart on boot
|
||||||
let status = std::process::Command::new("launchctl")
|
if let Err(e) = std::fs::remove_file(PLIST_DEST) {
|
||||||
.args(["unload", "-w", PLIST_DEST])
|
if e.kind() != std::io::ErrorKind::NotFound {
|
||||||
.status();
|
return Err(format!("failed to remove {}: {}", PLIST_DEST, e));
|
||||||
if let Ok(s) = status {
|
|
||||||
if !s.success() {
|
|
||||||
eprintln!(
|
|
||||||
" warning: launchctl unload returned non-zero (service may still be running)"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user