From 5786ff6cee43038542114ea3c4192e33b607bf34 Mon Sep 17 00:00:00 2001 From: Yan Lin Date: Fri, 10 Oct 2025 00:59:16 +0200 Subject: [PATCH] Add login display module --- hosts/nixos/hs/system.nix | 14 ++-- modules/login-display.nix | 148 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 7 deletions(-) create mode 100644 modules/login-display.nix diff --git a/hosts/nixos/hs/system.nix b/hosts/nixos/hs/system.nix index 91ba18a..450619b 100644 --- a/hosts/nixos/hs/system.nix +++ b/hosts/nixos/hs/system.nix @@ -11,7 +11,7 @@ ../../../modules/borg-client.nix ../../../modules/webdav.nix ../../../modules/container-updater.nix - ../../../modules/smart-report.nix + ../../../modules/login-display.nix ]; # GRUB bootloader with ZFS support @@ -219,19 +219,19 @@ hideDotFiles = true; }; - # SMART disk health reporting - services.smart-report = { + # Login display with SMART disk health status + services.login-display = { enable = true; - enableSystemdService = true; - schedule = "08:00:00"; - gotifyToken = "Ac9qKFH5cA.7Yly"; - drives = { + showSystemInfo = true; + showSmartStatus = true; + smartDrives = { "/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"; }; + showDiskUsage = false; }; # Borg backup configuration diff --git a/modules/login-display.nix b/modules/login-display.nix new file mode 100644 index 0000000..4229ba9 --- /dev/null +++ b/modules/login-display.nix @@ -0,0 +1,148 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.login-display; +in + +{ + options.services.login-display = { + enable = mkEnableOption "login information display on SSH sessions"; + + showSmartStatus = mkOption { + type = types.bool; + default = false; + description = "Show SMART disk health status"; + }; + + smartDrives = mkOption { + type = types.attrsOf types.str; + default = {}; + description = "Drives to monitor for SMART status (device path -> name mapping)"; + example = { + "/dev/disk/by-id/ata-Samsung_SSD" = "System_SSD"; + }; + }; + + showSystemInfo = mkOption { + type = types.bool; + default = true; + description = "Show basic system information (hostname, uptime, load)"; + }; + + showDiskUsage = mkOption { + type = types.bool; + default = false; + description = "Show disk usage information"; + }; + + diskUsagePaths = mkOption { + type = types.listOf types.str; + default = [ "/" ]; + description = "Paths to check for disk usage"; + }; + }; + + config = mkIf cfg.enable { + # Add smartmontools if SMART status is enabled + environment.systemPackages = mkIf cfg.showSmartStatus [ pkgs.smartmontools ]; + + # Configure shell login initialization + programs.zsh.loginShellInit = mkIf config.programs.zsh.enable ( + let + # Build SMART status display + smartStatusCode = optionalString cfg.showSmartStatus '' + # Only show on SSH sessions + if [[ -n "$SSH_CONNECTION" ]] || [[ -n "$SSH_TTY" ]]; then + echo "" + echo "=== Disk Health Status ===" + + ${concatStringsSep "\n" (mapAttrsToList (device: name: '' + if [[ -e "${device}" ]]; then + # Determine if NVMe + SMART_OPTS="" + if [[ "${device}" == *"nvme"* ]]; then + SMART_OPTS="-d nvme" + fi + + # Get health status (using sudo for disk access) + HEALTH=$(sudo ${pkgs.smartmontools}/bin/smartctl $SMART_OPTS -H "${device}" 2>/dev/null | ${pkgs.gnugrep}/bin/grep -o "PASSED\|FAILED" | head -1 || echo "UNKNOWN") + + # Get temperature + TEMP="N/A" + if [[ "$HEALTH" == "PASSED" ]]; then + SMART_DATA=$(sudo ${pkgs.smartmontools}/bin/smartctl $SMART_OPTS -A "${device}" 2>/dev/null) + if [[ "${device}" == *"nvme"* ]]; then + TEMP=$(echo "$SMART_DATA" | ${pkgs.gawk}/bin/awk '/^Temperature:/ {print $2}' | head -1) + [[ -n "$TEMP" && "$TEMP" =~ ^[0-9]+$ ]] && TEMP="''${TEMP}°C" || TEMP="N/A" + else + TEMP=$(echo "$SMART_DATA" | ${pkgs.gawk}/bin/awk '/Temperature_Celsius/ {print $10}' | head -1) + [[ -n "$TEMP" && "$TEMP" =~ ^[0-9]+$ ]] && TEMP="''${TEMP}°C" || TEMP="N/A" + fi + fi + + # Display status with color + if [[ "$HEALTH" == "PASSED" ]]; then + echo " ✓ ${name}: $HEALTH (Temp: $TEMP)" + else + echo " ✗ ${name}: $HEALTH" + fi + else + echo " ⚠ ${name}: Device not found" + fi + '') cfg.smartDrives)} + fi + ''; + + # Build system info display + systemInfoCode = optionalString cfg.showSystemInfo '' + # Only show on SSH sessions + if [[ -n "$SSH_CONNECTION" ]] || [[ -n "$SSH_TTY" ]]; then + echo "" + echo "=== System Information ===" + echo " Hostname: $(hostname)" + # Parse uptime output to get readable format + UPTIME_STR=$(uptime | ${pkgs.gawk}/bin/awk '{ + # Extract the uptime part (between "up" and "user" or "load") + match($0, /up\s+(.+?),\s+[0-9]+\s+user/, arr) + if (arr[1] != "") { + print arr[1] + } + }') + echo " Uptime: $UPTIME_STR" + echo " Load: $(uptime | ${pkgs.gawk}/bin/awk -F'load average:' '{print $2}')" + fi + ''; + + # Build disk usage display + diskUsageCode = optionalString cfg.showDiskUsage '' + # Only show on SSH sessions + if [[ -n "$SSH_CONNECTION" ]] || [[ -n "$SSH_TTY" ]]; then + echo "" + echo "=== Disk Usage ===" + ${concatMapStringsSep "\n" (path: '' + df -h "${path}" | ${pkgs.gawk}/bin/awk 'NR==2 {printf " ${path}: %s / %s (%s used)\n", $3, $2, $5}' + '') cfg.diskUsagePaths} + fi + ''; + + in '' + ${systemInfoCode} + ${smartStatusCode} + ${diskUsageCode} + + # Add blank line after all info + if [[ -n "$SSH_CONNECTION" ]] || [[ -n "$SSH_TTY" ]]; then + echo "" + fi + '' + ); + + # Also support bash if needed + programs.bash.loginShellInit = mkIf (!config.programs.zsh.enable) ( + # Same content as zsh + programs.zsh.loginShellInit + ); + }; +}