From 1fc6ea9b6c9ddef72e461172cbd686834a5ed6f2 Mon Sep 17 00:00:00 2001 From: MengMengCode Date: Sun, 9 Aug 2026 20:13:50 +0800 Subject: [PATCH] fix: make web updates restart cleanly --- cmd/vocat/main.go | 4 ++++ internal/update/update.go | 5 ++++- scripts/install.sh | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/cmd/vocat/main.go b/cmd/vocat/main.go index 21abe79..a39976c 100644 --- a/cmd/vocat/main.go +++ b/cmd/vocat/main.go @@ -248,6 +248,10 @@ func run(logger *slog.Logger, logs *loghub.Hub) error { case <-signalContext.Done(): logger.Info("shutdown signal received") } + // Long-lived SSE and polling handlers use this context. Stop them before + // http.Server.Shutdown so they do not consume the entire graceful-shutdown + // deadline while waiting for a stream that is intentionally still active. + cancelPolling() shutdownContext, cancelShutdown := context.WithTimeout( context.Background(), diff --git a/internal/update/update.go b/internal/update/update.go index d108653..3465545 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -232,7 +232,10 @@ func RestartService(logger *slog.Logger) error { if _, err := exec.LookPath("systemctl"); err != nil { return fmt.Errorf("systemctl not found in PATH") } - cmd := exec.Command("systemctl", "restart", "vocat") + // Queue the restart and let systemctl exit before systemd stops this unit. + // A blocking restart command becomes part of vocat.service's own cgroup and + // waits for that same cgroup to terminate, creating a stop-timeout cycle. + cmd := exec.Command("systemctl", "restart", "--no-block", "vocat") if out, err := cmd.CombinedOutput(); err != nil { logger.Warn("systemctl restart failed", "error", err, "output", string(out)) return fmt.Errorf("systemctl restart vocat: %w", err) diff --git a/scripts/install.sh b/scripts/install.sh index 075a0d1..31d49f5 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -216,7 +216,9 @@ ExecStart=${BINARY_PATH} Restart=on-failure RestartSec=3s TimeoutStartSec=30s -TimeoutStopSec=20s +# HTTP, VoWiFi, and modem cleanup have bounded shutdown contexts totalling up +# to 30 seconds. Leave a small margin before systemd resorts to SIGKILL. +TimeoutStopSec=40s AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW