#!/bin/sh
# DF-2928 — sys/kern/kern_spinlock.c: dead spin_lock_test_mode makes
# debug.spin_lock_test=1 spin 60s in a critical section and panic
# INVARIANTS kernels instead of breaking the wait as documented.
#
# Run as root on the target DragonFlyBSD (INVARIANTS kernel).
# Usage: ./trigger.sh          (blocks ~65s; the kernel panics at 60s)
#        ./trigger.sh poll     (read the live msgbuf mid-spin)
#
# Unprivileged users are refused (verified):
#   $ sysctl -w debug.spin_lock_test=1
#   sysctl: debug.spin_lock_test=1: Operation not permitted

set -eu

if [ "$(id -u)" -ne 0 ]; then
    echo "must be root (caps_priv_check_self(SYSCAP_RESTRICTEDROOT))" >&2
    exit 1
fi

if ! sysctl -n debug.spin_lock_test >/dev/null 2>&1; then
    echo "kernel lacks INVARIANTS; sysctl_spin_lock_test not compiled" >&2
    exit 1
fi

case "${1:-fire}" in
poll)
    # While the (backgrounded) fire run spins, the msgbuf accumulates
    # one "indefinite wait (N secs)!" report per second and never breaks.
    date
    dmesg | grep -E "indefinite|spin_lock" | tail -20
    ;;
fire)
    date
    # The sysctl handler (kern_spinlock.c:417-425) sets the DEAD static
    # spin_lock_test_mode before the recursive spin_lock, then blocks
    # in _spin_lock_contested -> indefinite_check(), which only honors
    # kern_lock.c's lock_test_mode.  After 60 seconds:
    #   panic("spin_lock_ex: %s, indefinite wait!")   [indefinite2.h:185]
    sysctl -w debug.spin_lock_test=1
    ;;
detach)
    # fire, but in the background so the caller can poll
    (sysctl -w debug.spin_lock_test=1 >/tmp/df2928.log 2>&1 &)
    date; echo launched
    ;;
esac
