Skip to content

packaging: run the kernel hooks through linux-run-hooks - #121

Open
Christopher Obbard (obbardc) wants to merge 4 commits into
mainfrom
wip/obbardc/linux-run-hooks
Open

Christopher Obbard (obbardc) wants to merge 4 commits into
mainfrom
wip/obbardc/linux-run-hooks

Conversation

@obbardc

Copy link
Copy Markdown
Contributor

Installing a kernel on forky or resolute writes /boot/vmlinuz- and stops:
no systemd-boot loader entry and no loader.conf are generated, so the new kernel
never appears in the boot menu and the board comes back on the old one. The
workaround in circulation is to symlink the hooks into /etc before installing,
which is the administrator's directory and not ours to populate.

The cause is that kernel hooks moved. Since linux-base 4.11 a package that ships
its own hook installs it in /usr/share/kernel/*.d rather than /etc, and
systemd-boot's zz-systemd-boot made that move in forky. Our maintainer scripts
run-parts only /etc/kernel/postinst.d, which on forky and resolute is empty. On
trixie the hook is still a conffile in /etc, which is why this has gone unnoticed.

Dispatch through linux-run-hooks(1) instead, as Debian's own linux-image packages
do: it runs both directories in the documented order and sets DEB_MAINT_PARAMS.
The postrm gets the same treatment, so removing a kernel drops its loader entry
instead of leaving sd-boot offering one that is gone. The command ships in
linux-base 4.12, declared as Pre-Depends: linux-base (>= 4.12~) — Pre-, because it
runs from our postinst. trixie, forky and resolute carry 4.12.1, 4.17 and
4.15ubuntu5, so the matrix is satisfied; noble is not, and is dropped from the
suites the build scripts accept, since nothing builds it and a build aimed there
would only produce a package noble refuses to install.

Getting there needs the maintainer scripts templated first: only the postinst was
a kver@ template, and the postrm was reconstructing the kernel release at run
time by stripping "linux-image-" and a trailing "-qcom" off
DPKG_MAINTSCRIPT_PACKAGE — a no-op at best now that the package is
linux-image-, and at worst a chopped version running hooks for a
kernel that does not exist. All three become .in templates generated by one loop
in override_dh_installdeb.

Split out of #107 so the boot-menu fix can land on its own. The metadata tidy-ups
there and the review feedback on them stay on that PR.

Only the postinst was a @kver@ template; the preinst and postrm were copied
in verbatim. That left the postrm reconstructing the kernel release at run
time, by stripping "linux-image-" and a trailing "-qcom" off
DPKG_MAINTSCRIPT_PACKAGE. The suffix is not part of the package name any more
-- the package is linux-image-<kernelrelease>, and the release carries its own
+qcom-next-<date>-g<sha> -- so the strip is a no-op at best, and at worst
chops a real part of the version off and runs the hooks for a kernel that
does not exist.

Rename all three to .in and substitute @kver@ into each, so every script
names its own kernel release the way the postinst already did. The scripts
now share one shape: header, set -e, KVER, #DEBHELPER#, body.

override_dh_installdeb generates them in one loop over the four maintainer
script names, taking whichever templates exist, so adding a prerm becomes a
matter of adding debian/linux-image.prerm.in. debian/clean and .gitignore
already cover all four generated names.

Which hook directories those scripts run is unchanged here, and wrong on
forky; the next commit fixes that.

Verified that the three generated scripts are shellcheck-clean with no @kver@
left in them, and that the postrm now names the same release the postinst
does.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
Kernel hooks used to live only in /etc/kernel/{postinst,postrm}.d, so that is
the one directory the maintainer scripts run. Since linux-base 4.11 a package
that ships its own hook installs it in /usr/share/kernel/*.d instead, leaving
/etc for the administrator, and systemd-boot's zz-systemd-boot made that move
in forky: on trixie it is a conffile in /etc/kernel/postinst.d, on forky
nothing is installed there at all.

The result is that installing this package on forky writes the kernel to /boot
and stops. No loader entry and no loader.conf are generated, so the new kernel
is invisible to sd-boot and the board comes back on the old one. The workaround
people have been using is to symlink the hooks into /etc before installing the
kernel package, which is the administrator's directory and not ours to
populate.

Dispatch through linux-run-hooks(1) instead, which is what Debian's own
linux-image packages call: it runs both directories in the documented order
and sets DEB_MAINT_PARAMS. The postrm gains the same treatment, so removing a
kernel drops its loader entry rather than leaving sd-boot offering a kernel
that is gone.

The command ships in linux-base, and nothing said it had to be there --
linux-base arrived unversioned, if at all, through the initramfs-tools or
dracut we merely recommend. Declare it the way Debian's linux-image packages
do, with Pre-Depends: linux-base (>= 4.12~). Pre- is the right strength for
the same reason it is right for them: the command runs from our postinst, so
it has to be configured before we are.

There is deliberately no run-parts fallback for the case where the command is
missing. Running /etc/kernel/*.d alone is not a degraded path, it is precisely
the silent unbootable install above, so a fallback could only reintroduce the
bug this commit exists to fix while hiding the missing dependency. The
postinst calls the command unguarded, as Debian's does.

The postrm is the exception and tests for it first. Pre-Depends holds while
the package is installed, not while it is being removed: dpkg may remove
linux-base in the same run, and on purge it can already be gone. Debian's
postrm guards it for the same reason and warns rather than failing -- a kernel
has to stay removable, and a skipped hook run there costs a stale bootloader
entry, not a system that will not boot. The warning says as much, on stderr.

The field is a plain line in control.in rather than something 'prepare'
decides per suite, because there is no suite to make an exception for: the
matrix builds trixie, forky and resolute, carrying 4.12.1, 4.17 and
4.15ubuntu5. Only noble in build-kernel.sh's list of accepted --distro values
predates 4.12, and nothing builds it. A build aimed there would produce a
package noble refuses to install, which is the better failure.

Note that linux-run-hooks arrived in linux-base 4.12, not the 4.11 that
release notes for the /usr/share move suggest: 4.12's changelog adds the
command, and Debian's kernel pre-depends on that same 4.12.

Verified on forky that linux-run-hooks runs both directories; over the
generated scripts, that postinst and postrm both dispatch with the version and
image path when the command is present, that the postrm takes the warning
branch and still exits 0 when it is absent, and that all three scripts are
shellcheck-clean; that 'prepare' emits the Pre-Depends field whatever DISTRO
it is given, the stanza parsing through Dpkg::Control::Info; and that
dpkg --compare-versions agrees 4.12.1 and 4.15ubuntu5 satisfy the clause while
4.11 and 4.5ubuntu9 do not.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
The image package now pre-depends on linux-base (>= 4.12~) for
linux-run-hooks(1). noble carries 4.5ubuntu9, so a package built for it cannot
be installed there: the pre-dependency is unsatisfiable and apt refuses the
package outright.

noble was the only --distro value left whose linux-base predates 4.12, and
nothing builds it -- ci/build-matrix.json covers trixie, forky and resolute and
names noble nowhere. So the only thing the accepted value bought was the chance
to hand-build a package noble would then refuse.

Drop it from VALID_DISTROS in both build-kernel.sh and prepare-source.sh, since
build-kernel.sh passes --distro straight through to prepare-source.sh and the
two lists have to agree, and from the help text, the worked example and the
supported-distributions table that advertised it.

The remaining noble mentions in .github/workflows/build-kernel-deb.yml and
build-kernel-ubuntu.yml are workflow_dispatch descriptions and comments rather
than validation, and those files are reworked wholesale by #110; they are left
alone here. So is the older gap where VALID_DISTROS accepts forky and unstable
that the help text has never listed.

Verified that both scripts now exit 1 with "Invalid distro: noble", that
trixie, forky, resolute, questing, sid and unstable all still get past the
check, and that shellcheck reports nothing new on either script.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
@bjordiscollaku

Copy link
Copy Markdown
Contributor

Checked the linux-run-hooks call against its own usage and what Debian's postinst does, looks right. Pre-Depends lines up too, 4.12 is where the command lands and trixie, forky, sid, questing and resolute all have it. noble's gone from build-kernel.sh, prepare-source.sh and the README so that's consistent.

Only thing still open is the one I left on #107, moving it here since the commits did: preinst.d and prerm.d aren't dispatched, and dkms ships hooks in both on forky and resolute.

Two of the four hook directories were being run. The postinst dispatches
postinst.d and the postrm dispatches postrm.d, but the preinst did nothing and
there was no prerm at all, so preinst.d and prerm.d never ran.

dkms ships a hook in both. /etc/kernel/preinst.d/dkms calls 'dkms
kernel_preinst' for the kernel about to be installed, and
/etc/kernel/prerm.d/dkms calls 'dkms kernel_prerm' for the one about to go, the
latter while the kernel is still on disk. Without a prerm, removing one of our
kernels leaves dkms holding modules built against a release that no longer
exists. linux-base ships zz-legacy-boot in /usr/share/kernel/prerm.d for the
same window.

Give the preinst a dispatch and add a prerm template, both through
linux-run-hooks(1) like the other two, so all four directories are run in both
of their locations. The preinst covers 'install' and 'upgrade', which is every
action that reaches it bar 'abort-upgrade' -- the one Debian's preinst skips
too.

The prerm calls linux-check-removal(1) before the hooks, as Debian's does. If
the kernel being removed is the one currently running it prompts first; it is
silent where the answer cannot matter, in a chroot or container or where
debconf prompts are disabled, so it only speaks up on an interactive removal of
the running kernel. That is worth having on a board you are logged into over
the network.

Neither script guards its calls or swallows their failures, which is the
opposite of what the postrm right beside them does, so both say why in a
comment. The package is still installed when the preinst and prerm run, so
Pre-Depends holds and linux-base is guaranteed -- the preinst is in fact the
sharpest reason that field is Pre- rather than Depends, since it runs before we
are even unpacked. And a failing prerm aborts the removal before dpkg has
removed anything, leaving nothing half-done, which is what makes it safe to let
linux-check-removal veto; vetoing is the point of calling it. The postrm has
neither property: linux-base may already be gone and a failure there would
wedge the package.

Debian's preinst also touches a .fresh-install flag under /lib/modules, and its
postinst and postrm call linux-update-symlinks. Neither is copied. The flag
exists only so the postinst can tell linux-update-symlinks whether this is an
install or an upgrade, and we ship no /vmlinuz symlinks and call that command
nowhere, so the flag would be a file nothing ever reads.

No build-system change is needed: the override_dh_installdeb loop already
iterates all four script names and takes whichever templates exist, and
debian/clean and .gitignore already list the generated prerm.

Verified over the generated scripts that all four are produced, are mode 755
and carry no leftover @kver@, and that they name the same release; that the
preinst dispatches on install and upgrade and skips abort-upgrade, and the
prerm dispatches on remove only; that both pass the version, the image path and
the maintainer parameters in the order linux-run-hooks documents, the real
command accepting 'image preinst' and 'image prerm' in that form; that a
declining linux-check-removal fails the prerm without running any hook; and
that the postinst and postrm are unchanged in behaviour, the postrm still
warning and exiting 0 when linux-base is absent. All four templates are
shellcheck-clean.

Signed-off-by: Christopher Obbard <chris.obbard@oss.qualcomm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants