Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,9 @@ pytest tests/ -v

## Roadmap

- v1.3: `stop --all` (pgrep-based) to recover a daemon whose pidfile was lost;
desktop notification dispatch; stash staleness
- v1.3 (shipped): `stop --all` (pgrep-based) to recover a daemon whose pidfile
was lost
- next: desktop notification dispatch; stash staleness
- v2: TUI panel (textual/rich) with hotkey drill-down
- v3: cross-machine sync (sqlite); team-shared cache

Expand Down
71 changes: 57 additions & 14 deletions bin/pyauto-pulse
Original file line number Diff line number Diff line change
Expand Up @@ -76,29 +76,72 @@ cmd_live() {
}

help_stop() { cat <<EOF
pyauto-pulse stop
pyauto-pulse stop [--all]

Kill a running pyauto-pulse daemon via its pidfile.

--all Also sweep for any daemon process whose pidfile was lost
(matches 'pulse/daemon.sh' via pgrep) and kill it, then clear
the pidfile. Use this when 'stop' reports "not running" but a
daemon is in fact still alive — e.g. after the pidfile was
deleted out from under a running daemon.
EOF
}

# Kill every pulse daemon process regardless of pidfile state. Used by
# 'stop --all'. Sends TERM, waits briefly, escalates to KILL for stragglers.
# Returns the number of processes it killed.
_pulse_kill_all_daemons() {
local pids killed=0 pid
# -f matches the full command line; the script path is the stable signature.
pids="$(pgrep -f "$PULSE_HOME/pulse/daemon.sh" 2>/dev/null || true)"
[[ -z "$pids" ]] && { echo 0; return 0; }
for pid in $pids; do
kill "$pid" 2>/dev/null && killed=$((killed + 1))
done
# Give them a moment to honour TERM (their EXIT trap clears the pidfile),
# then KILL anything still standing.
sleep 1
for pid in $pids; do
if kill -0 "$pid" 2>/dev/null; then
kill -9 "$pid" 2>/dev/null
fi
done
echo "$killed"
}

cmd_stop() {
if [[ ! -f "$PULSE_PID_FILE" ]]; then
local all=0
[[ "${1:-}" == "--all" ]] && all=1

local stopped_via_pidfile=0
if [[ -f "$PULSE_PID_FILE" ]]; then
local pid
pid="$(cat "$PULSE_PID_FILE")"
if [[ -z "$pid" ]]; then
echo "$(c_warn 'empty pidfile')"
rm -f "$PULSE_PID_FILE"
elif ! kill -0 "$pid" 2>/dev/null; then
echo "$(c_warn "pid $pid not running; cleaning up stale pidfile")"
rm -f "$PULSE_PID_FILE"
else
kill "$pid" && { echo "$(c_ok "stopped pid $pid")"; stopped_via_pidfile=1; }
fi
elif [[ "$all" -eq 0 ]]; then
echo "$(c_meta 'no pidfile — daemon not running')"
return 0
fi
local pid
pid="$(cat "$PULSE_PID_FILE")"
if [[ -z "$pid" ]]; then
echo "$(c_warn 'empty pidfile')"
rm -f "$PULSE_PID_FILE"
return 0
echo "$(c_meta '(use `pyauto-pulse stop --all` to sweep for an orphan daemon)')"
fi
if ! kill -0 "$pid" 2>/dev/null; then
echo "$(c_warn "pid $pid not running; cleaning up stale pidfile")"

if [[ "$all" -eq 1 ]]; then
local killed
killed="$(_pulse_kill_all_daemons)"
rm -f "$PULSE_PID_FILE"
return 0
if [[ "$killed" -gt 0 ]]; then
echo "$(c_ok "swept $killed daemon process(es)")"
elif [[ "$stopped_via_pidfile" -eq 0 ]]; then
echo "$(c_meta 'no daemon processes found')"
fi
fi
kill "$pid" && echo "$(c_ok "stopped pid $pid")"
}

help_tick() { cat <<EOF
Expand Down