From 776a05223b3a5c441042978ca76357f2b0d5bb21 Mon Sep 17 00:00:00 2001 From: Andrew Lytvynov Date: Thu, 30 May 2024 16:55:02 -0700 Subject: [PATCH] ipn/ipnlocal: support c2n updates with old systemd versions (#12296) The `--wait` flag for `systemd-run` was added in systemd 232. While it is quite old, it doesn't hurt to special-case them and skip the `--wait` flag. The consequence is that we lose the update command output in logs, but at least auto-updates will work. Fixes #12136 Signed-off-by: Andrew Lytvynov --- ipn/ipnlocal/c2n.go | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/ipn/ipnlocal/c2n.go b/ipn/ipnlocal/c2n.go index ec7dc8bc4..de6ed73b3 100644 --- a/ipn/ipnlocal/c2n.go +++ b/ipn/ipnlocal/c2n.go @@ -478,17 +478,44 @@ func findCmdTailscale() (string, error) { } func tailscaleUpdateCmd(cmdTS string) *exec.Cmd { + defaultCmd := exec.Command(cmdTS, "update", "--yes") if runtime.GOOS != "linux" { - return exec.Command(cmdTS, "update", "--yes") + return defaultCmd } if _, err := exec.LookPath("systemd-run"); err != nil { - return exec.Command(cmdTS, "update", "--yes") + return defaultCmd } + // When systemd-run is available, use it to run the update command. This // creates a new temporary unit separate from the tailscaled unit. When // tailscaled is restarted during the update, systemd won't kill this // temporary update unit, which could cause unexpected breakage. - return exec.Command("systemd-run", "--wait", "--pipe", "--collect", cmdTS, "update", "--yes") + // + // We want to use the --wait flag for systemd-run, to block the update + // command until completion and collect output. But this flag was added in + // systemd 232, so we need to check the version first. + // + // The output will look like: + // + // systemd 255 (255.7-1-arch) + // +PAM +AUDIT ... other feature flags ... + systemdVerOut, err := exec.Command("systemd-run", "--version").Output() + if err != nil { + return defaultCmd + } + parts := strings.Fields(string(systemdVerOut)) + if len(parts) < 2 || parts[0] != "systemd" { + return defaultCmd + } + systemdVer, err := strconv.Atoi(parts[1]) + if err != nil { + return defaultCmd + } + if systemdVer < 232 { + return exec.Command("systemd-run", "--pipe", "--collect", cmdTS, "update", "--yes") + } else { + return exec.Command("systemd-run", "--wait", "--pipe", "--collect", cmdTS, "update", "--yes") + } } func regularFileExists(path string) bool {