#!/usr/bin/env python3
"""DF-5369 attempt: open/close-contention deadlock probe.

Root loops open()/close() of the ums evdev node (each open runs
evdev_register_client under ev_mtx -> ums_ev_open -> sc_lock) while the
mouse streams reports (intr callback holds sc_lock -> EVDEV_ENTER takes
ev_mtx).  An AB-BA hit wedges the opener unkillably and progress stalls.
"""
import os, sys, time
sys.path.insert(0, "/home/maxx/dfbsd/dfbsd/findings/poc/DF-5368")
from umsrace import Mon, Spam, guest_run, guest_ok, evdev_node

UMS_KO = os.environ.get("UMS_KO", "/boot/kernel/ums.ko")

def main():
    m = Mon()
    guest_run("kldunload ums 2>/dev/null; sleep 0.5; "
              "kldload %s; sleep 2" % UMS_KO)
    node = evdev_node()
    print("node:", node, flush=True)
    script = "\n".join([
        "#!/bin/sh",
        "i=0",
        "end=$(( $(date +%s) + 150 ))",
        "while [ $(date +%s) -lt $end ]; do",
        "  exec 3<%s && exec 3<&-" % node,
        "  i=$((i+1))",
        '  if [ $((i % 200)) -eq 0 ]; then echo $i > /tmp/oc.count; fi',
        "done",
        "echo $i > /tmp/oc.count",
        ""])
    heredoc = "cat > /tmp/ocloop.sh <<'XEOF'\n" + script + "XEOF\nchmod +x /tmp/ocloop.sh\n"
    guest_run(heredoc)
    print(guest_run("rm -f /tmp/oc.count; /tmp/ocloop.sh </dev/null "
                    ">/dev/null 2>&1 & echo loop-started").stdout.strip())
    sp = Spam(m)
    sp.start()
    for tick in range(28):
        time.sleep(5)
        r = guest_run("cat /tmp/oc.count 2>/dev/null").stdout.strip()
        print("t=%2ds opens=%s" % ((tick + 1) * 5, r or "none"), flush=True)
        if not guest_ok():
            print("GUEST DOWN during contention")
            break
    sp.run_flag = False
    sp.join(3)
    r = guest_run("P=$(pgrep -f ocloop.sh); echo pids=$P; kill -9 $P 2>/dev/null; "
                  "sleep 1; pgrep -f ocloop.sh || echo loop-gone")
    print("kill check:", r.stdout.strip())
    print("alive:", guest_ok())

if __name__ == "__main__":
    main()
