bxcore::instance::lock (File::try_lock, file 0600, missing directory 0700), LockError::line for the two lines the daemons print, and the already-running runbook fragment; loopd locks <home>/run/loopd.lock before its socket and self-test, inferproxy <socket>.lock. Checks: given tests fail at ref 11 as the task says (bxcore E0432; loopd 3 and inferproxy 3 fail); the task's quoted edits applied to ref 11 reproduce ref 12 byte for byte; replay gate ok with 1113 tests (1099 + 14), /tmp unchanged; 25 mutations, 24 caught (the uncaught one maps TryLockError::Error to Held, not reachable from a test); given files rustfmt-clean, under 500 lines; no chat-template tags. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2.6 KiB
already-running
What you see. A daemon exits 1 at start, before it touches its sockets, with one of these lines:
<role>: another <role> is running (it holds <lock file>); see docs/runbook.md#already-running<role>: cannot take the lock <lock file>: <error>; see docs/runbook.md#already-running
<role> is loopd, brokerd, memoryd, gatewayd or inferproxy. The lock file is
$BOXMAKER_HOME/run/<role>.lock; for inferproxy it is its --listen socket's path with
.lock added (infer.sock.lock, beside infer.sock).
Why. Every daemon removes a socket it finds at its path before it binds, so that a socket left by a daemon that was killed never blocks the next start. A second copy started by mistake would therefore take the sockets of the one that runs, which would keep running, unreachable, and nothing would say so. So each daemon first takes an exclusive lock on its lock file, and holds it for as long as it runs (M5b spec, section 9a). The system releases the lock when the process ends, however it ends, so a killed daemon never leaves a lock behind. The file stays in place and holds nothing.
Confirm. For another <role> is running, find the copy that runs, with its command line
(the --config path says which home it serves):
pgrep -a -x <role>
ls -l "$BOXMAKER_HOME/run/<role>.lock"
For cannot take the lock, the error says why: Permission denied means the daemon's user
cannot create or open the file, or create run/ (ls -ld "$BOXMAKER_HOME" "$BOXMAKER_HOME/run"
and ls -l on the lock file show who owns them); Not a directory means a path above it is a
file; Read-only file system means what it says.
Fix. Two copies of a role against the same home are always a mistake: use the one that runs.
To restart it, stop it (pkill -x <role>, or Ctrl-C in its terminal), wait until pgrep -x <role> prints nothing, and start it again. Two copies with different homes are fine (each has
its own lock), as long as their configs do not name the same socket path. For a lock that cannot
be taken, give the daemon's user the directory (chown), or point its home somewhere it can
write.
Never delete a lock file while its daemon runs: the lock is held on the open file, so a new copy would create a new file, lock that, and take the sockets. Deleting it while the daemon is stopped does no harm, and does not help either.
Check. pgrep -x <role> prints one process id, and the daemon prints its start line
(loopd: serving on …, inferproxy: <socket> -> <upstream>, brokerd: serving tools on …,
memoryd: serving, gatewayd: connected to …).