diff --git a/crates/uffs-update/src/doctor.rs b/crates/uffs-update/src/doctor.rs index 4b21bf2be..a6a0f7bca 100644 --- a/crates/uffs-update/src/doctor.rs +++ b/crates/uffs-update/src/doctor.rs @@ -382,7 +382,18 @@ fn count_stale_backups(dir: &Path) -> usize { fn check_services(snapshot: &plan::Snapshot, repair: bool, report: &mut Report) { let mut down = Vec::new(); for running in &snapshot.running { - if proc::is_alive(running.pid) { + // The broker runs as a LocalSystem service. A non-elevated uffs-update + // can't `OpenProcess` a SYSTEM-owned pid, so `proc::is_alive` false- + // negatives on it — which would flag a perfectly healthy broker as + // "down" and then trigger a doomed (also elevation-gated) restart. + // Its authoritative liveness is the serving pipe (what check_broker + // probes too), so use that for the broker instead of pid liveness. + let alive = if running.component == "broker" { + restore::broker_pipe_ready(DOCTOR_PIPE_PROBE_MS) + } else { + proc::is_alive(running.pid) + }; + if alive { report.add( Health::Ok, format!("Service up: {}", running.component), diff --git a/crates/uffs-update/src/restore.rs b/crates/uffs-update/src/restore.rs index 357045da9..d92162e8e 100644 --- a/crates/uffs-update/src/restore.rs +++ b/crates/uffs-update/src/restore.rs @@ -62,7 +62,15 @@ fn start_component(component: &str, running: &SnapRunning) -> bool { /// wait until the pipe is actually serving (R10, §19.13). Service-RUNNING /// is necessary but not sufficient: the daemon's warm-up hits /// `ERROR_PIPE_BUSY` if it connects before the broker's pipe is listening. +/// +/// Idempotent: if the broker is **already serving**, it's up — and a +/// non-elevated caller can neither need nor (via SCM) perform a start of a +/// `LocalSystem` service. Treat that as success so a redundant, elevation-gated +/// `uffs_winsvc::start` failure can't surface as a fault on a healthy broker. fn start_broker() -> bool { + if broker_pipe_ready(PIPE_READY_TIMEOUT_MS) { + return true; + } uffs_winsvc::start(SERVICE_NAME).is_ok() && broker_pipe_ready(PIPE_READY_TIMEOUT_MS) }