1
2
3 package host
4
5 import (
6 "bytes"
7 "context"
8 "encoding/binary"
9 "fmt"
10 "io/ioutil"
11 "os"
12 "os/exec"
13 "path/filepath"
14 "regexp"
15 "strconv"
16 "strings"
17
18 "github.com/shirou/gopsutil/internal/common"
19 "golang.org/x/sys/unix"
20 )
21
22 type LSB struct {
23 ID string
24 Release string
25 Codename string
26 Description string
27 }
28
29
30 const USER_PROCESS = 7
31
32 func HostIDWithContext(ctx context.Context) (string, error) {
33 sysProductUUID := common.HostSys("class/dmi/id/product_uuid")
34 machineID := common.HostEtc("machine-id")
35 procSysKernelRandomBootID := common.HostProc("sys/kernel/random/boot_id")
36 switch {
37
38
39 case common.PathExists(sysProductUUID):
40 lines, err := common.ReadLines(sysProductUUID)
41 if err == nil && len(lines) > 0 && lines[0] != "" {
42 return strings.ToLower(lines[0]), nil
43 }
44 fallthrough
45
46 case common.PathExists(machineID):
47 lines, err := common.ReadLines(machineID)
48 if err == nil && len(lines) > 0 && len(lines[0]) == 32 {
49 st := lines[0]
50 return fmt.Sprintf("%s-%s-%s-%s-%s", st[0:8], st[8:12], st[12:16], st[16:20], st[20:32]), nil
51 }
52 fallthrough
53
54 default:
55 lines, err := common.ReadLines(procSysKernelRandomBootID)
56 if err == nil && len(lines) > 0 && lines[0] != "" {
57 return strings.ToLower(lines[0]), nil
58 }
59 }
60
61 return "", nil
62 }
63
64 func numProcs(ctx context.Context) (uint64, error) {
65 return common.NumProcs()
66 }
67
68 func BootTimeWithContext(ctx context.Context) (uint64, error) {
69 return common.BootTimeWithContext(ctx)
70 }
71
72 func UptimeWithContext(ctx context.Context) (uint64, error) {
73 sysinfo := &unix.Sysinfo_t{}
74 if err := unix.Sysinfo(sysinfo); err != nil {
75 return 0, err
76 }
77 return uint64(sysinfo.Uptime), nil
78 }
79
80 func UsersWithContext(ctx context.Context) ([]UserStat, error) {
81 utmpfile := common.HostVar("run/utmp")
82
83 file, err := os.Open(utmpfile)
84 if err != nil {
85 return nil, err
86 }
87 defer file.Close()
88
89 buf, err := ioutil.ReadAll(file)
90 if err != nil {
91 return nil, err
92 }
93
94 count := len(buf) / sizeOfUtmp
95
96 ret := make([]UserStat, 0, count)
97
98 for i := 0; i < count; i++ {
99 b := buf[i*sizeOfUtmp : (i+1)*sizeOfUtmp]
100
101 var u utmp
102 br := bytes.NewReader(b)
103 err := binary.Read(br, binary.LittleEndian, &u)
104 if err != nil {
105 continue
106 }
107 if u.Type != USER_PROCESS {
108 continue
109 }
110 user := UserStat{
111 User: common.IntToString(u.User[:]),
112 Terminal: common.IntToString(u.Line[:]),
113 Host: common.IntToString(u.Host[:]),
114 Started: int(u.Tv.Sec),
115 }
116 ret = append(ret, user)
117 }
118
119 return ret, nil
120
121 }
122
123 func getLSB() (*LSB, error) {
124 ret := &LSB{}
125 if common.PathExists(common.HostEtc("lsb-release")) {
126 contents, err := common.ReadLines(common.HostEtc("lsb-release"))
127 if err != nil {
128 return ret, err
129 }
130 for _, line := range contents {
131 field := strings.Split(line, "=")
132 if len(field) < 2 {
133 continue
134 }
135 switch field[0] {
136 case "DISTRIB_ID":
137 ret.ID = field[1]
138 case "DISTRIB_RELEASE":
139 ret.Release = field[1]
140 case "DISTRIB_CODENAME":
141 ret.Codename = field[1]
142 case "DISTRIB_DESCRIPTION":
143 ret.Description = field[1]
144 }
145 }
146 } else if common.PathExists("/usr/bin/lsb_release") {
147 lsb_release, err := exec.LookPath("lsb_release")
148 if err != nil {
149 return ret, err
150 }
151 out, err := invoke.Command(lsb_release)
152 if err != nil {
153 return ret, err
154 }
155 for _, line := range strings.Split(string(out), "\n") {
156 field := strings.Split(line, ":")
157 if len(field) < 2 {
158 continue
159 }
160 switch field[0] {
161 case "Distributor ID":
162 ret.ID = field[1]
163 case "Release":
164 ret.Release = field[1]
165 case "Codename":
166 ret.Codename = field[1]
167 case "Description":
168 ret.Description = field[1]
169 }
170 }
171
172 }
173
174 return ret, nil
175 }
176
177 func PlatformInformationWithContext(ctx context.Context) (platform string, family string, version string, err error) {
178 lsb, err := getLSB()
179 if err != nil {
180 lsb = &LSB{}
181 }
182
183 if common.PathExists(common.HostEtc("oracle-release")) {
184 platform = "oracle"
185 contents, err := common.ReadLines(common.HostEtc("oracle-release"))
186 if err == nil {
187 version = getRedhatishVersion(contents)
188 }
189
190 } else if common.PathExists(common.HostEtc("enterprise-release")) {
191 platform = "oracle"
192 contents, err := common.ReadLines(common.HostEtc("enterprise-release"))
193 if err == nil {
194 version = getRedhatishVersion(contents)
195 }
196 } else if common.PathExists(common.HostEtc("slackware-version")) {
197 platform = "slackware"
198 contents, err := common.ReadLines(common.HostEtc("slackware-version"))
199 if err == nil {
200 version = getSlackwareVersion(contents)
201 }
202 } else if common.PathExists(common.HostEtc("debian_version")) {
203 if lsb.ID == "Ubuntu" {
204 platform = "ubuntu"
205 version = lsb.Release
206 } else if lsb.ID == "LinuxMint" {
207 platform = "linuxmint"
208 version = lsb.Release
209 } else {
210 if common.PathExists("/usr/bin/raspi-config") {
211 platform = "raspbian"
212 } else {
213 platform = "debian"
214 }
215 contents, err := common.ReadLines(common.HostEtc("debian_version"))
216 if err == nil && len(contents) > 0 && contents[0] != "" {
217 version = contents[0]
218 }
219 }
220 } else if common.PathExists(common.HostEtc("redhat-release")) {
221 contents, err := common.ReadLines(common.HostEtc("redhat-release"))
222 if err == nil {
223 version = getRedhatishVersion(contents)
224 platform = getRedhatishPlatform(contents)
225 }
226 } else if common.PathExists(common.HostEtc("system-release")) {
227 contents, err := common.ReadLines(common.HostEtc("system-release"))
228 if err == nil {
229 version = getRedhatishVersion(contents)
230 platform = getRedhatishPlatform(contents)
231 }
232 } else if common.PathExists(common.HostEtc("gentoo-release")) {
233 platform = "gentoo"
234 contents, err := common.ReadLines(common.HostEtc("gentoo-release"))
235 if err == nil {
236 version = getRedhatishVersion(contents)
237 }
238 } else if common.PathExists(common.HostEtc("SuSE-release")) {
239 contents, err := common.ReadLines(common.HostEtc("SuSE-release"))
240 if err == nil {
241 version = getSuseVersion(contents)
242 platform = getSusePlatform(contents)
243 }
244
245 } else if common.PathExists(common.HostEtc("arch-release")) {
246 platform = "arch"
247 version = lsb.Release
248 } else if common.PathExists(common.HostEtc("alpine-release")) {
249 platform = "alpine"
250 contents, err := common.ReadLines(common.HostEtc("alpine-release"))
251 if err == nil && len(contents) > 0 && contents[0] != "" {
252 version = contents[0]
253 }
254 } else if common.PathExists(common.HostEtc("os-release")) {
255 p, v, err := common.GetOSRelease()
256 if err == nil {
257 platform = p
258 version = v
259 }
260 } else if lsb.ID == "RedHat" {
261 platform = "redhat"
262 version = lsb.Release
263 } else if lsb.ID == "Amazon" {
264 platform = "amazon"
265 version = lsb.Release
266 } else if lsb.ID == "ScientificSL" {
267 platform = "scientific"
268 version = lsb.Release
269 } else if lsb.ID == "XenServer" {
270 platform = "xenserver"
271 version = lsb.Release
272 } else if lsb.ID != "" {
273 platform = strings.ToLower(lsb.ID)
274 version = lsb.Release
275 }
276
277 switch platform {
278 case "debian", "ubuntu", "linuxmint", "raspbian":
279 family = "debian"
280 case "fedora":
281 family = "fedora"
282 case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm", "rocky":
283 family = "rhel"
284 case "suse", "opensuse", "opensuse-leap", "opensuse-tumbleweed", "opensuse-tumbleweed-kubic", "sles", "sled", "caasp":
285 family = "suse"
286 case "gentoo":
287 family = "gentoo"
288 case "slackware":
289 family = "slackware"
290 case "arch":
291 family = "arch"
292 case "exherbo":
293 family = "exherbo"
294 case "alpine":
295 family = "alpine"
296 case "coreos":
297 family = "coreos"
298 case "solus":
299 family = "solus"
300 }
301
302 return platform, family, version, nil
303
304 }
305
306 func KernelVersionWithContext(ctx context.Context) (version string, err error) {
307 var utsname unix.Utsname
308 err = unix.Uname(&utsname)
309 if err != nil {
310 return "", err
311 }
312 return string(utsname.Release[:bytes.IndexByte(utsname.Release[:], 0)]), nil
313 }
314
315 func getSlackwareVersion(contents []string) string {
316 c := strings.ToLower(strings.Join(contents, ""))
317 c = strings.Replace(c, "slackware ", "", 1)
318 return c
319 }
320
321 func getRedhatishVersion(contents []string) string {
322 c := strings.ToLower(strings.Join(contents, ""))
323
324 if strings.Contains(c, "rawhide") {
325 return "rawhide"
326 }
327 if matches := regexp.MustCompile(`release (\d[\d.]*)`).FindStringSubmatch(c); matches != nil {
328 return matches[1]
329 }
330 return ""
331 }
332
333 func getRedhatishPlatform(contents []string) string {
334 c := strings.ToLower(strings.Join(contents, ""))
335
336 if strings.Contains(c, "red hat") {
337 return "redhat"
338 }
339 f := strings.Split(c, " ")
340
341 return f[0]
342 }
343
344 func getSuseVersion(contents []string) string {
345 version := ""
346 for _, line := range contents {
347 if matches := regexp.MustCompile(`VERSION = ([\d.]+)`).FindStringSubmatch(line); matches != nil {
348 version = matches[1]
349 } else if matches := regexp.MustCompile(`PATCHLEVEL = ([\d]+)`).FindStringSubmatch(line); matches != nil {
350 version = version + "." + matches[1]
351 }
352 }
353 return version
354 }
355
356 func getSusePlatform(contents []string) string {
357 c := strings.ToLower(strings.Join(contents, ""))
358 if strings.Contains(c, "opensuse") {
359 return "opensuse"
360 }
361 return "suse"
362 }
363
364 func VirtualizationWithContext(ctx context.Context) (string, string, error) {
365 return common.VirtualizationWithContext(ctx)
366 }
367
368 func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
369 var temperatures []TemperatureStat
370 files, err := filepath.Glob(common.HostSys("/class/hwmon/hwmon*/temp*_*"))
371 if err != nil {
372 return temperatures, err
373 }
374 if len(files) == 0 {
375
376
377 files, err = filepath.Glob(common.HostSys("/class/hwmon/hwmon*/device/temp*_*"))
378 if err != nil {
379 return temperatures, err
380 }
381 }
382 var warns Warnings
383
384 if len(files) == 0 {
385 files, err = filepath.Glob(common.HostSys("/class/thermal/thermal_zone*/"))
386 if err != nil {
387 return temperatures, err
388 }
389 for _, file := range files {
390
391 name, err := ioutil.ReadFile(filepath.Join(file, "type"))
392 if err != nil {
393 warns.Add(err)
394 continue
395 }
396
397 current, err := ioutil.ReadFile(filepath.Join(file, "temp"))
398 if err != nil {
399 warns.Add(err)
400 continue
401 }
402 temperature, err := strconv.ParseInt(strings.TrimSpace(string(current)), 10, 64)
403 if err != nil {
404 warns.Add(err)
405 continue
406 }
407
408 temperatures = append(temperatures, TemperatureStat{
409 SensorKey: strings.TrimSpace(string(name)),
410 Temperature: float64(temperature) / 1000.0,
411 })
412 }
413 return temperatures, warns.Reference()
414 }
415
416
417
418
419
420
421
422 for _, file := range files {
423 filename := strings.Split(filepath.Base(file), "_")
424 if filename[1] == "label" {
425
426 continue
427 }
428
429
430 var label string
431 c, _ := ioutil.ReadFile(filepath.Join(filepath.Dir(file), filename[0]+"_label"))
432 if c != nil {
433
434 label = fmt.Sprintf("%s_", strings.Join(strings.Split(strings.TrimSpace(strings.ToLower(string(c))), " "), ""))
435 }
436
437
438 name, err := ioutil.ReadFile(filepath.Join(filepath.Dir(file), "name"))
439 if err != nil {
440 warns.Add(err)
441 continue
442 }
443
444
445 current, err := ioutil.ReadFile(file)
446 if err != nil {
447 warns.Add(err)
448 continue
449 }
450 temperature, err := strconv.ParseFloat(strings.TrimSpace(string(current)), 64)
451 if err != nil {
452 warns.Add(err)
453 continue
454 }
455
456 tempName := strings.TrimSpace(strings.ToLower(string(strings.Join(filename[1:], ""))))
457 temperatures = append(temperatures, TemperatureStat{
458 SensorKey: fmt.Sprintf("%s_%s%s", strings.TrimSpace(string(name)), label, tempName),
459 Temperature: temperature / 1000.0,
460 })
461 }
462 return temperatures, warns.Reference()
463 }
464
View as plain text