majestic: match the daemon by executable, not by a stale pidfile - #2324
Merged
Conversation
Given -p, busybox start-stop-daemon inspects only the pid that file holds and never scans /proc, so the pidfile is the sole liveness oracle -- and it goes stale on its own. Measured on a Hi3516EV300 (busybox 1.36.1) with majestic live as pid 1177 and the pidfile poisoned with a dead pid: start-stop-daemon -b -m -S -q -p $PIDFILE -x majestic -- -s returned 0 and started a SECOND majestic (pid 1233). The duplicate died four seconds later on the busy sensor HAL, but -m had already written 1233 into the pidfile, so every later start re-poisoned it. That one mechanism explains both reported symptoms: a pidfile full of pids that were never a usable daemon, and two daemons fighting over the encoder while config edits appear to apply to neither. stop() was worse. It passed -p with no -x, so it signalled whatever now owned that pid: pointed at an unrelated live process, it killed that process and left majestic running. Drop the pidfile and match on -x /usr/bin/majestic, which scans every process. -a keeps argv[0] as "majestic", so ps output, pidof, killall and bare-name matchers all behave exactly as before. start is now idempotent -- a second start is refused and reports success, leaving the running daemon untouched -- which makes the script safe to call unconditionally from a crond watchdog. stop polls until the daemon is really gone before restart proceeds, and reload signals through the same matcher instead of killall. Nothing in the tree reads /var/run/majestic.pid; stop removes a leftover so an older file cannot mislead. Verified on a Hi3516EV300: stale pidfile no longer spawns a duplicate, stop no longer touches an unrelated process, stop/start/restart/reload each behave across nine cycles, kill -9 followed by an unconditional start recovers, and a cold boot brings up exactly one daemon with RTSP and HTTP listening. Co-authored-by: zavaruev <29773394+zavaruev@users.noreply.github.com> Co-authored-by: Adrian Fedoreanu <1611001+phedoreanu@users.noreply.github.com>
This was referenced Aug 29, 2026
Closed
PR Summary by QodoMajestic: match the daemon by executable instead of stale pidfiles
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTip of the day💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #2245 and #2250. Both reported the same failure from opposite ends and both diagnoses were close but not quite right, so I reproduced the whole thing on a Hi3516EV300 (busybox 1.36.1) and traced it to one flag.
Root cause:
-pBusybox
start-stop-daemon'sdo_procinit()reads only the pid the pidfile holds and never scans/proc.-xis then just a filter applied to that single pid. So the moment the pidfile is stale,start-stop-daemoncannot see the live daemon at all.With majestic live as pid 1177 and the pidfile poisoned with a dead pid:
The second instance dies on the busy sensor HAL, but
-mhas already overwritten the pidfile with its pid. Every laterstartre-poisons the file, so the state is self-perpetuating.That single mechanism explains both reports without needing any majestic-internal watchdog:
stop()was worse. It passed-pwith no-x, so it signalled whatever process now owned that pid. Pointed at an unrelated live process, it killed that process and left majestic running:Two corrections to #2250's reasoning
-x majestic(bare name) is not the problem. Busybox compares/proc/PID/cmdlineargv[0] as well as the/proc/PID/exesymlink, and I measured the bare form matching correctly (rc=0). The absolute path is not what fixes detection — dropping-pis.-pwithout-ais a silent compat regression. argv[0] becomes/usr/bin/majestic,psoutput changes, and bare-namestart-stop-daemon -x majesticmatchers start returning rc=1. Adding-a majestickeeps argv[0] asmajesticwhile-x /usr/bin/majesticstill matches through the exe symlink, so nothing observable changes.The fix
-x /usr/bin/majesticscans every process, and-a majestickeeps the identity stable:startis idempotent. A second start is refused, reports success, and leaves the running daemon untouched — so the script is safe to call unconditionally from a crond watchdog (* * * * * /etc/init.d/S95majestic start), with nopgrep/pidofpre-check that can itself go stale on a zombie.stoppolls until the daemon is genuinely gone (measured: 1 s) instead of sleeping a fixed 4 s, and returns non-zero if it survives, sorestartaborts rather than stacking a second instance.reloadsignals through the same matcher instead ofkillall.Nothing in the tree reads
/var/run/majestic.pid, so removing it is safe;stopdeletes a leftover file so it cannot mislead anyone debugging.Verification (Hi3516EV300 + IMX335, busybox 1.36.1, kernel 4.9.37)
sh -nunder busybox ashstopkills the daemon and clears the pidfilestopwhen already stoppedstart— argv[0] staysmajesticps:majestic -sstartwhile runningstartstoprestartfrom stoppedrestartwhile runningreloadkill -9+ unconditional watchdog tickpidof/killall/ps/ bare-x majesticCold boot on the fixed script: exactly one majestic,
argv[0]=majestic, no pidfile,:554and:80listening, and the/etc/TZre-read from #2244 still propagates (TZ=GMT0).Follow-up
Eleven other init scripts carry the same
-b -m -S -p/-K -ppattern and therefore the same latent duplicate-spawn and wrong-pid-kill defects:S60crond,S01syslogd,S49ntpd,S50dropbear,S96onvifserver,S89edge,S97baresip,S50mdnsd,S60siproxd,S60precision-time,S90matter. They bite far less often because those daemons rarely die and get restarted. Filed separately rather than widening this PR's blast radius across every board's boot path.Credit to @zavaruev (#2250) for the pidfile-as-stale-oracle diagnosis and the watchdog-safety framing, and to @phedoreanu (#2245) for spotting the duplicate-daemon symptom and insisting
stopverify the daemon is actually gone.