diff --git a/agent/smart.go b/agent/smart.go index c17d9b77..e2e56175 100644 --- a/agent/smart.go +++ b/agent/smart.go @@ -453,7 +453,7 @@ func (sm *SmartManager) parseSmartForSata(output []byte) (bool, int) { Value: attr.Value, Worst: attr.Worst, Threshold: attr.Thresh, - RawValue: attr.Raw.Value, + RawValue: uint64(attr.Raw.Value), RawString: attr.Raw.String, WhenFailed: attr.WhenFailed, } diff --git a/internal/entities/smart/smart.go b/internal/entities/smart/smart.go index b141ef2f..a40b85f8 100644 --- a/internal/entities/smart/smart.go +++ b/internal/entities/smart/smart.go @@ -1,5 +1,10 @@ package smart +import ( + "strconv" + "strings" +) + // Common types type VersionInfo [2]int @@ -129,30 +134,97 @@ type AtaSmartAttributes struct { } type AtaSmartAttribute struct { - ID uint16 `json:"id"` - Name string `json:"name"` - Value uint16 `json:"value"` - Worst uint16 `json:"worst"` - Thresh uint16 `json:"thresh"` - WhenFailed string `json:"when_failed"` - Flags AttributeFlags `json:"flags"` - Raw RawValue `json:"raw"` + ID uint16 `json:"id"` + Name string `json:"name"` + Value uint16 `json:"value"` + Worst uint16 `json:"worst"` + Thresh uint16 `json:"thresh"` + WhenFailed string `json:"when_failed"` + // Flags AttributeFlags `json:"flags"` + Raw RawValue `json:"raw"` } -type AttributeFlags struct { - Value int `json:"value"` - String string `json:"string"` - Prefailure bool `json:"prefailure"` - UpdatedOnline bool `json:"updated_online"` - Performance bool `json:"performance"` - ErrorRate bool `json:"error_rate"` - EventCount bool `json:"event_count"` - AutoKeep bool `json:"auto_keep"` -} +// type AttributeFlags struct { +// Value int `json:"value"` +// String string `json:"string"` +// Prefailure bool `json:"prefailure"` +// UpdatedOnline bool `json:"updated_online"` +// Performance bool `json:"performance"` +// ErrorRate bool `json:"error_rate"` +// EventCount bool `json:"event_count"` +// AutoKeep bool `json:"auto_keep"` +// } type RawValue struct { - Value uint64 `json:"value"` - String string `json:"string"` + Value SmartRawValue `json:"value"` + String string `json:"string"` +} + +type SmartRawValue uint64 + +// handles when drives report strings like "0h+0m+0.000s" or "7344 (253d 8h)" for power on hours +func (v *SmartRawValue) UnmarshalJSON(data []byte) error { + trimmed := strings.TrimSpace(string(data)) + if len(trimmed) == 0 || trimmed == "null" { + *v = 0 + return nil + } + + if trimmed[0] != '"' { + parsed, err := strconv.ParseUint(trimmed, 0, 64) + if err != nil { + return err + } + *v = SmartRawValue(parsed) + return nil + } + + valueStr, err := strconv.Unquote(trimmed) + if err != nil { + return err + } + if valueStr == "" { + *v = 0 + return nil + } + + if parsed, err := strconv.ParseUint(valueStr, 0, 64); err == nil { + *v = SmartRawValue(parsed) + return nil + } + + if idx := strings.IndexRune(valueStr, 'h'); idx >= 0 { + hoursPart := strings.TrimSpace(valueStr[:idx]) + if hoursPart == "" { + *v = 0 + return nil + } + if parsed, err := strconv.ParseFloat(hoursPart, 64); err == nil { + *v = SmartRawValue(uint64(parsed)) + return nil + } + } + + if digits := leadingDigitPrefix(valueStr); digits != "" { + if parsed, err := strconv.ParseUint(digits, 0, 64); err == nil { + *v = SmartRawValue(parsed) + return nil + } + } + + *v = 0 + return nil +} + +func leadingDigitPrefix(value string) string { + var builder strings.Builder + for _, r := range value { + if r < '0' || r > '9' { + break + } + builder.WriteRune(r) + } + return builder.String() } // type PowerOnTimeInfo struct { diff --git a/internal/entities/smart/smart_test.go b/internal/entities/smart/smart_test.go new file mode 100644 index 00000000..67be4b61 --- /dev/null +++ b/internal/entities/smart/smart_test.go @@ -0,0 +1,30 @@ +package smart + +import ( + "encoding/json" + "testing" +) + +func TestSmartRawValueUnmarshalDuration(t *testing.T) { + input := []byte(`{"value":"62312h+33m+50.907s","string":"62312h+33m+50.907s"}`) + var raw RawValue + if err := json.Unmarshal(input, &raw); err != nil { + t.Fatalf("unexpected error unmarshalling raw value: %v", err) + } + + if uint64(raw.Value) != 62312 { + t.Fatalf("expected hours to be 62312, got %d", raw.Value) + } +} + +func TestSmartRawValueUnmarshalNumericString(t *testing.T) { + input := []byte(`{"value":"7344","string":"7344"}`) + var raw RawValue + if err := json.Unmarshal(input, &raw); err != nil { + t.Fatalf("unexpected error unmarshalling numeric string: %v", err) + } + + if uint64(raw.Value) != 7344 { + t.Fatalf("expected hours to be 7344, got %d", raw.Value) + } +}