mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
fix(process): the supervisor's TERM-to-KILL grace no longer depends on PATH
Review (nit 1): `sleep` is not a dash builtin, so with an unusable PATH (`sleep: not found`) the supervisor's three one-second waits returned at once and the KILL followed the TERM at +0 s, cutting short a child's TERM cleanup. The wait now runs `command -p sleep 1` (the shell's default PATH), falling back to a plain `sleep 1` on a shell without `command -p`. Test: with PATH=/nonexistent a TERM-ignoring group is still alive 1.5 s after the EOF and is then KILLed; fails with the plain `sleep`.
This commit is contained in:
1 parent
782c0f847c
commit
e883b5a4d5
2 files changed
+26
-1
No files matched your search
@@ -49,7 +49,8 @@ for _ in 1 2 3; do
|
||||
for g in $reg; do kill -0 -"$g" 2>/dev/null && live="$live $g"; done
|
||||
reg=$live
|
||||
[ -n "$reg" ] || exit 0
|
||||
sleep 1
|
||||
# The default PATH: a broken PATH must not turn the TERM grace into an instant KILL.
|
||||
command -p sleep 1 2>/dev/null || sleep 1
|
||||
done
|
||||
for g in $reg; do kill -KILL -"$g" 2>/dev/null; done
|
||||
exit 0
|
||||
@@ -203,6 +203,30 @@ def test_escalates_to_sigkill_for_a_group_that_ignores_sigterm():
|
||||
supervisor.wait(timeout=10)
|
||||
|
||||
|
||||
def test_the_term_grace_holds_even_with_a_broken_path():
|
||||
"""``sleep`` is not a dash builtin: with PATH unusable the grace loop skipped its sleeps and the
|
||||
KILL landed at once, cutting short a child's TERM cleanup."""
|
||||
victim = subprocess.Popen(
|
||||
[sys.executable, "-c",
|
||||
"import signal, time; signal.signal(signal.SIGTERM, signal.SIG_IGN); print('armed', flush=True); time.sleep(300)"],
|
||||
stdout=subprocess.PIPE, text=True, start_new_session=True,
|
||||
)
|
||||
supervisor = subprocess.Popen(_supervisor_argv(os.getpgid(0)), stdin=subprocess.PIPE, text=True,
|
||||
start_new_session=True, env={"PATH": "/nonexistent"})
|
||||
try:
|
||||
assert victim.stdout.readline().strip() == "armed"
|
||||
supervisor.stdin.write(f"register {victim.pid}\n")
|
||||
supervisor.stdin.close()
|
||||
closed = time.monotonic()
|
||||
assert _wait_exited(victim), "a SIGTERM-ignoring group survived the reap"
|
||||
assert time.monotonic() - closed >= 1.5, "the KILL skipped the TERM grace"
|
||||
finally:
|
||||
_kill(victim.pid)
|
||||
_kill(supervisor.pid)
|
||||
victim.wait(timeout=10)
|
||||
supervisor.wait(timeout=10)
|
||||
|
||||
|
||||
def test_leaves_an_unregistered_group_alone_at_eof():
|
||||
# The other failure direction, and the more damaging one: a clean Hermes
|
||||
# shutdown unregisters as it tears each child down, so EOF must not become
|
||||
|
||||
Reference in new issue
Block a user