-
-
Notifications
You must be signed in to change notification settings - Fork 66
Expand file tree
/
Copy pathsmart.go
More file actions
132 lines (112 loc) · 3.71 KB
/
Copy pathsmart.go
File metadata and controls
132 lines (112 loc) · 3.71 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
// Copyright (c) 2024-2026, s0up and the autobrr contributors.
// SPDX-License-Identifier: GPL-2.0-or-later
//go:build !nosmart && (linux || darwin)
// +build !nosmart
// +build linux darwin
package agent
import (
"fmt"
"path/filepath"
"strings"
"github.com/anatol/smart.go"
"github.com/rs/zerolog/log"
)
// swapBytes swaps pairs of bytes in ATA strings which are stored in a special format
func swapBytes(b []byte) []byte {
swapped := make([]byte, len(b))
for i := 0; i < len(b)-1; i += 2 {
swapped[i] = b[i+1]
swapped[i+1] = b[i]
}
if len(b)%2 == 1 {
swapped[len(b)-1] = b[len(b)-1]
}
return swapped
}
// getDiskInfo retrieves disk model/serial info for a device path
func (a *Agent) getDiskInfo(devicePath string) (model string, serial string) {
dev, err := smart.Open(devicePath)
if err != nil {
return "", ""
}
defer dev.Close()
// Try different device types to get identify information
switch d := dev.(type) {
case *smart.SataDevice:
if ident, err := d.Identify(); err == nil {
// ATA strings have byte pairs swapped
model = strings.TrimSpace(string(swapBytes(ident.ModelNumberRaw[:])))
serial = strings.TrimSpace(string(swapBytes(ident.SerialNumberRaw[:])))
}
case *smart.NVMeDevice:
if ctrl, _, err := d.Identify(); err == nil {
// NVMe strings are stored normally
model = strings.TrimSpace(string(ctrl.ModelNumberRaw[:]))
serial = strings.TrimSpace(string(ctrl.SerialNumberRaw[:]))
}
}
return model, serial
}
// getHDDTemperatures retrieves disk temperatures via SMART data
func (a *Agent) getHDDTemperatures() []TemperatureStats {
var temps []TemperatureStats
// Get device paths based on platform
devicePaths := a.getDevicePaths()
for _, devicePath := range devicePaths {
name := filepath.Base(devicePath)
// Try to open the device with SMART
dev, err := smart.Open(devicePath)
if err != nil {
log.Trace().Str("device", devicePath).Err(err).Msg("Failed to open device for SMART (may need root or unsupported on this platform)")
continue
}
// Get temperature using the generic attributes API
attrs, err := dev.ReadGenericAttributes()
if err != nil {
dev.Close()
log.Trace().Str("device", devicePath).Err(err).Msg("Failed to read generic attributes")
continue
}
// Check if we got a valid temperature
if attrs != nil && attrs.Temperature > 0 && attrs.Temperature < 100 {
deviceType := "HDD"
modelName := ""
// Try to get model information
switch d := dev.(type) {
case *smart.SataDevice:
if ident, err := d.Identify(); err == nil {
// ATA strings have byte pairs swapped
modelName = strings.TrimSpace(string(swapBytes(ident.ModelNumberRaw[:])))
// Check rotation rate to determine if SSD (0 RPM) or HDD
// Note: This is a simplified check, some SSDs may not report 0
deviceType = "HDD" // Default to HDD for SATA
}
case *smart.NVMeDevice:
deviceType = "NVMe"
if ctrl, _, err := d.Identify(); err == nil {
// NVMe strings are stored normally
modelName = strings.TrimSpace(string(ctrl.ModelNumberRaw[:]))
}
}
label := fmt.Sprintf("%s %s", deviceType, strings.ToUpper(name))
if modelName != "" {
// Include both model name and device identifier
label = fmt.Sprintf("%s (%s)", modelName, strings.ToUpper(name))
}
temps = append(temps, TemperatureStats{
SensorKey: fmt.Sprintf("smart_%s", name),
Temperature: float64(attrs.Temperature),
Label: label,
Critical: 60.0, // HDDs typically warn at 60°C, SSDs at 70°C
})
log.Debug().
Str("device", name).
Uint64("temp", attrs.Temperature).
Str("type", deviceType).
Str("model", modelName).
Msg("Added disk temperature from SMART")
}
dev.Close()
}
return temps
}