#!/usr/bin/env bash # astro-menu system-monitor backend — the ship's "Systems Diagnostic". # Emits one TAB-separated record per subsystem: # # CATEGORY NAME USAGE_PCT TEMP_C DETAIL # # CATEGORY is cpu|gpu|ram|disk (drives the icon). USAGE_PCT is 0..100. TEMP_C is # an integer °C or empty when no sensor exists. One `disk` row per mounted real # filesystem. Reads everything from /proc + /sys (no extra deps). set -uo pipefail milli_to_c() { awk '{printf "%d", ($1 + 500) / 1000}'; } hwmon_temp() { # $1 = chip-name substring; first temp input of that chip, in °C local h n t for h in /sys/class/hwmon/hwmon*; do n=$(cat "$h/name" 2>/dev/null) || continue [[ "$n" == *"$1"* ]] || continue for t in "$h"/temp*_input; do [[ -r "$t" ]] && { milli_to_c < "$t"; return 0; } done done return 1 } hwmon_dir_temp() { # $1 = hwmon dir, $2 = preferred label substring local d="$1" pref="$2" lf lbl base for lf in "$d"/temp*_label; do [[ -r "$lf" ]] || continue [[ "$(cat "$lf" 2>/dev/null)" == *"$pref"* ]] || continue base="${lf%_label}_input" [[ -r "$base" ]] && { milli_to_c < "$base"; return 0; } done for base in "$d"/temp*_input; do [[ -r "$base" ]] && { milli_to_c < "$base"; return 0; } done return 1 } nvme_temp() { # $1 = disk (e.g. nvme0n1); temp of that specific controller, in °C local ctrl h t ctrl=$(basename "$(readlink -f "/sys/block/$1/device" 2>/dev/null)") for h in /sys/class/hwmon/hwmon*; do [[ "$(cat "$h/name" 2>/dev/null)" == "nvme" ]] || continue [[ "$(basename "$(readlink -f "$h/device" 2>/dev/null)")" == "$ctrl" ]] || continue for t in "$h"/temp*_input; do [[ -r "$t" ]] && { milli_to_c < "$t"; return 0; } done done return 1 } # --- CPU: Thrusters --------------------------------------------------------- cpu_snapshot() { awk '/^cpu /{t=0; for (i=2; i<=NF; i++) t+=$i; print t, $5}' /proc/stat; } read t1 idle1 < <(cpu_snapshot); sleep 0.2; read t2 idle2 < <(cpu_snapshot) dtotal=$(( t2 - t1 )); didle=$(( idle2 - idle1 )); cpu_usage=0 (( dtotal > 0 )) && cpu_usage=$(( ((dtotal - didle) * 100 + dtotal / 2) / dtotal )) cpu_temp=$(hwmon_temp k10temp) || cpu_temp=$(hwmon_temp coretemp) || cpu_temp="" printf 'cpu\tThrusters\t%s\t%s\t%s cores online\n' "$cpu_usage" "$cpu_temp" "$(nproc 2>/dev/null || echo '?')" # --- GPU: Hyperdrive (discrete = the card with the most VRAM) --------------- gpu_dev=""; best_vram=-1 for d in /sys/class/drm/card[0-9]*/device; do [[ -r "$d/gpu_busy_percent" ]] || continue v=$(cat "$d/mem_info_vram_total" 2>/dev/null || echo 0) (( v > best_vram )) && { best_vram=$v; gpu_dev=$d; } done gpu_usage=0; gpu_temp=""; gpu_detail="offline" if [[ -n "$gpu_dev" ]]; then gpu_usage=$(cat "$gpu_dev/gpu_busy_percent" 2>/dev/null || echo 0) for hd in "$gpu_dev"/hwmon/hwmon*; do [[ -d "$hd" ]] && { gpu_temp=$(hwmon_dir_temp "$hd" junction) || gpu_temp=""; break; } done (( best_vram > 0 )) && gpu_detail=$(awk -v b="$best_vram" 'BEGIN{printf "%.0f GiB core", b/1073741824}') fi printf 'gpu\tHyperdrive\t%s\t%s\t%s\n' "$gpu_usage" "$gpu_temp" "$gpu_detail" # --- RAM: Life Support Systems --------------------------------------------- read ram_used ram_total < <(free -b | awk '/^Mem:/{print $3, $2}') ram_usage=0; (( ram_total > 0 )) && ram_usage=$(( (ram_used * 100 + ram_total / 2) / ram_total )) ram_temp=$(hwmon_temp spd5118) || ram_temp=$(hwmon_temp jc42) || ram_temp="" ram_detail=$(awk -v u="$ram_used" -v t="$ram_total" 'BEGIN{printf "%.1f/%.1f GiB", u/1073741824, t/1073741824}') printf 'ram\tLife Support Systems\t%s\t%s\t%s\n' "$ram_usage" "$ram_temp" "$ram_detail" # --- Storage: Cargo Hold (one row per real filesystem, deduped by device) --- declare -A seen while read -r src used size; do [[ "$src" == /dev/* ]] || continue [[ -n "${seen[$src]:-}" ]] && continue seen[$src]=1 dev=$(basename "$src") # e.g. sda1 / nvme0n1p2 disk=$(lsblk -no pkname "$src" 2>/dev/null | head -1) # parent disk, if partitioned [[ -z "$disk" ]] && disk="$dev" (( size > 0 )) || continue usage=$(( (used * 100 + size / 2) / size )) temp=""; [[ "$disk" == nvme* ]] && { temp=$(nvme_temp "$disk") || temp=""; } [[ -z "$temp" ]] && temp=$(hwmon_temp drivetemp 2>/dev/null) || true detail=$(awk -v d="$dev" -v u="$used" -v t="$size" \ 'BEGIN{printf "%-10s %.0f/%.0f GiB", d, u/1073741824, t/1073741824}') printf 'disk\tCargo Hold\t%s\t%s\t%s\n' "$usage" "$temp" "$detail" done < <(df -B1 --output=source,used,size -x tmpfs -x devtmpfs -x overlay -x squashfs 2>/dev/null | tail -n +2)