Files
Oleksandr ShyshatskyiandClaude Opus 5 d6c07faa9b Measure s6 and runit on the printer before choosing either
anvil-service.sh hand-rolls what a supervisor does: liveness, a stop that
waits, a respawn loop. The question was whether to keep hand-rolling it. This
answers it with measurements from the replica rather than from project pages,
and changes nothing that ships.

There is nothing on the printer to reuse. busybox 1.31.1 is PID 1 and no
supervisor exists anywhere on the rootfs. busybox init does implement respawn,
but only from /etc/inittab, and /etc is inside the read-only squashfs that a
stock flash overwrites -- the one supervisor already on the box cannot be
reached. So both candidates had to be cross-compiled and run.

Two findings cost most of the time, and neither would have surfaced from a
build test:

  * Without -D_FILE_OFFSET_BITS=64 both supervisors die identically the moment
    they scan their own service directory -- readdir() returns EOVERFLOW,
    "Value too large for defined data type", a 32-bit build meeting 64-bit
    inodes. They start, they load, and then they cannot see their services.
  * s6 resolves its helper programs through a prefix baked in at compile time.
    s6-svc -w and s6-svwait exec s6-svlisten, which spawns s6-ftrigrd out of
    libexecdir. Installed anywhere other than the configured prefix, status
    and respawn keep working and every waiting verb fails -- late, and only
    partially. runit's tools find each other on PATH and do not care.

That second one is why build.sh configures --prefix=/usr/data/anvil. The mod's
install root has to behave like a real prefix root -- bin/, lib/, libexec/,
etc/, share/ -- which is also what would let a newer Python and Moonraker
install into the same tree instead of being special-cased.

Both stacks supervise correctly on the printer's own kernel: status answers, a
kill -9 is respawned, and a stop waits for the process to actually be gone.
Either would delete the two bugs anvil-service.sh fixes by hand. The one real
difference is readiness: an s6 service can declare itself ready over a
notification-fd and s6-svwait -U blocks until it does, measured at 5s against a
service that waits 5s. runit's sv start returns when the process is forked,
which says nothing about whether it works. Our boot order is filenames and our
waiting is hand-rolled -- S65camera polls /dev/video0 for 30s, S70klipper
retries an MCU handshake -- so readiness is the part worth having.

Size, built against musl rather than static glibc, which took s6 from 73MB to
3.6MB: the s6 supervision subset is 13 binaries at 813KB plus s6-ftrigrd at
116KB; runit is 5 binaries at 335KB. execline is not needed -- s6 run scripts
can be plain sh.

test/integration/printer/case-supervisor.sh is the gate proving all of the
above on the replica. tools/supervisor/README.md records what neither
supervisor replaces: MOD_* gating, FF_PYTHON, S70klipper's force-start handoff,
and the fact that firmwareExe runs S* in the foreground in filename order --
ordering that adopting s6 would have to re-express as readiness waits. That,
not the supervisor, is the work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 18:22:12 +03:00

9 lines
365 B
Docker

FROM debian:bookworm
RUN apt-get update && apt-get install -y --no-install-recommends \
make curl ca-certificates xz-utils file && \
rm -rf /var/lib/apt/lists/*
RUN curl -sSL https://musl.cc/mipsel-linux-musl-cross.tgz -o /tmp/tc.tgz && \
tar -xzf /tmp/tc.tgz -C /opt && rm /tmp/tc.tgz
ENV PATH=/opt/mipsel-linux-musl-cross/bin:$PATH
WORKDIR /build