mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 00:53:38 +02:00
35 lines
1.7 KiB
Python
35 lines
1.7 KiB
Python
"""Linux-only E2E fault injection. pidfd pins the process across PID reuse."""
|
|
import json
|
|
import os
|
|
import signal
|
|
import sys
|
|
|
|
mode, raw_pid, run_id, *expected = sys.argv[1:]
|
|
pid = int(raw_pid)
|
|
if sys.platform != "linux" or not hasattr(os, "pidfd_open") or not hasattr(signal, "pidfd_send_signal"):
|
|
raise RuntimeError("Worker-crash qualification requires Linux pidfd support")
|
|
if pid <= 1 or pid == os.getpid() or mode not in ("inspect", "kill"):
|
|
raise RuntimeError("Invalid fault target")
|
|
fd = os.pidfd_open(pid)
|
|
try:
|
|
# Open the process handle before reading /proc. If the numeric PID is reused
|
|
# during these reads, fdinfo below no longer names it. Sending via the handle
|
|
# can only target the original process, never its replacement.
|
|
with open(f"/proc/{pid}/stat") as source:
|
|
start_ticks = source.read().rsplit(")", 1)[1].split()[19]
|
|
with open(f"/proc/{pid}/cmdline", "rb") as source:
|
|
args = source.read().decode().rstrip("\0").split("\0")
|
|
if not any(args[i:i + 2] == ["--run-id", run_id] for i in range(len(args) - 1)):
|
|
raise RuntimeError("Worker run ID does not match")
|
|
with open(f"/proc/self/fdinfo/{fd}") as source:
|
|
fields = dict(line.split(":", 1) for line in source if ":" in line)
|
|
if int(fields.get("Pid", "-1").strip()) != pid:
|
|
raise RuntimeError("Worker exited during identity check")
|
|
if mode == "kill":
|
|
if expected != [start_ticks]:
|
|
raise RuntimeError("Worker start identity changed")
|
|
signal.pidfd_send_signal(fd, signal.SIGKILL, None, 0)
|
|
print(json.dumps({"pid": pid, "startTicks": start_ticks, "runId": run_id, "signalled": mode == "kill"}))
|
|
finally:
|
|
os.close(fd)
|