klapki
[-nvVEh
]… [op
[arg
…]]…
klapki(8) generates and manages EFI boot entries on platforms compatible therewith.
This command-line interface is based on running a set of operations (see [OPS][]) which modify the state and context, then settling the new set-up, then committing it; this means that, barring I/O errors, {dump}ing after the last operation with -n is an accurate representation of what would be committed without it.
Care is taken to only write what is needed and only when it's needed – files and wanted entries are hashed with SHA1(3ssl) and only updated on mismatch.
Minimal state is stored, and it's only supplementary. This means that removing all instances of a kernel boot entry with tools such as efibootmgr(8), efivar(1), or the platform UI will make klapki(8) forget about the kernel entirely, after minor complaints.
klapki(8)'s entries can be moved across BootNNNN
entries, however, so long as they are kept identical.
The entry description and kernel cmdline are controlled via small executable files, see [WISDOM][].
-
-n
: Don't commit – nothing will be written to the filesystem or the firmware. -
-v
: Verbose operation. -
-V
: Very verbose – adds a {dump} op in-between each specified op. -
-E
: Increase libefivar verbosity level.At time of writing, libefivar supports
LOG_VERBOSE
andLOG_DEBUG
, which require -E to be specified one and two times, respectively.See the [SEE ALSO][] sexion for details.
-
-h
: Show a help message with these flags, recognised environment variables (see [ENVIRONMENT][]) and ops (see [OPS][]). -
op
[arg
…]: Specify an operation to run and arguments to pass to it.See [OPS][] for more detail.
-
KLAPKI_HOST
=: By default, klapki(8) uses the value found in/etc/machine-id
(or, failing that, the current hostname, as obtained with gethostname(2)) as the identifier for the host.If this environment variable is present, it will be used instead; note, that the host identifier is used verbatim as an EFI variable name under klapki's GUID (a8a9ad3a-f831-11ea-946d-674ccd7415cc).
-
KLAPKI_WISDOM
=: To obtain the description and cmdline, klapki(8) invokes respectively-named files under the wisdom root via execl(3), which is/etc/klapki
by defailt. This value overrides that path. If not empty, a '/' is additionally appended before the executable name.See also [WISDOM][] below.
-
KLAPKI_EFI_ROOT
=: By default, klapki(8) puts newly-installed files in\klapki\{host}\{version}
under the ESP.If present, this overrides the constant prefix. Par exemple, setting
KLAPKI_EFI_ROOT
= when adding a kernel will put it and the initrds directly under\{host}\{version}
(note that by default this collides with kernel-install(8)).
-
dump
: Write some state (boot order, total boot entries, boot position, each wanted entry, boot variants) and context (our kernels, fresh kernels, deleted files) to the standard output. -
bootpos
<position
>: Change the boot position to 0-based <position
>.The cluster of entries for the current host can be placed at any point in the boot order; it's 0 (i.e. at the beginning) by default, but if you have another operating system or boot-loader and wish to have it be the default, you can simply move klapki(8) down the required amount of entries.
See description of addvariant below for sorting inside the cluster.
-
addkernel
<version
> <image
> [initrd
…] <""
>: Allocate entries for the kernel with version <version
> whose image resides at <image
> and initrds at [initrd
]…. The list of initrds is terminated with an empty argument.This directive is ignored if a kernel with version <
version
> is already known. See delkernel below.The kernel image and initrds will be copied to the ESP (see
KLAPKI_EFI_ROOT
= in [ENVIRONMENT][]) during context commit. -
delkernel
<version
>: Purge all entries for which the version is <version
>.The kernel image, initrds and containing folder will, if not used, be removed from the ESP during context commit.
-
addvariant
<variant
>: Add an explicit variant <variant
> to the end, if not already known. Accompanying boot entries will be allocated in the derivation phase as needed.Variants are a global property, and a boot entry is generated for each variant (that is: for the implicit variant, represented by the empty string, in addition to any configured explicit variants).
The order of explicit variants is preserved within each version group, which are sorted highest-to-lowest. For example: a host with two kernels (5.8.0-[12]-amd64) and two explicit variants (debug, silent) will produce the following entries (assume
$KLAPKI_WISDOM/description
symlinked to/bin/echo
); note how the highest kernel version is at the top:
5.8.0-2-amd64
5.8.0-2-amd64 debug
5.8.0-2-amd64 silent
5.8.0-1-amd64
5.8.0-1-amd64 debug
5.8.0-1-amd64 silentAfter running klapki(8) with "delvariant debug addvariant debug", the two explicit variants are now ordered differently (silent, debug), and this is reflected in the boot order; note also how the implicit variant always sorts earlier than any explicit ones:
5.8.0-2-amd64
5.8.0-2-amd64 silent
5.8.0-2-amd64 debug
5.8.0-1-amd64
5.8.0-1-amd64 silent
5.8.0-1-amd64 debug -
delvariant
<variant
>: Remove explicit variant <variant
>, if any; accompanying boot entries will purged in the derivation phase as needed.
The entry description and kernel cmdline are acquired by executing description
and cmdline
in /etc/klapki
(or KLAPKI_WISDOM=
, see [ENVIRONMENT][])
with the following arguments:
0: "description" or "cmdline"
1: kernel version
2: boot variant
And the standard output tied to a memfd (see memfd_create(2)), which is then trimmed, and all newlines are replaced with a single space.
klapki(8) stops processing if the child exits with a non-zero status or is killed by signal. The special exit value 0x6B (107, ASCII 'k') is used to signal an error to execl(2) the wisdom binary.
Additional initrd=
statements should work (with warnings, since the should.
Please report on the bug tracker/mailing list (see [REPORTING BUGS][]) if you use them successfully!) and will not be managed by klapki(8),
The simplest /etc/klapki/description
would be a link to /bin/echo
.
A simple cmdline
is a /bin/sh
shebang + echo
command,
A cursed cmdline
would be a /bin/sh
shebang and an awk '{gsub(/initrd=[^ ]+ ?/, ""); print}' /proc/cmdline
command.
1 - error reading configuration,
2 - error loading state,
3 - error resolving state,
4 - error running an op,
5 - error in propagation phase,
6 - error in aging phase,
7 - error in wisening phase,
8 - error in saving phase,
9 - error committing context,
10 - error committing state.
A simple set-up:
root@zoot:~# ls -l description cmdline
-rwxr-xr-x 1 root root 79 Sep 21 03:30 cmdline
lrwxrwxrwx 1 root root 9 Sep 20 04:30 description -> /bin/echo
root@zoot:~# cat cmdline
#!/bin/sh
echo root=ZFS=zoot/root console=ttyS0
Add a kernel with a single initrd and a variant:
root@zoot:~# KLAPKI_HOST=zoot klapki addkernel 5.8.0-2-amd64 /boot/vmlinuz-5.8.0-2-amd64 /boot/initrd.img-5.8.0-2-amd64 "" addvariant debug
EFI load: no config for this host (zoot) found; going to the top
Entry 000B changed
Entry 000D changed
Entry 000B: copied vmlinuz-5.8.0-2-amd64 from /boot to \klapki\zoot\5.8.0-2-amd64\
Entry 000B: copied initrd.img-5.8.0-2-amd64 from /boot to \klapki\zoot\5.8.0-2-amd64\
Updating state config
Updating boot order
Writing entry 000B
Writing entry 000D
Success! But the new "debug" entry doesn't really do much:
root@zoot:~# efibootmgr
BootCurrent: 000D
Timeout: 0 seconds
BootOrder: 000B,000D,000C,000A,0000,0001,0002,0003,0004,0005,0006,0007,0008,0009
Boot0000 through Boot0008 omitted
Boot0009* EFI Internal Shell FvVol(7cb8bdc9-f8eb-4f34-aaea-3ee4af6516a1)/FvFile(7c04a583-9e3e-4f1c-ad65-e05268d0b4d1)
Boot000A* Linux Boot Manager HD(1,GPT,0655a4fb-e2e9-4fa6-b37b-a52633aed855,0x800,0x76800)/File(\EFI\systemd\systemd-bootx64.efi)
Boot000B* 5.8.0-2-amd64 HD(1,GPT,0655a4fb-e2e9-4fa6-b37b-a52633aed855,0x800,0x76800)/File(\klapki\zoot\5.8.0-2-amd64\vmlinuz-5.8.0-2-amd64)i.n.i.t.r.d.=.\.k.l.a.p.k.i.\.z.o.o.t.\.5...8...0.-.2.-.a.m.d.6.4.\.i.n.i.t.r.d...i.m.g.-.5...8...0.-.2.-.a.m.d.6.4. .r.o.o.t.=.Z.F.S.=.z.o.o.t./.r.o.o.t. .c.o.n.s.o.l.e.=.t.t.y.S.0.
Boot000C* zoot 5.8.0-1-amd64 HD(1,GPT,0655a4fb-e2e9-4fa6-b37b-a52633aed855,0x800,0x76800)/File(\62dd03a4928c412180b3024ac6c03a90\5.8.0-1-amd64\linux)i.n.i.t.r.d.=.\.6.2.d.d.0.3.a.4.9.2.8.c.4.1.2.1.8.0.b.3.0.2.4.a.c.6.c.0.3.a.9.0.\.5...8...0.-.1.-.a.m.d.6.4.\.i.n.i.t.r.d...i.m.g.-.5...8...0.-.1.-.a.m.d.6.4. .r.o.o.t.=.Z.F.S.=.z.o.o.t./.r.o.o.t. .c.o.n.s.o.l.e.=.t.t.y.S.0.
Boot000D* 5.8.0-2-amd64 debug HD(1,GPT,0655a4fb-e2e9-4fa6-b37b-a52633aed855,0x800,0x76800)/File(\klapki\zoot\5.8.0-2-amd64\vmlinuz-5.8.0-2-amd64)i.n.i.t.r.d.=.\.k.l.a.p.k.i.\.z.o.o.t.\.5...8...0.-.2.-.a.m.d.6.4.\.i.n.i.t.r.d...i.m.g.-.5...8...0.-.2.-.a.m.d.6.4. .r.o.o.t.=.Z.F.S.=.z.o.o.t./.r.o.o.t. .c.o.n.s.o.l.e.=.t.t.y.S.0.
Uncontrivedly, adding a conditional with another argument to enable debugging in the Intel PRO/100 driver and re-running klapki(8) will work:
root@zoot:~# cat cmdline
#!/bin/sh
echo root=ZFS=zoot/root console=ttyS0
[ "$2" = "debug" ] && echo e100.debug=16 || :
root@zoot:~# KLAPKI_WISDOM=. KLAPKI_HOST=zoot klapki
Entry 000D changed
Updating state config
Updating entry 000D
Note, that, as expected, only the state configuration (which stores the hash of the wanted entry) and the debug entry itself was updated:
root@zoot:~# efibootmgr -v
BootCurrent: 000D
Timeout: 0 seconds
BootOrder: 000B,000D,000C,000A,0000,0001,0002,0003,0004,0005,0006,0007,0008,0009
Boot0000 through Boot000A and Boot000C omitted
Boot000B* 5.8.0-2-amd64 HD(1,GPT,0655a4fb-e2e9-4fa6-b37b-a52633aed855,0x800,0x76800)/File(\klapki\zoot\5.8.0-2-amd64\vmlinuz-5.8.0-2-amd64)i.n.i.t.r.d.=.\.k.l.a.p.k.i.\.z.o.o.t.\.5...8...0.-.2.-.a.m.d.6.4.\.i.n.i.t.r.d...i.m.g.-.5...8...0.-.2.-.a.m.d.6.4. .r.o.o.t.=.Z.F.S.=.z.o.o.t./.r.o.o.t. .c.o.n.s.o.l.e.=.t.t.y.S.0.
Boot000D* 5.8.0-2-amd64 debug HD(1,GPT,0655a4fb-e2e9-4fa6-b37b-a52633aed855,0x800,0x76800)/File(\klapki\zoot\5.8.0-2-amd64\vmlinuz-5.8.0-2-amd64)i.n.i.t.r.d.=.\.k.l.a.p.k.i.\.z.o.o.t.\.5...8...0.-.2.-.a.m.d.6.4.\.i.n.i.t.r.d...i.m.g.-.5...8...0.-.2.-.a.m.d.6.4. .r.o.o.t.=.Z.F.S.=.z.o.o.t./.r.o.o.t. .c.o.n.s.o.l.e.=.t.t.y.S.0. .e.1.0.0...d.e.b.u.g.=.1.6.
Written by наб <nabijaczleweli@nabijaczleweli.xyz>
To all who support further development, in particular:
- ThePhD
- Embark Studios
<https://todo.sr.ht/~nabijaczleweli/klapki>
<mailto:~nabijaczleweli/klapki@lists.sr.ht>, archived at <https://lists.sr.ht/~nabijaczleweli/klapki>
<https://git.sr.ht/~nabijaczleweli/klapki>
UEFI specification, Sexion 3.1.3 Load Options and related: <https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf>
libefivar verbosity levels: <https://github.com/rhboot/efivar/blob/36297adcb266f07bb06e725a0da377bc6e6aedd0/src/util.h#L328>