/
hostinfo_linux.go
170 lines (151 loc) · 4.13 KB
/
hostinfo_linux.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux && !android
package hostinfo
import (
"bytes"
"os"
"strings"
"golang.org/x/sys/unix"
"tailscale.com/types/ptr"
"tailscale.com/util/lineread"
"tailscale.com/version/distro"
)
func init() {
osVersion = lazyOSVersion.Get
packageType = packageTypeLinux
distroName = distroNameLinux
distroVersion = distroVersionLinux
distroCodeName = distroCodeNameLinux
deviceModel = deviceModelLinux
}
var (
lazyVersionMeta = &lazyAtomicValue[versionMeta]{f: ptr.To(linuxVersionMeta)}
lazyOSVersion = &lazyAtomicValue[string]{f: ptr.To(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 deviceModelLinux() string {
for _, path := range []string{
// First try the Synology-specific location.
// Example: "DS916+-j"
"/proc/sys/kernel/syno_hw_version",
// Otherwise, try the Devicetree model, usually set on
// ARM SBCs, etc.
// Example: "Raspberry Pi 4 Model B Rev 1.2"
// Example: "WD My Cloud Gen2: Marvell Armada 375"
"/sys/firmware/devicetree/base/model", // Raspberry Pi 4 Model B Rev 1.2"
} {
b, _ := os.ReadFile(path)
if s := strings.Trim(string(b), "\x00\r\n\t "); s != "" {
return s
}
}
return ""
}
func getQnapQtsVersion(versionInfo string) string {
for _, field := range strings.Fields(versionInfo) {
if suffix, ok := strings.CutPrefix(field, "QTSFW_"); ok {
return suffix
}
}
return ""
}
func osVersionLinux() string {
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:
propFile = "/etc.defaults/VERSION"
case distro.OpenWrt:
propFile = "/etc/openwrt_release"
case distro.Unraid:
propFile = "/etc/unraid-version"
case distro.WDMyCloud:
slurp, _ := os.ReadFile("/etc/version")
meta.DistroVersion = string(bytes.TrimSpace(slurp))
return
case distro.QNAP:
slurp, _ := os.ReadFile("/etc/version_info")
meta.DistroVersion = getQnapQtsVersion(string(slurp))
return
}
m := map[string]string{}
lineread.File(propFile, func(line []byte) error {
eq := bytes.IndexByte(line, '=')
if eq == -1 {
return nil
}
k, v := string(line[:eq]), strings.Trim(string(line[eq+1:]), `"'`)
m[k] = v
return nil
})
if v := m["VERSION_CODENAME"]; v != "" {
meta.DistroCodeName = v
}
if v := m["VERSION_ID"]; v != "" {
meta.DistroVersion = v
}
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, _ := os.ReadFile("/etc/debian_version")
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 meta.DistroVersion == "" {
if cr, _ := os.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:
meta.DistroVersion = m["productversion"]
case distro.OpenWrt:
meta.DistroVersion = m["DISTRIB_RELEASE"]
case distro.Unraid:
meta.DistroVersion = m["version"]
}
return
}
func packageTypeLinux() string {
// Report whether this is in a snap.
// See https://snapcraft.io/docs/environment-variables
// We just look at two somewhat arbitrarily.
if os.Getenv("SNAP_NAME") != "" && os.Getenv("SNAP") != "" {
return "snap"
}
return ""
}