#!/usr/bin/env bash
# Proves pids-rescue recovers a genuinely wedged cgroup, in the danger lab only.
#
# DANGEROUS: this script saturates a cgroup's pids.max until fork() fails inside it.
# Host: /home/user/dangerlab/dangerlab-run --payload <repo>/modules/monitor \
#   --timeout 240 -- bash monitor/tests/pids-rescue.dangerlab.sh
#
# What it proves:
#   1. the wedge is real — fork() inside the scope returns EAGAIN
#   2. a named survivor is moved out and stays alive after the scope is killed
#   3. the wedge is gone
#   4. a human-owned target is refused
set -uo pipefail

[[ "$(hostname)" == dangerlab ]] || {
  echo "REFUSED: this script exhausts a pids cap; it runs in the danger lab only." >&2
  exit 78
}

HERE="$(cd "$(dirname "$0")" && pwd)"
RESCUE="$HERE/../bin/pids-rescue"
PASS=0; FAIL=0
ok()  { PASS=$((PASS+1)); printf 'PASS %s\n' "$1"; }
bad() { FAIL=$((FAIL+1)); printf 'FAIL %s\n     %s\n' "$1" "$2"; }

UID_NUM="$(id -u)"
CG=/sys/fs/cgroup/user.slice/user-${UID_NUM}.slice/user@${UID_NUM}.service
UNIT=confine-agent-9-9.scope
SCOPE="$CG/agent.slice/$UNIT"
CAP=128

cur() { cat "$1/pids.current" 2>/dev/null || echo -1; }

# A survivor with a recognisable name, plus enough sleepers to saturate the cap.
systemd-run --user --scope --quiet --collect --unit="$UNIT" --slice=agent.slice \
  -p TasksMax="$CAP" -- bash -c \
  'cp /usr/bin/sleep /tmp/claude 2>/dev/null; /tmp/claude 600 & for i in $(seq 1 200); do sleep 600 & done; wait' &
disown
for _ in $(seq 1 40); do [[ "$(cur "$SCOPE")" -ge "$CAP" ]] && break; sleep 0.25; done

if [[ "$(cur "$SCOPE")" -ge "$CAP" ]] && awk '/^max /{exit !($2 > 0)}' "$SCOPE/pids.events.local"; then
  ok "wedge: $(cur "$SCOPE")/$CAP tasks, $(awk '/^max /{print $2}' "$SCOPE/pids.events.local") forks already denied"
else
  bad "wedge: cgroup saturated" "cur=$(cur "$SCOPE") events=$(cat "$SCOPE/pids.events.local" 2>/dev/null | tr '\n' ' ')"
  exit 1
fi

SURVIVOR=$(for p in $(cat "$SCOPE/cgroup.procs"); do
  [[ "$(cat /proc/$p/comm 2>/dev/null)" == claude ]] && echo "$p" && break
done)
[[ -n "$SURVIVOR" ]] && ok "wedge: survivor pid $SURVIVOR is inside it" || { bad "wedge: survivor present" "none found"; exit 1; }

out=$(python3 "$RESCUE" "$CG/human.slice" 2>&1); rc=$?
if (( rc == 3 )) && [[ "$out" == *refusing* ]]; then
  ok "refusal: a human.slice target is rejected (rc=3)"
else
  bad "refusal: human.slice target" "rc=$rc out=${out:0:200}"
fi

out=$(python3 "$RESCUE" "$SCOPE" --keep-comm claude --dry-run 2>&1); rc=$?
if (( rc == 0 )) && [[ "$out" == *"1 to evacuate"* && "$out" == *"wedged   True"* ]]; then
  ok "dry-run: reports the wedge and the survivor without touching either"
else
  bad "dry-run" "rc=$rc out=${out:0:300}"
fi

out=$(python3 "$RESCUE" "$SCOPE" --keep-comm claude 2>&1); rc=$?
if (( rc == 0 )) && [[ "$out" == *"moved    1 pids"* ]]; then
  ok "rescue: $(grep -m1 moved <<<"$out" | tr -s ' ')"
else
  bad "rescue: survivor moved and scope killed" "rc=$rc out=${out:0:300}"
fi

sleep 1
kill -0 "$SURVIVOR" 2>/dev/null \
  && ok "rescue: survivor $SURVIVOR still alive after the wedge was killed" \
  || bad "rescue: survivor survives" "pid $SURVIVOR is gone — the rescue killed what it was asked to save"

n=$(cur "$SCOPE")
(( n <= 1 )) && ok "rescue: wedged scope emptied (now $n tasks)" || bad "rescue: wedge cleared" "still $n tasks"

RESCUE_CG=$(awk -F: '{print $3}' /proc/$SURVIVOR/cgroup 2>/dev/null)
[[ "$RESCUE_CG" == *pids-rescue-* ]] \
  && ok "rescue: survivor now lives in $RESCUE_CG" \
  || bad "rescue: survivor reparented" "cgroup is $RESCUE_CG"

kill "$SURVIVOR" 2>/dev/null
wait 2>/dev/null
printf 'pids-rescue: pass=%d fail=%d\n' "$PASS" "$FAIL"
(( FAIL == 0 ))
