A fresh classroom needed two things installed by hand on every box:
Google Chrome, which is not in Debian, and the DVD codecs, which Debian
cannot ship. Both now happen on their own.
ekklesia-classroom-provision.service runs after every boot and does the
work that needs the network and the apt lock, neither of which a package
postinst can have. Each step returns immediately if it is already done,
so a provisioned machine costs nothing and a machine that was offline
the first time gets another go at the next boot. Nothing there is
allowed to fail the boot: a classroom with no DVD codecs is still a
working classroom, and it says so in the log rather than silently.
The postinst enables the service but deliberately does not start it.
Every OTA update reboots the moment the install finishes, and an apt run
killed by that reboot would leave dpkg half-configured.
Chrome comes from Google's own repository rather than a one-off .deb, so
it keeps getting security updates. Google's package migrates whatever we
write into a deb822 file of its own, so the repository is looked for
across every source file instead of only the one we would write;
otherwise a machine that had Chrome removed would end up with two copies
of the same repository.
The DVD codec helper previously assumed deb822 sources and did nothing
on a machine using a classic /etc/apt/sources.list, which is what the
hardware actually runs. It now handles both formats, and only ever adds
a missing component to an existing Debian entry — so it cannot produce a
duplicate repository or a duplicate component however many times it
runs, and third-party repositories like Chrome's are never touched. It
also answers libdvd-pkg's debconf questions, so nothing blocks on a
prompt in a service with no terminal.
Tested against fixture APT trees for classic sources.list, deb822
.sources, contrib already enabled, repeated runs, third-party
repositories and the awkward real lines: bracketed options, indentation,
trailing comments and flat repositories.
Co-Authored-By: Claude Opus 5 <[email protected]>
The classroom desktop belongs to the classroom user and mako sits on
that user's D-Bus session. An administrator over SSH is in a different
session where nothing owns org.freedesktop.Notifications, so notify-send
asked D-Bus to start a second mako, which had no display to open and
timed out with NoReply.
Find the running sway process for the classroom user and deliver into
its session instead. XDG_RUNTIME_DIR and the bus address are read from
that process, falling back to /run/user/<uid> and its bus socket when
the environment cannot be read — root has ptrace access on the endpoint,
but that is not somewhere to be brittle. The Wayland display is read the
same way, and failing that from the wayland-N.lock sway holds open, so
no socket name is ever assumed. notify-send only needs the bus, so a
display that cannot be resolved is not worth refusing to send over.
Refuses to send when mako is not already running, rather than letting
D-Bus start one, and says plainly what is wrong when there is no
session, no privileges, or no bus socket. --dry-run reports what it
resolved without sending.
Behaviour inside the session is unchanged, and the sample notifications
and styling are untouched.
The tests stand a real background process in for sway so /proc/<pid>/environ
is genuine rather than mocked, and cover both invocation paths plus the
fallbacks and every refusal. The build runs them.
Co-Authored-By: Claude Opus 5 <[email protected]>