fix(windows): stop service before port probe, wait for full exit

Stop the running service before disabling Dnscache so the port 53 probe
sees the real state (not Numa's own binding). Wait for SCM STOPPED
state before copying the binary to avoid os error 32 (file in use).
This commit is contained in:
Razvan Dimescu
2026-04-16 19:21:56 +03:00
parent 9bea038cb6
commit 9f08d8b489

View File

@@ -679,15 +679,15 @@ fn install_windows() -> Result<(), String> {
std::fs::write(&path, json).map_err(|e| format!("failed to write backup: {}", e))?; std::fs::write(&path, json).map_err(|e| format!("failed to write backup: {}", e))?;
} }
let needs_reboot = disable_dnscache()?;
// On re-install, stop the running service first so the binary can be // On re-install, stop the running service first so the binary can be
// overwritten (SCM holds a handle to the exe while it's running). // overwritten and port 53 is released for the Dnscache probe.
let reinstall = is_service_registered(); if is_service_registered() {
if reinstall { eprintln!(" Stopping existing service...");
stop_service_scm(); stop_service_scm();
} }
let needs_reboot = disable_dnscache()?;
// Copy the binary to a stable path under ProgramData and register it // Copy the binary to a stable path under ProgramData and register it
// as a real Windows service (SCM-managed, boot-time, auto-restart). // as a real Windows service (SCM-managed, boot-time, auto-restart).
let service_exe = install_service_binary()?; let service_exe = install_service_binary()?;
@@ -880,15 +880,25 @@ fn start_service_scm() -> Result<(), String> {
Ok(()) Ok(())
} }
/// Stop the service. Idempotent — already-stopped or missing service logs /// Stop the service and wait for it to fully exit. Idempotent —
/// a warning but doesn't error, since both callers (install re-run, /// already-stopped or missing service is not an error.
/// uninstall) want best-effort cleanup rather than hard failure.
#[cfg(windows)] #[cfg(windows)]
fn stop_service_scm() { fn stop_service_scm() {
if let Err(e) = run_sc(&["stop", crate::windows_service::SERVICE_NAME]) { let name = crate::windows_service::SERVICE_NAME;
log::warn!("sc stop failed: {}", e); let _ = run_sc(&["stop", name]);
// Wait up to 10s for the service to reach STOPPED state so the
// binary file handle is released before we try to overwrite it.
for _ in 0..20 {
if let Ok(out) = run_sc(&["query", name]) {
let text = String::from_utf8_lossy(&out.stdout);
if text.contains("STOPPED") || text.contains("1060") {
return;
} }
} }
std::thread::sleep(std::time::Duration::from_millis(500));
}
eprintln!(" warning: service did not stop within 10s");
}
/// Remove the service from SCM. Idempotent — see `stop_service_scm`. /// Remove the service from SCM. Idempotent — see `stop_service_scm`.
#[cfg(windows)] #[cfg(windows)]