Skip to content

fix: make stop no longer leaves orphaned sysmon processes running - #5

Merged
dezween merged 5 commits into
mainfrom
gsd/quick-260826-pn8-make-stop-orphans
Aug 28, 2026
Merged

dezween merged 5 commits into
mainfrom
gsd/quick-260826-pn8-make-stop-orphans

Conversation

@dezween

@dezween dezween commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Problem

make stop reported "sysmon is not running" while a sysmon process kept running. Observed live: PID 70263, alive since Aug 19, 2:44 of accumulated CPU, with no /tmp/sysmon.pid present. It had to be killed by hand.

stop and status derived liveness solely from $(PIDFILE). The pidfile desyncs from reality in two ways:

  1. Double make start — the second start overwrites the pidfile with its own PID, orphaning the first instance permanently.
  2. make clean — deleted the pidfile without killing anything.

stop also ran rm -f $(PIDFILE) unconditionally, even when no kill happened.

Fix

Process liveness becomes the source of truth; the pidfile is demoted to a label that is trusted only when a live scan corroborates it. Makefile is the only file changed.

  • PGREP helper — pgrep -x -U $(id -u) sysmon, the single liveness source for start/stop/status.
  • stop — kills by pidfile when valid, falls back to the live scan when the pidfile is missing or stale, confirms termination (10 polls × 0.2s), and removes the pidfile only after a confirmed stop. A failed kill exits non-zero and leaves the pidfile in place.
  • start — refuses with a non-zero exit when any live instance exists, instead of spawning the next orphan.
  • clean — declares stop as a prerequisite, so it can never strand a running process.
  • status — reports running-and-tracked, running-but-UNTRACKED, stale-pidfile, and not-running as distinct states, and warns when a tracked instance coexists with an orphan.

Match precision (important)

The obvious pkill -f sysmon would have been dangerous: on macOS, pgrep -f sysmon matches the system daemon /usr/libexec/sysmond (verified — PID 92862 on the dev machine). This PR uses -x (exact process name, so the trailing d excludes sysmond) and -U (current user only), and never uses -f or pkill.

Two related hardening points:

  • The pidfile PID is honoured only when it is a member of the live-scan list — otherwise a recycled PID in a stale pidfile could send SIGTERM to an unrelated process, a worse bug than the one being fixed.
  • stop kills every PID in the scan, not just the tracked one, so a tracked instance plus stray orphans are cleared in one pass.

Verification

Full manual sequence run on macOS 26 / arm64, all passing:

# Step Result
1 make start started (PID 98220)
2 make status running, tracked (PID 98220)
3 second make start refused, exit≠0, still exactly one PID
4 make stop stopping tracked PID → stopped
5 make status not running, pidfile gone
6 start + rm pidfile running, UNTRACKED (PID(s) 98367)
7 make stop ← the original bug pidfile missing or stale -- stopping PID(s) found by live scan: 98367 → stopped
8 make status / pgrep not running / empty
9 start + make clean stop output precedes cleaned, nothing left

Edge cases also verified: stale pidfile with nothing running (cleared, exit 0); tracked instance coexisting with an orphan (status warns, stop clears both); sysmond never matched by the Makefile's scan.

Note on project state

This supersedes the recorded decision in .planning/STATE.md — "pidfile-based start/stop in Makefile retained as-is (not touched by this refactor)". The pidfile is retained, but it is no longer the sole liveness signal. STATE.md is updated accordingly.

🤖 Generated with Claude Code

dezween and others added 5 commits August 26, 2026 18:35
…rphaned processes

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Add PGREP helper (`pgrep -x -U $(id -u) sysmon`) as the single liveness
  source of truth, avoiding false matches on /usr/libexec/sysmond or other
  users' processes.
- Rewrite `stop` to scan for live processes instead of trusting only the
  pidfile: kills every live PID, confirms termination with a bounded
  retry loop, and only removes the pidfile after a confirmed stop or when
  nothing was running.
- Chain `clean: stop` so cleanup can never strand a running process.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… status

- `start` now checks both the tracked pidfile PID and a fresh live scan
  before launching, refusing with a non-zero exit if a sysmon instance
  is already live (tracked or untracked) instead of silently orphaning it.
- `status` reports four distinct states -- not running, stale pidfile,
  running+tracked (with an extra warning if untracked orphans coexist),
  and running+UNTRACKED -- always exiting 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Records that quick task 260826-pn8 supersedes the decision
'pidfile-based start/stop in Makefile retained as-is'.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dezween
dezween merged commit 9233431 into main Aug 28, 2026
0 of 3 checks passed
@dezween
dezween deleted the gsd/quick-260826-pn8-make-stop-orphans branch August 28, 2026 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant