hostinfo, tailcfg: split Hostinfo.OSVersion into separate fields

Stop jamming everything into one string.

Fixes #5578

Change-Id: I7dec8d6c073bddc7dc5f653e3baf2b4bf6b68378
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2022-09-11 10:57:18 -07:00
committed by Brad Fitzpatrick
parent 708b7bff3d
commit d5e7e3093d
9 changed files with 249 additions and 118 deletions

View File

@@ -9,7 +9,6 @@ package hostinfo
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"strings"
@@ -21,14 +20,39 @@ import (
)
func init() {
osVersion = osVersionLinux
osVersion = lazyOSVersion.Get
packageType = packageTypeLinux
distroName = distroNameLinux
distroVersion = distroVersionLinux
distroCodeName = distroCodeNameLinux
if v := linuxDeviceModel(); v != "" {
SetDeviceModel(v)
}
}
var (
lazyVersionMeta = &lazyAtomicValue[versionMeta]{f: ptrTo(linuxVersionMeta)}
lazyOSVersion = &lazyAtomicValue[string]{f: ptrTo(osVersionLinux)}
)
type versionMeta struct {
DistroName string
DistroVersion string
DistroCodeName string // "jammy", etc (VERSION_CODENAME from /etc/os-release)
}
func distroNameLinux() string {
return lazyVersionMeta.Get().DistroName
}
func distroVersionLinux() string {
return lazyVersionMeta.Get().DistroVersion
}
func distroCodeNameLinux() string {
return lazyVersionMeta.Get().DistroCodeName
}
func linuxDeviceModel() string {
for _, path := range []string{
// First try the Synology-specific location.
@@ -52,15 +76,22 @@ func linuxDeviceModel() string {
func getQnapQtsVersion(versionInfo string) string {
for _, field := range strings.Fields(versionInfo) {
if suffix, ok := strs.CutPrefix(field, "QTSFW_"); ok {
return "QTS " + suffix
return suffix
}
}
return ""
}
func osVersionLinux() string {
// TODO(bradfitz,dgentry): cache this, or make caller(s) cache it.
var un unix.Utsname
unix.Uname(&un)
return unix.ByteSliceToString(un.Release[:])
}
func linuxVersionMeta() (meta versionMeta) {
dist := distro.Get()
meta.DistroName = string(dist)
propFile := "/etc/os-release"
switch dist {
case distro.Synology:
@@ -69,10 +100,12 @@ func osVersionLinux() string {
propFile = "/etc/openwrt_release"
case distro.WDMyCloud:
slurp, _ := ioutil.ReadFile("/etc/version")
return fmt.Sprintf("%s", string(bytes.TrimSpace(slurp)))
meta.DistroVersion = string(bytes.TrimSpace(slurp))
return
case distro.QNAP:
slurp, _ := ioutil.ReadFile("/etc/version_info")
return getQnapQtsVersion(string(slurp))
meta.DistroVersion = getQnapQtsVersion(string(slurp))
return
}
m := map[string]string{}
@@ -86,50 +119,45 @@ func osVersionLinux() string {
return nil
})
var un unix.Utsname
unix.Uname(&un)
var attrBuf strings.Builder
attrBuf.WriteString("; kernel=")
attrBuf.WriteString(unix.ByteSliceToString(un.Release[:]))
if inContainer() {
attrBuf.WriteString("; container")
if v := m["VERSION_CODENAME"]; v != "" {
meta.DistroCodeName = v
}
if env := GetEnvType(); env != "" {
fmt.Fprintf(&attrBuf, "; env=%s", env)
if v := m["VERSION_ID"]; v != "" {
meta.DistroVersion = v
}
attr := attrBuf.String()
id := m["ID"]
if id != "" {
meta.DistroName = id
}
switch id {
case "debian":
// Debian's VERSION_ID is just like "11". But /etc/debian_version has "11.5" normally.
// Or "bookworm/sid" on sid/testing.
slurp, _ := ioutil.ReadFile("/etc/debian_version")
return fmt.Sprintf("Debian %s (%s)%s", bytes.TrimSpace(slurp), m["VERSION_CODENAME"], attr)
case "ubuntu":
return fmt.Sprintf("Ubuntu %s%s", m["VERSION"], attr)
if v := string(bytes.TrimSpace(slurp)); v != "" {
if '0' <= v[0] && v[0] <= '9' {
meta.DistroVersion = v
} else if meta.DistroCodeName == "" {
meta.DistroCodeName = v
}
}
case "", "centos": // CentOS 6 has no /etc/os-release, so its id is ""
if cr, _ := ioutil.ReadFile("/etc/centos-release"); len(cr) > 0 { // "CentOS release 6.10 (Final)
return fmt.Sprintf("%s%s", bytes.TrimSpace(cr), attr)
}
fallthrough
case "fedora", "rhel", "alpine", "nixos":
// Their PRETTY_NAME is fine as-is for all versions I tested.
fallthrough
default:
if v := m["PRETTY_NAME"]; v != "" {
return fmt.Sprintf("%s%s", v, attr)
if meta.DistroVersion == "" {
if cr, _ := ioutil.ReadFile("/etc/centos-release"); len(cr) > 0 { // "CentOS release 6.10 (Final)
meta.DistroVersion = string(bytes.TrimSpace(cr))
}
}
}
if v := m["PRETTY_NAME"]; v != "" && meta.DistroVersion == "" && !strings.HasSuffix(v, "/sid") {
meta.DistroVersion = v
}
switch dist {
case distro.Synology:
return fmt.Sprintf("Synology %s%s", m["productversion"], attr)
meta.DistroVersion = m["productversion"]
case distro.OpenWrt:
return fmt.Sprintf("OpenWrt %s%s", m["DISTRIB_RELEASE"], attr)
case distro.Gokrazy:
return fmt.Sprintf("Gokrazy%s", attr)
meta.DistroVersion = m["DISTRIB_RELEASE"]
}
return fmt.Sprintf("Other%s", attr)
return
}
func packageTypeLinux() string {