-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathutils_linux.go
More file actions
151 lines (130 loc) · 3.88 KB
/
utils_linux.go
File metadata and controls
151 lines (130 loc) · 3.88 KB
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
//go:build linux
// +build linux
package system
import (
"fmt"
"os"
"strings"
"sync"
"github.com/digitalocean/go-smbios/smbios"
)
type Feature int
const (
_ Feature = iota
// System manufacturer. Requires access to SMBIOS data via DMI (i.e. root privileges)
SystemManufacturer
// System product name. Requires access to SMBIOS data via DMI (i.e. root privileges)
SystemProductName
// System UUID. Makes sense for virtual machines. Requires access to SMBIOS data via DMI (i.e. root privileges)
SystemUUID
)
var (
readSMBIOSOnce sync.Once
smbiosReadingErr error
smbiosAttrValues = make(map[Feature]string)
// See SMBIOS specification https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.3.0.pdf
smbiosAttrTable = [...]smbiosAttribute{
// System 0x01
{0x01, 0x08, 0x04, SystemManufacturer, nil},
{0x01, 0x08, 0x05, SystemProductName, nil},
{0x01, 0x14, 0x08, SystemUUID, formatUUID},
}
)
func formatUUID(b []byte) (string, error) {
return fmt.Sprintf("%0x-%0x-%0x-%0x-%0x", b[:4], b[4:6], b[6:8], b[8:10], b[10:16]), nil
}
func getSMBIOSAttr(feat Feature) (string, error) {
readSMBIOSOnce.Do(func() {
smbiosReadingErr = readSMBIOSAttributes()
})
if smbiosReadingErr != nil {
return "", smbiosReadingErr
}
return smbiosAttrValues[feat], nil
}
func readSMBIOSAttributes() error {
smbiosAttrIndex := buildSMBIOSAttrIndex()
rc, _, err := smbios.Stream()
if err != nil {
return fmt.Errorf("unable to open SMBIOS info: %v", err)
}
defer rc.Close()
structures, err := smbios.NewDecoder(rc).Decode()
if err != nil {
return fmt.Errorf("unable to decode SMBIOS info: %v", err)
}
for _, s := range structures {
attrList := smbiosAttrIndex[int(s.Header.Type)]
for _, attr := range attrList {
val, err := attr.readValueString(s)
if err != nil {
return fmt.Errorf("unable to read SMBIOS attribute '%v' of structure type 0x%0x: %v",
attr.feature, s.Header.Type, err)
}
smbiosAttrValues[attr.feature] = val
}
}
return nil
}
func buildSMBIOSAttrIndex() map[int][]*smbiosAttribute {
res := make(map[int][]*smbiosAttribute)
for i := range smbiosAttrTable {
attr := &smbiosAttrTable[i]
res[attr.structType] = append(res[attr.structType], attr)
}
return res
}
type smbiosAttribute struct {
structType int
structMinLength int
offset int
feature Feature
format func(data []byte) (string, error)
}
func (attr *smbiosAttribute) readValueString(s *smbios.Structure) (string, error) {
if len(s.Formatted) < attr.structMinLength {
return "", nil
}
if attr.format != nil {
const headerSize = 4
return attr.format(s.Formatted[attr.offset-headerSize:])
}
return attr.getString(s)
}
func (attr *smbiosAttribute) getString(s *smbios.Structure) (string, error) {
const headerSize = 4
strNo := int(s.Formatted[attr.offset-headerSize])
strNo -= 1
if strNo < 0 || strNo >= len(s.Strings) {
return "", fmt.Errorf("invalid string no")
}
return s.Strings[strNo], nil
}
func getMachineID() (string, error) {
const (
// dbusPath is the default path for dbus machine id.
dbusPath = "/var/lib/dbus/machine-id"
// dbusPathEtc is the default path for dbus machine id located in /etc.
// Some systems (like Fedora 20) only know this path.
// Sometimes it's the other way round.
dbusPathEtc = "/etc/machine-id"
)
id, err := os.ReadFile(dbusPath)
if err != nil {
id, err = os.ReadFile(dbusPathEtc)
}
if err != nil {
return "", err
}
machineID := strings.TrimSpace(strings.Trim(string(id), "\n"))
// root privileges are required to access attributes, the process will be skipped in case of insufficient privileges
smbiosAttrs := [...]Feature{SystemUUID, SystemManufacturer, SystemProductName}
for _, attr := range smbiosAttrs {
attrVal, err := getSMBIOSAttr(attr)
if err != nil || strings.TrimSpace(attrVal) == "" {
continue
}
machineID = fmt.Sprintf("%s:%s", machineID, strings.ToLower(attrVal))
}
return machineID, nil
}