master
go 132 lines 2.53 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package hddtemp
4
5 import (
6 "errors"
7 "fmt"
8 "strconv"
9 "strings"
10 )
11
12 type diskStats struct {
13 devPath string
14 model string
15 temperature string
16 unit string
17 }
18
19 func (c *Collector) collect() (map[string]int64, error) {
20 msg, err := c.conn.queryHddTemp()
21 if err != nil {
22 return nil, err
23 }
24
25 c.Debugf("hddtemp daemon response: %s", msg)
26
27 disks, err := parseHddTempMessage(msg)
28 if err != nil {
29 return nil, err
30 }
31
32 mx := make(map[string]int64)
33
34 for _, disk := range disks {
35 id := getDiskID(disk)
36 if id == "" {
37 c.Debugf("can not extract disk id from '%s'", disk.devPath)
38 continue
39 }
40
41 if !c.disks[id] {
42 c.disks[id] = true
43 c.addDiskTempSensorStatusChart(id, disk)
44 }
45
46 px := fmt.Sprintf("disk_%s_", id)
47
48 for _, st := range []string{"ok", "na", "unk", "nos", "slp", "err"} {
49 mx[px+"temp_sensor_status_"+st] = 0
50 }
51 switch disk.temperature {
52 case "NA":
53 mx[px+"temp_sensor_status_na"] = 1
54 case "UNK":
55 mx[px+"temp_sensor_status_unk"] = 1
56 case "NOS":
57 mx[px+"temp_sensor_status_nos"] = 1
58 case "SLP":
59 mx[px+"temp_sensor_status_slp"] = 1
60 case "ERR":
61 mx[px+"temp_sensor_status_err"] = 1
62 default:
63 if v, ok := getTemperature(disk); ok {
64 if !c.disksTemp[id] {
65 c.disksTemp[id] = true
66 c.addDiskTempChart(id, disk)
67 }
68 mx[px+"temp_sensor_status_ok"] = 1
69 mx[px+"temperature"] = v
70 } else {
71 mx[px+"temp_sensor_status_unk"] = 1
72 }
73 }
74 }
75
76 return mx, nil
77 }
78
79 func getDiskID(d diskStats) string {
80 i := strings.LastIndexByte(d.devPath, '/')
81 if i == -1 {
82 return ""
83 }
84 return d.devPath[i+1:]
85 }
86
87 func getTemperature(d diskStats) (int64, bool) {
88 v, err := strconv.ParseInt(d.temperature, 10, 64)
89 if err != nil {
90 return 0, false
91 }
92 if d.unit == "F" {
93 v = (v - 32) * 5 / 9
94 }
95 return v, true
96 }
97
98 func parseHddTempMessage(msg string) ([]diskStats, error) {
99 if msg == "" {
100 return nil, errors.New("empty hddtemp message")
101 }
102
103 // https://github.com/guzu/hddtemp/blob/e16aed6d0145d7ad8b3308dd0b9199fc701c0417/src/daemon.c#L165
104 parts := strings.Split(msg, "|")
105
106 var i int
107 // remove empty values
108 for _, v := range parts {
109 if v = strings.TrimSpace(v); v != "" {
110 parts[i] = v
111 i++
112 }
113 }
114 parts = parts[:i]
115
116 if len(parts) == 0 || len(parts)%4 != 0 {
117 return nil, errors.New("invalid hddtemp output format")
118 }
119
120 var disks []diskStats
121
122 for i := 0; i < len(parts); i += 4 {
123 disks = append(disks, diskStats{
124 devPath: parts[i],
125 model: parts[i+1],
126 temperature: parts[i+2],
127 unit: parts[i+3],
128 })
129 }
130
131 return disks, nil
132 }