Two defects let a second daemon start beside a running one, and once
that happened the init script could not stop either of them again.
pid_save() wrote its pid over whatever was in brisk.pid, and pid_remove()
deleted the file without looking at whose pid was in it. So an instance
exiting after another one had taken the file over left the survivor
unrecorded, and from then on "stop" found no pid to kill: every restart
added an orphan instead of replacing it. The orphan was not idle - a
starting daemon unlinks the socket files and binds its own, so it takes
every new connection while the old one stays alive on its own shared
memory, and nothing says so.
brisk.pid is now opened once and held under an exclusive non blocking
lock for the whole life of the daemon. The kernel drops the lock when the
process dies, however it dies, so neither a stale file left by a crash
nor two instances starting in the same instant can get through - and
checking the recorded pid for liveness could not have covered the second
case. A daemon that cannot take the lock says who holds it and exits 3.
pid_remove() only unlinks the file while it still holds the lock.
In the init script the pipe into grep was written "\|", so it never was
a pipe: the daemon was run with "|", "grep" and "IN LOOP" as three extra
arguments, and the loop that restarts it watched the wrong exit status.
Harmless in itself - Sac_a_push::create only looks for -d and --daemon -
but the loop never worked as intended and the junk showed up in ps.
"stop" now also quits our screen sessions. Killing the process alone was
never enough, because each session carries the loop that respawns it; and
a session left behind by a start that was refused would sit there and
grab the daemon at the next stop. The loop also sleeps a second between
attempts, so a daemon that cannot start does not spin.
Checked in the container: stop brings a deliberately dirty state (two
daemons, four screens) back to nothing; three restarts in a row leave
exactly one daemon with a pid file that matches it; a second start is
refused naming the holder; a kill -9 leaves the file behind and the next
start takes it over anyway. 100 players with gzip afterwards: no errors,
stream integrity clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014M1jiEq9cHdE5SE5j6vFuE