#!/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 </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"