From 7499595649e825315193bb127b6b4265eafd3eb0 Mon Sep 17 00:00:00 2001 From: lukas Date: Fri, 15 Apr 2022 17:16:15 +0200 Subject: [PATCH] init --- disks.go | 36 +++++++++++++++++ go.mod | 3 ++ main.go | 43 ++++++++++++++++++++ smartdiskinfo.go | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 disks.go create mode 100644 go.mod create mode 100644 main.go create mode 100644 smartdiskinfo.go diff --git a/disks.go b/disks.go new file mode 100644 index 0000000..227a1fc --- /dev/null +++ b/disks.go @@ -0,0 +1,36 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" +) + +func getDisks() *Devices { + rawout := execSystem("lsblk", "-dJ", "-I 8") + if rawout == nil { + log.Fatal("") + } + var parsed Devices + + // decoding country1 struct + // from json format + err := json.Unmarshal(rawout, &parsed) + + if err != nil { + // if error is not nil + // print error + fmt.Println(err) + } + return &parsed +} + +type BlockDevice struct { + Name string `json:"name"` + Size string `json:"size"` + Type string `json:"type"` +} + +type Devices struct { + Blockdevices []BlockDevice `json:"blockdevices"` +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..62f39e8 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module diskhealth + +go 1.16 diff --git a/main.go b/main.go new file mode 100644 index 0000000..1a26e62 --- /dev/null +++ b/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "bytes" + "fmt" + "log" + "os/exec" + "strings" +) + +func execSystem(cmd string, args ...string) []byte { + com := exec.Command(cmd, args...) + var out bytes.Buffer + com.Stdout = &out + com.Stderr = &out + + err := com.Run() + + if out.Bytes() == nil { + fmt.Println(out.String()) + log.Fatal(err) + return nil + } + + return out.Bytes() +} + +func main() { + fmt.Println("Running smart check on all disks") + + disks := getDisks() + for _, blockdevice := range disks.Blockdevices { + fmt.Printf("\nChecking /dev/%s\n", blockdevice.Name) + + path := fmt.Sprintf("/dev/%s", blockdevice.Name) + + info := getDiskInfo(path) + fmt.Printf("%s %s\n", info.ModelFamily, info.ModelName) + + checkSmartAttributes(path, strings.Contains(strings.ToLower(info.ModelName+info.ModelFamily), strings.ToLower("Seagate")), info.RotationRate != 0) + } + +} diff --git a/smartdiskinfo.go b/smartdiskinfo.go new file mode 100644 index 0000000..48fbdd6 --- /dev/null +++ b/smartdiskinfo.go @@ -0,0 +1,101 @@ +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 uint64 `json:"value"` + } `json:"raw"` +} + +func checkSmartAttributes(diskpath string, isSeagate bool, isHdd bool) { + rawsmart := execSystem("smartctl", "-A", "-json", diskpath) + if rawsmart == nil { + fmt.Println("error while getting smart info") + } + + type RawAttr struct { + AtaSmartAttr struct { + Table []Item `json:"table"` + } `json:"ata_smart_attributes"` + } + + var rawattr RawAttr + + err := json.Unmarshal(rawsmart, &rawattr) + + if err != nil { + fmt.Println(err) + } + + 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"))) + + // 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 + } + fmt.Println("Seek_Error_Rate: " + evalStrZero(rrerrrate)) + fmt.Println("Spin_Retry_Count: " + evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Spin_Retry_Count"))) + fmt.Println("Spin_Up_Time: " + evalStrZero(getItemValue(rawattr.AtaSmartAttr.Table, "Spin_Up_Time"))) + } + + fmt.Println(rawattr) +} + +func evalStrZero(nr uint64) string { + if nr == 0 { + return "\tPASS" + } else { + return fmt.Sprintf("\tFAIL :: raw value=%d", nr) + } +} + +func getItemValue(arr []Item, name string) uint64 { + for i := range arr { + if arr[i].Name == name { + return arr[i].Raw.Value + } + } + return 0 +}