fix(update): wait for the old process, not for a fixed window

The relaunch after an update stopped working, and the regression is mine:
removing the PowerShell helper — which is what Defender was reading as a
dropper — also removed the wait it was doing. Nothing took over the job.

The numbers made it certain rather than unlucky. The instance being
replaced is allowed THIRTY seconds to shut down (armExitWatchdog forces
it out at that point) because it closes a remote logbook, a CAT session
and sometimes a backup. The new instance was patient with the
single-instance mutex for TWENTY. On any station where shutting down ran
past that, the new process gave up and exited in silence: no window after
an update, and the previous OpsLog still in the task manager. Exactly the
report.

Both relaunch paths now pass --wait-pid, and the new process waits on
that process's handle — a plain kernel wait, which ends the instant the
old one ends, however long or short that is, and looks nothing like a
script starting another program. The mutex retry stays as a backstop and
goes to forty-five seconds, so it is longer than the wait it exists for
rather than shorter.

And when the old process really has not gone, the message says that
instead of "OpsLog is already running" — after an update the operator did
not start a second copy, and what they need to know is which one to
close.

A test keeps the two spawn sites honest: a relaunch added without
--wait-pid is this bug again.
This commit is contained in:
2026-09-08 09:18:06 +02:00
parent 38da904f4b
commit e0b110392a
7 changed files with 162 additions and 10 deletions
+12 -4
View File
@@ -222,10 +222,18 @@ func (a *App) DownloadAndApplyUpdate(url string) error {
// not our intentions, and an operator updating to 0.27.14 had OpsLog removed
// under Trojan:Script/Wacatac.H!ml — the "Script/" being the PowerShell.
//
// The wait it existed for is not needed: --post-update already makes the new
// instance patient with the single-instance mutex (see acquireInstance), so it
// can start while this one is still shutting down and simply wait its turn.
cmd := exec.Command(exe, "--post-update")
// The wait it existed for still has to happen — it just happens on the other
// side now. The new instance is told OUR pid and waits for this process to
// end before taking the single-instance mutex.
//
// Waiting on the mutex alone was not enough, and that is the bug this line
// fixes: shutting down is allowed thirty seconds here (armExitWatchdog),
// because it closes a remote logbook, a CAT session and sometimes a backup,
// while the new instance was only patient for twenty. On a station where
// that ran long, the new process gave up and exited — leaving the old one
// still running and no new window, which is precisely what the PowerShell
// helper never did: it waited for the pid, however long it took.
cmd := exec.Command(exe, "--post-update", "--wait-pid", strconv.Itoa(os.Getpid()))
cmd.Dir = dir
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true, CreationFlags: 0x08000000} // CREATE_NO_WINDOW
if err := cmd.Start(); err != nil {