tailscale/version/distro/distro.go
Brad Fitzpatrick 1a45274d4c
version/distro: detect Ubuntu separately from Debian
(for an upcoming change where I wanted to easily distinguish these
two)

Change-Id: Icb6a0144275cc9bf8978a7cb96b601d516d8da46
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-18 10:28:00 -07:00

132 lines
2.9 KiB
Go

// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package distro reports which distro we're running on.
package distro
import (
"os"
"runtime"
"strconv"
"go4.org/mem"
"tailscale.com/syncs"
"tailscale.com/util/lineread"
)
type Distro string
const (
Debian = Distro("debian")
Ubuntu = Distro("ubuntu")
Arch = Distro("arch")
Synology = Distro("synology")
OpenWrt = Distro("openwrt")
NixOS = Distro("nixos")
QNAP = Distro("qnap")
Pfsense = Distro("pfsense")
OPNsense = Distro("opnsense")
TrueNAS = Distro("truenas")
Gokrazy = Distro("gokrazy")
WDMyCloud = Distro("wdmycloud")
)
var distroAtomic syncs.AtomicValue[Distro]
// Get returns the current distro, or the empty string if unknown.
func Get() Distro {
if d, ok := distroAtomic.LoadOk(); ok {
return d
}
var d Distro
switch runtime.GOOS {
case "linux":
d = linuxDistro()
case "freebsd":
d = freebsdDistro()
}
distroAtomic.Store(d) // even if empty
return d
}
func have(file string) bool {
_, err := os.Stat(file)
return err == nil
}
func haveDir(file string) bool {
fi, err := os.Stat(file)
return err == nil && fi.IsDir()
}
func linuxDistro() Distro {
switch {
case haveDir("/usr/syno"):
return Synology
case have("/usr/local/bin/freenas-debug"):
// TrueNAS Scale runs on debian so test for it before Debian.
return TrueNAS
case have("/etc/debian_version"):
// Ubuntu also has an /etc/debian_version file, so see if it's actually Ubuntu.
isUbuntu := false
lineread.File("/etc/os-release", func(line []byte) error {
if mem.HasPrefix(mem.B(line), mem.S("ID=ubuntu")) {
isUbuntu = true
}
return nil
})
if isUbuntu {
return Ubuntu
}
return Debian
case have("/etc/arch-release"):
return Arch
case have("/etc/openwrt_version"):
return OpenWrt
case have("/run/current-system/sw/bin/nixos-version"):
return NixOS
case have("/etc/config/uLinux.conf"):
return QNAP
case haveDir("/gokrazy"):
return Gokrazy
case have("/usr/local/wdmcserver/bin/wdmc.xml"): // Western Digital MyCloud OS3
return WDMyCloud
case have("/usr/sbin/wd_crontab.sh"): // Western Digital MyCloud OS5
return WDMyCloud
}
return ""
}
func freebsdDistro() Distro {
switch {
case have("/etc/pfSense-rc"):
return Pfsense
case have("/usr/local/sbin/opnsense-shell"):
return OPNsense
case have("/usr/local/bin/freenas-debug"):
// TrueNAS Core runs on FreeBSD
return TrueNAS
}
return ""
}
// DSMVersion reports the Synology DSM major version.
//
// If not Synology, it reports 0.
func DSMVersion() int {
if runtime.GOOS != "linux" {
return 0
}
if Get() != Synology {
return 0
}
v, _ := strconv.Atoi(os.Getenv("SYNOPKG_DSM_VERSION_MAJOR"))
return v
}
// LikeDebian reports whether d is Debian-derived.
func (d Distro) LikeDebian() bool {
return d == Debian || d == Ubuntu
}