diff --git a/clientupdate/clientupdate.go b/clientupdate/clientupdate.go index d2b04e70e..e7c379592 100644 --- a/clientupdate/clientupdate.go +++ b/clientupdate/clientupdate.go @@ -158,6 +158,10 @@ func Update(args UpdateArgs) error { case haveExecutable("apk"): up.update = up.updateAlpineLike } + // If nothing matched, fall back to tarball updates. + if up.update == nil { + up.update = up.updateLinuxBinary + } case "darwin": switch { case !args.AppStore && !version.IsSandboxedMacOS(): @@ -300,6 +304,14 @@ func parseSynoinfo(path string) (string, error) { } func (up *updater) updateDebLike() error { + if err := requireRoot(); err != nil { + return err + } + if err := exec.Command("dpkg", "--status", "tailscale").Run(); err != nil && isExitError(err) { + // Tailscale was not installed via apt, update via tarball download + // instead. + return up.updateLinuxBinary() + } ver, err := requestedTailscaleVersion(up.Version, up.track) if err != nil { return err @@ -308,10 +320,6 @@ func (up *updater) updateDebLike() error { return nil } - if err := requireRoot(); err != nil { - return err - } - if updated, err := updateDebianAptSourcesList(up.track); err != nil { return err } else if updated { @@ -402,6 +410,11 @@ func updateDebianAptSourcesListBytes(was []byte, dstTrack string) (newContent [] } func (up *updater) updateArchLike() error { + if err := exec.Command("pacman", "--query", "tailscale").Run(); err != nil && isExitError(err) { + // Tailscale was not installed via pacman, update via tarball download + // instead. + return up.updateLinuxBinary() + } // Arch maintainer asked us not to implement "tailscale update" or // auto-updates on Arch-based distros: // https://github.com/tailscale/tailscale/issues/6995#issuecomment-1687080106 @@ -419,6 +432,11 @@ func (up *updater) updateFedoraLike(packageManager string) func() error { if err := requireRoot(); err != nil { return err } + if err := exec.Command(packageManager, "info", "--installed", "tailscale").Run(); err != nil && isExitError(err) { + // Tailscale was not installed via yum/dnf, update via tarball + // download instead. + return up.updateLinuxBinary() + } defer func() { if err != nil { err = fmt.Errorf(`%w; you can try updating using "%s upgrade tailscale"`, err, packageManager) @@ -497,6 +515,11 @@ func (up *updater) updateAlpineLike() (err error) { if err := requireRoot(); err != nil { return err } + if err := exec.Command("apk", "info", "--installed", "tailscale").Run(); err != nil && isExitError(err) { + // Tailscale was not installed via apk, update via tarball download + // instead. + return up.updateLinuxBinary() + } defer func() { if err != nil { @@ -764,6 +787,11 @@ func (up *updater) updateFreeBSD() (err error) { if err := requireRoot(); err != nil { return err } + if err := exec.Command("pkg", "query", "%n", "tailscale").Run(); err != nil && isExitError(err) { + // Tailscale was not installed via pkg and we don't pre-compile + // binaries for it. + return errors.New("Tailscale was not installed via pkg, binary updates on FreeBSD are not supported; please reinstall Tailscale using pkg or update manually") + } defer func() { if err != nil { @@ -793,6 +821,10 @@ func (up *updater) updateFreeBSD() (err error) { return nil } +func (up *updater) updateLinuxBinary() error { + return errors.New("Linux binary updates without a package manager are not supported yet") +} + func haveExecutable(name string) bool { path, err := exec.LookPath(name) return err == nil && path != "" @@ -867,3 +899,8 @@ func requireRoot() error { return errors.New("must be root") } } + +func isExitError(err error) bool { + var exitErr *exec.ExitError + return errors.As(err, &exitErr) +}