80 lines
3.2 KiB
Bash
80 lines
3.2 KiB
Bash
# Read-only probe of the box, as the unprivileged user (`ssh $HOST`).
|
|
#
|
|
# host.sh sends THIS FILE, verbatim, over `ssh $HOST bash -s`, and prints this
|
|
# file, verbatim, when asked what it would run. The commands you read are the
|
|
# commands that run — there is no second copy to drift from.
|
|
#
|
|
# Never read here, because a snapshot is written to disk on another machine:
|
|
# - container environments (bare `docker inspect` prints Env — every
|
|
# inspect below names the fields it prints)
|
|
# - compose files and .env (inline secrets)
|
|
# - container logs (tokens and credentials leak into logs)
|
|
# No sudo. What needs root is probe-admin.sh, a separately announced batch.
|
|
set -u
|
|
section() { printf '\n### %s\n' "$1"; }
|
|
# Every step that walks a tree is bounded. `du /` takes ~20s on this box and
|
|
# prints nothing until it finishes (its output goes through sort), so a slow or
|
|
# sick disk looks exactly like a hang. A step that times out says so and the
|
|
# probe moves on: a partial answer beats none.
|
|
bounded() { local t="$1"; shift; timeout "$t" "$@"; rc=$?; [ $rc = 124 ] && echo "(timed out after ${t}s: $*)"; return 0; }
|
|
REAL='-x tmpfs -x devtmpfs -x overlay -x squashfs -x efivarfs'
|
|
|
|
section meta
|
|
date -u +%Y-%m-%dT%H:%M:%SZ
|
|
hostname
|
|
uname -r
|
|
uptime
|
|
|
|
section df
|
|
df -P -B1M $REAL 2>/dev/null
|
|
|
|
section df_inodes
|
|
df -P -i $REAL 2>/dev/null
|
|
|
|
section meminfo
|
|
cat /proc/meminfo
|
|
|
|
section containers
|
|
docker ps -a --format '{{.Names}}\t{{.State}}\t{{.Status}}\t{{.Image}}' 2>&1
|
|
|
|
section container_config
|
|
ids=$(docker ps -aq 2>/dev/null)
|
|
[ -n "$ids" ] && docker inspect --format '{{.Id}} {{.Name}} {{.RestartCount}} {{.HostConfig.RestartPolicy.Name}} {{.HostConfig.LogConfig.Type}} {{index .HostConfig.LogConfig.Config "max-size"}} {{index .Config.Labels "com.docker.compose.project.working_dir"}}' $ids 2>&1
|
|
|
|
section docker_df
|
|
docker system df 2>&1
|
|
|
|
section docker_df_verbose
|
|
bounded 30 docker system df -v 2>&1
|
|
|
|
section daemon_json
|
|
cat /etc/docker/daemon.json 2>/dev/null || echo '(absent)'
|
|
|
|
section journal
|
|
journalctl --disk-usage 2>&1
|
|
|
|
section du_home
|
|
bounded 30 du -xm --max-depth=3 "$HOME" 2>/dev/null | sort -rn | head -40
|
|
|
|
section du_root
|
|
# Unprivileged, so /var/lib/docker is absent — probe-admin.sh sizes that. What it
|
|
# does see matters anyway: the OS under /usr, and /var/log (779 MB, 2026-09-16).
|
|
bounded 90 du -xm --max-depth=2 / 2>/dev/null | sort -rn | head -40
|
|
|
|
section registry_repos
|
|
# The registry answers /v2/ anonymously (curl -> 200, 2026-09-14), so what is
|
|
# stored in it is a question in its own right, not bookkeeping.
|
|
wd=$(docker inspect --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}' registry 2>/dev/null)
|
|
r="${wd:-$HOME/ppl/gateway}/registry-data/docker/registry/v2/repositories"
|
|
if [ -d "$r" ]; then
|
|
timeout 30 find "$r" -type d -name _manifests -prune -print 2>/dev/null | while read -r m; do
|
|
repo=${m%/_manifests}; repo=${repo#"$r"/}
|
|
newest=$(find "$m" -type f -printf '%TY-%Tm-%Td\n' 2>/dev/null | sort | tail -1)
|
|
tags=$(ls "$m/tags" 2>/dev/null | wc -l)
|
|
printf '%s\t%s\t%s\n' "${newest:-?}" "$tags" "$repo"
|
|
done | sort
|
|
printf 'blobs_mb\t%s\n' "$(du -sm "${r%/repositories}/blobs" 2>/dev/null | cut -f1)"
|
|
else
|
|
echo "(not found at $r)"
|
|
fi
|