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 { Value int64 `json:"value"` } `json:"raw"` } type RawAttr struct { AtaSmartAttr struct { Table []Item `json:"table"` } `json:"ata_smart_attributes"` } func getSmartValues(diskpath string) *RawAttr { 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) } 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 printAligned(paramname string, valuestr string) { fmt.Printf("%-24v%s\n", paramname+":", valuestr) } func checkSmartAttributes(diskpath string, isSeagate bool, isHdd bool) { rawattr := getSmartValues(diskpath) 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 } printAligned("Raw_Read_Error_Rate", evalStrZero(rrerrrate)) printAligned("Reallocated_Sector_Ct", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Reallocated_Sector_Ct"))) // todo further investigate if this smart attributes can occur within hdds/ssds // https://www.backblaze.com/blog/what-smart-stats-indicate-hard-drive-failures/ printAligned("Reported_Uncorrect", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Reported_Uncorrect"))) printAligned("Command_Timeout", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Command_Timeout"))) printAligned("Current_Pending_Sector", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Current_Pending_Sector"))) printAligned("Offline_Uncorrectable", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Offline_Uncorrectable"))) // there are some additinoal hdd only smart values if isHdd { rrerrrate = getItemValue(rawattr.AtaSmartAttr.Table, "Seek_Error_Rate") if isSeagate { // [--16bits---|---32bits---] // error count| operation count rrerrrate = rrerrrate >> 32 } printAligned("Seek_Error_Rate", evalStrZero(rrerrrate)) printAligned("Spin_Retry_Count", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Spin_Retry_Count"))) printAligned("Spin_Up_Time", evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Spin_Up_Time"))) } else { // todo ssd only parameters } } func evalStrZero(nr int64) string { if nr < 0 { return "N/A" } else if nr == 0 { return "PASS" } else { return fmt.Sprintf("FAIL :: raw value=%d", nr) } } func getItemValue(arr []Item, name string) int64 { for i := range arr { if arr[i].Name == name { return arr[i].Raw.Value } } return -1 }