2022-04-15 15:16:15 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
type DiskInfo struct {
|
|
|
|
ModelFamily string `json:"model_family"`
|
|
|
|
ModelName string `json:"model_name"`
|
|
|
|
SerialNumber string `json:"serial_number"`
|
|
|
|
RotationRate uint `json:"rotation_rate"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func getDiskInfo(diskpath string) *DiskInfo {
|
|
|
|
rawsmart := execSystem("smartctl", "-i", "-json", diskpath)
|
|
|
|
if rawsmart == nil {
|
|
|
|
fmt.Println("error while getting smart info")
|
|
|
|
}
|
|
|
|
|
|
|
|
var info DiskInfo
|
|
|
|
|
|
|
|
// decoding country1 struct
|
|
|
|
// from json format
|
|
|
|
err := json.Unmarshal(rawsmart, &info)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
return &info
|
|
|
|
}
|
|
|
|
|
|
|
|
type Item struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Raw struct {
|
2022-05-05 10:17:21 +00:00
|
|
|
Value int64 `json:"value"`
|
2022-04-15 15:16:15 +00:00
|
|
|
} `json:"raw"`
|
|
|
|
}
|
|
|
|
|
2022-05-05 09:56:08 +00:00
|
|
|
type RawAttr struct {
|
|
|
|
AtaSmartAttr struct {
|
|
|
|
Table []Item `json:"table"`
|
|
|
|
} `json:"ata_smart_attributes"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func getSmartValues(diskpath string) *RawAttr {
|
2022-04-15 15:16:15 +00:00
|
|
|
rawsmart := execSystem("smartctl", "-A", "-json", diskpath)
|
|
|
|
if rawsmart == nil {
|
|
|
|
fmt.Println("error while getting smart info")
|
|
|
|
}
|
|
|
|
|
|
|
|
var rawattr RawAttr
|
|
|
|
|
|
|
|
err := json.Unmarshal(rawsmart, &rawattr)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
2022-05-05 09:56:08 +00:00
|
|
|
return &rawattr
|
|
|
|
}
|
|
|
|
|
|
|
|
func getTemp(diskpath string) (uint32, uint32, uint32) {
|
|
|
|
rawattr := getSmartValues(diskpath)
|
|
|
|
temp := getItemValue(rawattr.AtaSmartAttr.Table, "Temperature_Celsius")
|
|
|
|
|
|
|
|
// temp value is storead as following:
|
|
|
|
// 4bytes 4bytes 4bytes
|
|
|
|
// [----------------|----------------|---------------]
|
|
|
|
// max temp min temp curr temp
|
|
|
|
|
|
|
|
max := temp >> 32
|
|
|
|
min := (temp & 0xff00ff) >> 16
|
|
|
|
currtemp := temp & 0x0000ff
|
|
|
|
|
|
|
|
return uint32(max), uint32(min), uint32(currtemp)
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkSmartAttributes(diskpath string, isSeagate bool, isHdd bool) {
|
|
|
|
rawattr := getSmartValues(diskpath)
|
2022-04-15 15:16:15 +00:00
|
|
|
|
|
|
|
rrerrrate := getItemValue(rawattr.AtaSmartAttr.Table, "Raw_Read_Error_Rate")
|
|
|
|
// seagate hdds have a special way to represent their error rates
|
|
|
|
if isSeagate {
|
|
|
|
// [--16bits---|---32bits---]
|
|
|
|
// error count| operation count
|
|
|
|
rrerrrate = rrerrrate >> 32
|
|
|
|
}
|
|
|
|
fmt.Println("Raw_Read_Error_Rate: " + evalStrZero(rrerrrate))
|
|
|
|
fmt.Println("Reallocated_Sector_Ct: " + evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Reallocated_Sector_Ct")))
|
|
|
|
|
2022-05-05 10:17:21 +00:00
|
|
|
// todo further investigate if this smart attributes can occur within hdds/ssds
|
|
|
|
// https://www.backblaze.com/blog/what-smart-stats-indicate-hard-drive-failures/
|
|
|
|
fmt.Println("Reported_Uncorrect: " + evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Reported_Uncorrect")))
|
|
|
|
fmt.Println("Command_Timeout: " + evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Command_Timeout")))
|
|
|
|
fmt.Println("Current_Pending_Sector: " + evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Current_Pending_Sector")))
|
|
|
|
fmt.Println("Offline_Uncorrectable: " + evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Offline_Uncorrectable")))
|
|
|
|
|
2022-04-15 15:16:15 +00:00
|
|
|
// there are some additinoal hdd smart values
|
|
|
|
if isHdd {
|
|
|
|
rrerrrate = getItemValue(rawattr.AtaSmartAttr.Table, "Seek_Error_Rate")
|
|
|
|
if isSeagate {
|
|
|
|
// [--16bits---|---32bits---]
|
|
|
|
// error count| operation count
|
|
|
|
rrerrrate = rrerrrate >> 32
|
|
|
|
}
|
2022-05-04 10:16:02 +00:00
|
|
|
fmt.Println("Seek_Error_Rate:\t" + evalStrZero(rrerrrate))
|
|
|
|
fmt.Println("Spin_Retry_Count:\t" + evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Spin_Retry_Count")))
|
|
|
|
fmt.Println("Spin_Up_Time:\t\t" + evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Spin_Up_Time")))
|
2022-04-15 15:16:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-05 10:17:21 +00:00
|
|
|
func evalStrZero(nr int64) string {
|
|
|
|
if nr < 0 {
|
|
|
|
return "N/A"
|
|
|
|
} else if nr == 0 {
|
2022-04-15 15:16:15 +00:00
|
|
|
return "\tPASS"
|
|
|
|
} else {
|
|
|
|
return fmt.Sprintf("\tFAIL :: raw value=%d", nr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-05 10:17:21 +00:00
|
|
|
func getItemValue(arr []Item, name string) int64 {
|
2022-04-15 15:16:15 +00:00
|
|
|
for i := range arr {
|
|
|
|
if arr[i].Name == name {
|
|
|
|
return arr[i].Raw.Value
|
|
|
|
}
|
|
|
|
}
|
2022-05-05 10:17:21 +00:00
|
|
|
return -1
|
2022-04-15 15:16:15 +00:00
|
|
|
}
|