From 4a216328016fe4c5f1cbed24885df1bfde304082 Mon Sep 17 00:00:00 2001 From: Yan Lin Date: Sun, 14 Sep 2025 14:00:06 +0200 Subject: [PATCH] Enhance SMART report information --- hosts/nixos/hs/home.nix | 2 +- hosts/nixos/thinkpad/home.nix | 2 +- scripts/daily-smart-report.sh | 107 +++++++++++++++++++++++++++++++--- 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/hosts/nixos/hs/home.nix b/hosts/nixos/hs/home.nix index f223d29..c8027d5 100644 --- a/hosts/nixos/hs/home.nix +++ b/hosts/nixos/hs/home.nix @@ -9,7 +9,7 @@ # hs-specific home configuration programs.zsh.shellAliases = { # Disk health monitoring - smart-report = "sudo SMART_DRIVES='/dev/disk/by-id/ata-ZHITAI_SC001_XT_1000GB_ZTB401TAB244431J4R:ZFS Mirror 1;/dev/disk/by-id/ata-ZHITAI_SC001_XT_1000GB_ZTB401TAB244431KEG:ZFS Mirror 2;/dev/disk/by-id/ata-HGST_HUH721212ALE604_5PK2N4GB:Data Drive 1 (12TB);/dev/disk/by-id/ata-HGST_HUH721212ALE604_5PJ7Z3LE:Data Drive 2 (12TB);/dev/disk/by-id/ata-ST16000NM000J-2TW103_WRS0F8BE:Parity Drive (16TB)' /home/yanlin/.config/nix/scripts/daily-smart-report.sh Ac9qKFH5cA.7Yly"; + smart-report = "sudo SMART_DRIVES='/dev/disk/by-id/ata-ZHITAI_SC001_XT_1000GB_ZTB401TAB244431J4R:ZFS_Mirror_1;/dev/disk/by-id/ata-ZHITAI_SC001_XT_1000GB_ZTB401TAB244431KEG:ZFS_Mirror_2;/dev/disk/by-id/ata-HGST_HUH721212ALE604_5PK2N4GB:Data_Drive_1_12TB;/dev/disk/by-id/ata-HGST_HUH721212ALE604_5PJ7Z3LE:Data_Drive_2_12TB;/dev/disk/by-id/ata-ST16000NM000J-2TW103_WRS0F8BE:Parity_Drive_16TB' /home/yanlin/.config/nix/scripts/daily-smart-report.sh Ac9qKFH5cA.7Yly"; move-inbox = "cp -rl /mnt/storage/Media/downloads/.inbox/* /mnt/storage/Media/downloads/inbox && chown -R yanlin:users /mnt/storage/Media/downloads/inbox"; }; diff --git a/hosts/nixos/thinkpad/home.nix b/hosts/nixos/thinkpad/home.nix index 33b1d0e..e60c2b6 100644 --- a/hosts/nixos/thinkpad/home.nix +++ b/hosts/nixos/thinkpad/home.nix @@ -33,7 +33,7 @@ programs.zsh.shellAliases = { # Disk health monitoring - smart-report = "sudo SMART_DRIVES='/dev/nvme0n1:System SSD (ThinkPad)' /home/yanlin/.config/nix/scripts/daily-smart-report.sh AieM4SJHFcyl7TC"; + smart-report = "sudo SMART_DRIVES='/dev/nvme0n1:System_SSD_ThinkPad' /home/yanlin/.config/nix/scripts/daily-smart-report.sh AieM4SJHFcyl7TC"; }; home.packages = with pkgs; [ diff --git a/scripts/daily-smart-report.sh b/scripts/daily-smart-report.sh index 3e43565..0ea3a43 100755 --- a/scripts/daily-smart-report.sh +++ b/scripts/daily-smart-report.sh @@ -64,6 +64,14 @@ main() { continue fi + # Detect drive type + local drive_type="UNKNOWN" + if [[ "$device" == *"nvme"* ]]; then + drive_type="NVMe" + elif smartctl -i "$device" 2>/dev/null | grep -q "SATA\|ATA"; then + drive_type="SATA" + fi + # Get SMART health local health="UNKNOWN" if health=$(smartctl -H "$device" 2>/dev/null | grep -o "PASSED\|FAILED" | head -1); then @@ -73,29 +81,110 @@ main() { echo " Health: $health" fi - # Get temperature + # Get enhanced SMART data local temp="N/A" + local power_hours="N/A" + local wear_info="N/A" + local data_info="" + local error_info="" + if [[ "$health" == "PASSED" ]]; then - if temp=$(smartctl -A "$device" 2>/dev/null | awk '/Temperature_Celsius/ {print $10}' | head -1); then - if [[ "$temp" -gt 0 ]] 2>/dev/null; then + local smart_data + smart_data=$(smartctl -A "$device" 2>/dev/null) + + if [[ "$drive_type" == "NVMe" ]]; then + # NVMe specific attributes + temp=$(echo "$smart_data" | awk '/^Temperature:/ {print $2}' | head -1) + if [[ -n "$temp" && "$temp" =~ ^[0-9]+$ ]]; then temp="${temp}C" - echo " Temperature: $temp" else temp="N/A" - echo " Temperature: $temp" fi + + power_hours=$(echo "$smart_data" | awk '/^Power On Hours:/ {print $4}' | sed 's/,//g') + + local percentage_used + percentage_used=$(echo "$smart_data" | awk '/^Percentage Used:/ {print $3}' | tr -d '%') + if [[ -n "$percentage_used" ]]; then + wear_info="Wear: ${percentage_used}%" + fi + + local data_read data_written + data_read=$(echo "$smart_data" | awk '/^Data Units Read:/ {match($0, /\[([^\]]+)\]/, arr); print arr[1]}') + data_written=$(echo "$smart_data" | awk '/^Data Units Written:/ {match($0, /\[([^\]]+)\]/, arr); print arr[1]}') + if [[ -n "$data_read" && -n "$data_written" ]]; then + data_info="Data: R:${data_read} W:${data_written}" + fi + + local unsafe_shutdowns media_errors + unsafe_shutdowns=$(echo "$smart_data" | awk '/^Unsafe Shutdowns:/ {print $3}') + media_errors=$(echo "$smart_data" | awk '/^Media and Data Integrity Errors:/ {print $6}') + + local error_parts=() + if [[ -n "$unsafe_shutdowns" && "$unsafe_shutdowns" -gt 0 ]]; then + error_parts+=("UnsafeShutdowns:$unsafe_shutdowns") + fi + if [[ -n "$media_errors" && "$media_errors" -gt 0 ]]; then + error_parts+=("MediaErrors:$media_errors") + fi + if [[ ${#error_parts[@]} -gt 0 ]]; then + error_info=$(IFS=' '; echo "${error_parts[*]}") + fi + else - temp="N/A" - echo " Temperature: $temp" + # SATA/SAS specific attributes + temp=$(echo "$smart_data" | awk '/Temperature_Celsius/ {print $10}' | head -1) + if [[ -n "$temp" && "$temp" =~ ^[0-9]+$ ]]; then + temp="${temp}C" + else + temp="N/A" + fi + + power_hours=$(echo "$smart_data" | awk '/Power_On_Hours/ {print $10}' | head -1) + + local reallocated + reallocated=$(echo "$smart_data" | awk '/Reallocated_Sector_Ct/ {print $10}' | head -1) + if [[ -n "$reallocated" ]]; then + wear_info="Reallocated:$reallocated" + fi + + local power_cycles + power_cycles=$(echo "$smart_data" | awk '/Power_Cycle_Count/ {print $10}' | head -1) + if [[ -n "$power_cycles" ]]; then + data_info="PowerCycles:$power_cycles" + fi fi + + echo " Temperature: $temp" + echo " Power Hours: $power_hours" + [[ -n "$wear_info" ]] && echo " $wear_info" + [[ -n "$data_info" ]] && echo " $data_info" + [[ -n "$error_info" ]] && echo " $error_info" fi # Format output if [[ "$health" == "PASSED" ]]; then - report+="[OK] $device_name: $health (Temp: $temp)\n" + report+="[OK] $device_name ($drive_type): $health\\n" + report+=" Temp: $temp" + if [[ "$power_hours" != "N/A" ]]; then + report+=", PowerOn: ${power_hours}h" + fi + if [[ -n "$wear_info" ]]; then + report+=", $wear_info" + fi + report+="\\n" + if [[ -n "$data_info" ]]; then + report+=" $data_info\\n" + fi + if [[ -n "$error_info" ]]; then + report+=" ⚠️ $error_info\\n" + fi healthy_drives=$((healthy_drives + 1)) else - report+="[FAIL] $device_name: $health (Temp: $temp)\n" + report+="[FAIL] $device_name ($drive_type): $health\\n" + if [[ "$temp" != "N/A" ]]; then + report+=" Temp: $temp\\n" + fi fi done