session-tools/bin/disk-guard

82 lines
2.9 KiB
Text
Raw Normal View History

#!/bin/sh
# disk-guard [--warn PCT] [--crit PCT] [--volume PATH] [--quiet]
#
# Check free space on the data volume and raise a macOS notification when it
# runs low. Meant to run unattended (launchd), but safe to run by hand.
#
# Unlike `disk-reclaim` (which only reports and is read-only), this is the
# *alarm*: it makes low disk visible BEFORE the volume hits ~97% full and
# things start failing. It never deletes anything.
#
# Flags:
# --warn PCT warn when free space drops below PCT percent (default 15)
# --crit PCT critical when free space drops below PCT percent (default 7)
# --volume PATH volume to check (default /System/Volumes/Data)
# --quiet no stdout; only notify + log (for launchd)
#
# Exit status: 0 ok, 1 warn, 2 critical. Always logs to
# ~/Library/Logs/disk-reclaim.log so boot snapshots and alarms share a trail.
set -eu
warn_pct=15
crit_pct=7
volume=/System/Volumes/Data
quiet=0
die() { echo "disk-guard: $*" >&2; exit 64; }
while [ $# -gt 0 ]; do
case "$1" in
-h|--help) sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
--warn) [ $# -ge 2 ] || die "--warn needs a value"; warn_pct=$2; shift 2 ;;
--crit) [ $# -ge 2 ] || die "--crit needs a value"; crit_pct=$2; shift 2 ;;
--volume) [ $# -ge 2 ] || die "--volume needs a value"; volume=$2; shift 2 ;;
--quiet) quiet=1; shift ;;
*) die "unknown arg: $1" ;;
esac
done
# df -k: portable. Columns: Filesystem 1024-blocks Used Available Capacity ...
# Available is field 4, in KiB. Compute free percent ourselves (Capacity is
# "used %" and rounds oddly on APFS shared containers).
read -r avail_kb total_kb <<EOF
$(df -k "$volume" | awk 'NR==2 {print $4, $2}')
EOF
[ -n "${total_kb:-}" ] && [ "$total_kb" -gt 0 ] || die "could not read df for $volume"
free_pct=$(( avail_kb * 100 / total_kb ))
avail_gb=$(awk -v k="$avail_kb" 'BEGIN { printf "%.1f", k/1048576 }')
log="$HOME/Library/Logs/disk-reclaim.log"
mkdir -p "$(dirname "$log")"
stamp=$(date '+%Y-%m-%d %H:%M:%S %z')
notify() {
# Best-effort macOS banner; ignore failure (e.g. headless/ssh).
osascript -e "display notification \"$2\" with title \"$1\" sound name \"Basso\"" \
>/dev/null 2>&1 || true
}
if [ "$free_pct" -lt "$crit_pct" ]; then
level=CRITICAL; rc=2
notify "Disk critically full" "${avail_gb}G free (${free_pct}%) on $volume. Run: disk-reclaim"
elif [ "$free_pct" -lt "$warn_pct" ]; then
level=WARN; rc=1
notify "Disk getting full" "${avail_gb}G free (${free_pct}%) on $volume. Run: disk-reclaim"
else
level=OK; rc=0
fi
echo "=== $stamp (guard) $level: ${avail_gb}G free (${free_pct}%) on $volume ===" >> "$log"
# On WARN/CRIT, append a reclaim snapshot so the log shows what to delete.
if [ "$rc" -ne 0 ]; then
script_dir=$(cd "$(dirname "$0")" && pwd -P)
"$script_dir/disk-reclaim" "$HOME" --min 1G >> "$log" 2>&1 || true
fi
[ "$quiet" = 1 ] || echo "$level: ${avail_gb}G free (${free_pct}%) on $volume"
exit "$rc"