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:
@@ -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)]
|
||||||
|
|||||||
Reference in New Issue
Block a user