One first boot run at a time, and keel-init as the command #25
No reviewers
Labels
No labels
accessibility
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Keel-Linux/inithooks!25
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/one-first-boot-run"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #24. Fixes #22.
One first boot run at a time (#24)
On keel-wordpress 19.0-3 (LXC on Proxmox, 2026-09-30), the boot run (
/usr/lib/inithooks/run, pid 260) was waiting in30rootpasson the container console (pts/1). Aturnkey-inittyped afterpct enterstarted a second run of the same hooks. It set the root password and then hung in35mysqlpass.The fix is a lock that both runs hold:
flock(2)on/run/inithooks.lock(override withINITHOOKS_LOCK), held for as long as hooks run. The holder writes its pid into the file, so a refusal can name it.keel-initdoes not wait. While another run holds the lock it exits 75 and says where that run is waiting:pct console <ctid>on Proxmox, orlxc-console -n <name>on LXC, or the VM's console (it picks the hint withsystemd-detect-virt). The container id cannot be known from inside, so the hint says<ctid>.runwaits for the lock instead, because systemd cannot come back later. It takes the lock before it reads/etc/default/inithooks, so after a keel-init that completed the first boot (98finalize setsRUN_FIRSTBOOT=false) it skips the firstboot hooks.runcloses the descriptor for each hook ({INIT_LOCK_FD}>&-), and keel-init's descriptor is not inheritable (PEP 446), so a daemon left behind by a hook does not hold the lock. Both runs release the lock before confconsole starts, because confconsole stays open./etc/update-motd.d/06-keel-initprintskeel-init --statusat login. While a run is in progress it says where to answer the wizard. While the fence is up and no run is in progress it says to runkeel-init. Otherwise it prints nothing. The login message and the refusal use the same text from one function.After review (second commit)
inithooks.servicestarts aftergetty.target, sokeel-initcould get the lock before the boot run. It now also refuses (exit 75) whileRUN_FIRSTBOOT=trueandrunhas not yet written/run/inithooks-complete. It makes this check while holding the lock. A test covers the race with the realrun: keel-init goes first and is refused, the boot run then does the first boot and writes the marker, and keel-init runs after that.kind,pid,tty,dtach,phase,hook,preseeded), andrunrewrites it before every hook. The refusal changes with the holder:dtach -a <socket>no-manual-pagelintian override forusr/sbin/keel-init.Second review (third commit)
A first boot that nothing runs. When the first boot is pending, keel-init now checks the state of
inithooks.service:/usr/lib/inithooks/runon the operator's terminal.Why run the boot run rather than only print the remedy: in the container case
systemctl start inithooksis skipped again by the same condition, so the only remedy that works is runningrunby hand, and keel-init can do that itself. Why not keel-init's own hooks: they are a reduced set (_TURNKEY_INIT, blacklist) and would do the incomplete first boot the previous commit prevents.runtakes the lock itself, so if the unit starts in the meantime, it waits and then finds the first boot done.Tests: a stopped unit, a failed unit, a skipped unit, and a boot run killed in its wizard. In each case the real
runthen does the first boot.Two smaller fixes.
RUN_FIRSTBOOTis now read by bash sourcing the file, exactly asrunreads it, soexport, quotes and trailing comments are handled the same way. A relative dtach socket is now resolved against the dtach process's working directory, so the attach hint works from any shell.tests/test_vocabulary.py: the two local failures are a test-environment issue, not a bug. The defaultKEEL_SRCpoints at a siblingkeelcheckout that predates keel#48. WithKEEL_SRCset to current keel main, master and this branch both pass, and CI skips these tests.keel-init as the command (#22, handbook decision 0015)
keel-initis the real file.turnkey-initis a relative link to it (turnkey-init -> keel-init), committed in the tree and installed bydh_installas a link. I checked this by building the package in a trixie container: the.debcontains./usr/sbin/turnkey-init -> keel-init.--helpand--statusdo not need root.keel-init. The imageturnkey-init-root.pngkeeps its file name.Tests
tests/test_init_lock.py(38 tests): the library; keel-init loaded in-process;keel-initandturnkey-initrun as processes; the link checked as relative; and the reported case, with the realrunblocked in a30rootpasshook while keel-init refuses, names run's pid and does not run the hook. After the boot run finishes, keel-init runs.tests/test-init-lock.bats(10 tests), six new tests intests/test-run.bats, andtests/test-motd.bats(4 tests). Every lock is a real flock, and the other holder is a real process.run, the release before confconsole, the per-hook close, and keel-init's lock or its release. Each one made the suite fail.lib/init-lock.sh21/21, shell total 99.71 (gate 98). Python:libinithooks/init_lock.py100 percent, total 99 (gate 95). The details are in COVERAGE.md.Follow-ups in other repositories (not changed here)
overlays/turnkey.d/turnkey-init-fence/root/.profile.d/turnkey-init-fencestartsturnkey-initin dtach at first login. It should startkeel-init.conf/turnkey.d/motdwrites07-check-inithooksand08-turnkey-confconsole; neither names turnkey-init.patches/headless/overlay/root/.profile.d/turnkey-init-fenceandpatches/container/overlay/root/.profile.d/turnkey-init-fencerunturnkey-init --full-confconsole. They should runkeel-init.firstboot.d/10keel-system(comment),tests/hook.bats(test name) anddocs/apply.mdnameturnkey-init./usr/sbin/turnkey-initrow can say that this package does it now.grepfinds no text that tells an operator to runturnkey-init, so nothing to change there.turnkey-*in this package (0015's table):turnkey-sudoadmin,turnkey-install-security-updates,turnkey-init-fence.