bastion 0.53.2
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
//go:build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// relaunchAfterUpdate restarts this bastion after a self-update swapped its
|
||||
// binary — because on Windows nothing else will.
|
||||
//
|
||||
// update.go ends a successful swap with os.Exit(0), and its comment says why
|
||||
// that is enough: "systemd Restart=always re-execs the path, now the new
|
||||
// binary". True on Linux. On Windows there IS no supervisor — deploy/install.ps1
|
||||
// installs the exe, puts it on PATH, and registers nothing with the Service
|
||||
// Control Manager. So every Windows self-update has been a clean, silent
|
||||
// shutdown: the update applies, the process exits, and the machine stops
|
||||
// heartbeating until someone notices and starts it by hand.
|
||||
//
|
||||
// Measured on a real desktop updating 0.23.0 -> 0.23.1: the heartbeat
|
||||
// stopped at the moment of the swap and the box went dark. It had just
|
||||
// been anchored, and because the daemon never came back the anchor never reached
|
||||
// the host either — the machine looked simply unanchored, with nothing anywhere
|
||||
// saying a process had exited. That is the worst part: the failure has no
|
||||
// symptom except absence.
|
||||
//
|
||||
// So on Windows we start the new binary ourselves before exiting. DETACHED_PROCESS
|
||||
// plus CREATE_NEW_PROCESS_GROUP so the child does not die with this console, and
|
||||
// it is deliberately NOT inheriting our handles — a child holding the parent's
|
||||
// stdio keeps the invoking terminal from ever returning.
|
||||
//
|
||||
// Best-effort, and it must stay that way: if the relaunch cannot start, the
|
||||
// update itself already succeeded and the binary on disk is the new one. Report
|
||||
// it and exit rather than pretending, so the operator knows to start it.
|
||||
//
|
||||
// NOT AS A SERVICE. A networked install runs as a Windows service
|
||||
// (sysservice_windows.go), and there the Service Control Manager is the
|
||||
// supervisor: the exit that follows ends the process without reporting a stop,
|
||||
// which the manager counts as a failure and answers with a restart of the new
|
||||
// binary. A detached copy started here would be a second bastion outside the
|
||||
// manager, racing the one it is about to start.
|
||||
func relaunchAfterUpdate() {
|
||||
if runningAsService {
|
||||
return
|
||||
}
|
||||
self, err := os.Executable()
|
||||
if err != nil {
|
||||
fmt.Printf("self-update: cannot find my own path to restart (%v) — start it again with `privatae-bastion run`\n", err)
|
||||
return
|
||||
}
|
||||
cmd := exec.Command(self, os.Args[1:]...)
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP,
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
fmt.Printf("self-update: could not restart automatically (%v) — the new binary is installed, start it with `privatae-bastion run`\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("self-update: restarted as pid %d\n", cmd.Process.Pid)
|
||||
// Do NOT Wait: the child is meant to outlive this process.
|
||||
_ = cmd.Process.Release()
|
||||
}
|
||||
Reference in New Issue
Block a user