~nabijaczleweli/klapki

92bd266682eb2df69b39bb2fa529c913ac050a64 — наб 1 year, 1 day ago 0c5db6f
This is kinda releasablish
M .build.yml => .build.yml +1 -2
@@ 4,9 4,8 @@ packages:
  - libefivar-dev
  - libefiboot-dev
  - libssl-dev
  - ronn
tasks:
  - cmdline: |
      cat /proc/cmdline
  - build-gcc: |
      cd klapki
      make

M .gitignore => .gitignore +2 -0
@@ 11,6 11,8 @@
!*.md
!src
!src/**
!man
!man/**
!ext
!ext/**
!test

M Makefile => Makefile +9 -2
@@ 29,15 29,16 @@ INCAR := $(foreach l,$(foreach l,fmt,$(l)/include) Catch2/single_include/catch2,
VERAR := $(foreach l,KLAPKI,-D$(l)_VERSION='$($(l)_VERSION)')
SOURCES := $(sort $(wildcard $(SRCDIR)*.cpp $(SRCDIR)**/*.cpp $(SRCDIR)**/**/*.cpp $(SRCDIR)**/**/**/*.cpp))
TEST_SOURCES := $(sort $(wildcard $(TSTDIR)*.cpp $(TSTDIR)**/*.cpp $(TSTDIR)**/**/*.cpp $(TSTDIR)**/**/**/*.cpp))
MANPAGE_SOURCES := $(sort $(wildcard $(MANDIR)*.md $(MANDIR)**/*.md))

# Building with -flto on Clang means we can't make useful $(ARCH)es, so don't
LIBFMT := $(patsubst ext/fmt/src/%.cc,$(BLDDIR)fmt/obj/%$(OBJ),$(wildcard ext/fmt/src/*.cc))


.PHONY : all clean build build-test fmt
.PHONY : all clean build build-test man fmt


all : fmt build build-test test
all : fmt build man build-test test

test: build-test
	$(OUTDIR)klapki-test$(EXE)


@@ 47,6 48,7 @@ clean :

build : fmt $(OUTDIR)klapki$(EXE)
build-test : fmt $(OUTDIR)klapki-test$(EXE)
man : $(subst $(MANDIR),$(OUTDIR)man/,$(MANPAGE_SOURCES))
fmt : $(LIBFMT)




@@ 57,6 59,11 @@ $(OUTDIR)klapki$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(SOURCE
$(OUTDIR)klapki-test$(EXE) : $(subst $(TSTDIR),$(BLDDIR)test/,$(subst .cpp,$(OBJ),$(TEST_SOURCES))) $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(filter-out $(SRCDIR)main.cpp,$(SOURCES)))) $(patsubst ext/fmt/src/%.cc,$(BLDDIR)fmt/obj/%$(OBJ),$(wildcard ext/fmt/src/*.cc))
	$(CXX) $(CXXAR) -o$@ $^ $(PIC) $(LDAR)

$(subst $(MANDIR),$(OUTDIR)man/,$(MANPAGE_SOURCES)) : $(MANDIR)index.txt $(MANPAGE_SOURCES)
	@rm -rf $(dir $@) && mkdir -p $(dir $@)
	cp $^ $(dir $@)
	ronn $@


$(OBJDIR)%$(OBJ) : $(SRCDIR)%.cpp
	@mkdir -p $(dir $@)

M README.md => README.md +10 -11
@@ 1,19 1,17 @@
# klapki [![Licence](//img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
EFI boot manager; or, well, an EFI bootorder [compiler](//twitter.com/nabijaczleweli/status/1306144037070569472).

## [Manpage](//rawcdn.githack.com/nabijaczleweli/feembox/man/feembox.1.html)
<!-- TODO: put something on man.sr.ht -->
## [Manpage](//git.sr.ht/~nabijaczleweli/klapki/tree/trunk/man/klapki.md)

### Installation
### Building

dunno yet
You'll need `libssl-dev` and `libefi{var,boot}-dev`, and `make` should hopefully Just Work™.

#### probably a building sexion here
### Installation

CXX='clang++ -stdlib=libc++' make -j && ./to-zoot owo scp -P 10023 out/klapki nab@127.0.0.1:
libssl-dev
libefi{var,boot}-dev
Copy `out/klapki` to `/sbin` and write a `/etc/klapki/{description,cmdline}`, as seen in the [manpage](//git.sr.ht/~nabijaczleweli/klapki/tree/trunk/man/klapki.md),

<!-- TODO:
#### From Debian repository

The following line in `/etc/apt/sources.list` or equivalent:


@@ 36,6 34,7 @@ sudo apt install feembox
will work on x86_64 and i686.

See the [repository README](//debian.nabijaczleweli.xyz/README) for more information.
-->

## Reporting bugs



@@ 45,11 44,11 @@ There's [the tracker](//todo.sr.ht/~nabijaczleweli/klapki), but also see the lis

Send a patch inline, as an attachment, or a git link and a ref to pull from to
[the list](//lists.sr.ht/~nabijaczleweli/klapki) ([~nabijaczleweli/klapki@lists.sr.ht](mailto:~nabijaczleweli/klapki)) or [me](mailto:nabijaczleweli@nabijaczleweli.xyz)
directly. I'm not picky, just include the repo name in the subject prefix.
directly. I'm not picky, just please include the repo name in the subject prefix.

## Discussing
## Discussion

Please use the tracker or the list.
Please use the tracker, the list, or [Twitter](//twitter.com/nabijaczleweli/status/1306144037070569472).

## Special thanks


M configMakefile => configMakefile +1 -0
@@ 64,3 64,4 @@ BLDDIR := out/build/
OBJDIR := $(BLDDIR)obj/
SRCDIR := src/
TSTDIR := test/
MANDIR := man/

D klapki.md => klapki.md +0 -110
@@ 1,110 0,0 @@
feembox(1) -- What if a feed, but it's a mailbox?
=================================================

## SYNOPSIS

`feembox` [-v] [-t FROM:TO:HOW]... [-f MIME] [MAILDIR] [FEED]<br />
`feembox` [-v] [-t FROM:TO:HOW]... [-f MIME] [MAILDIR] < feed.xml

## DESCRIPTION

`feembox` represents an (RSS/Atom/JSON) feed as a mailbox in the [maildir](https://cr.yp.to/proto/maildir.html) format.

## OPTIONS

  [MAILDIR]

    Deliver to the specified directory instead of the CWD

    Parents must exist, all directory and its subdirs will be created as necessary

  [FEED]

    Read the feed from the specified file instead of stdin

    If "-" use stdin, otherwise must exist and be a file

  -v --verbose

    Print what's happening to the standard output,
    if specified twice: print parse debugging information.

  -t --transfrom <FROM:TO:HOW|FROM;TO;HOW>...

    Define an alternative transformation invocation HOW
    from the mime-type FROM to the mime-type TO.

    If the post content type matches FROM, "/bin/sh -c HOW" ("cmd /C HOW" on NT)
    is executed, its standard input tied thereto, and standard output
    to the buffer for the new multipart/alternative part TO.

    The separator between FROM, TO, and HOW is the platform's path list separator
    (i.e. ";" on NT and ":" elsewhere).

    Can be specified multiple times, in which case each transformation is invoked once,
    in order, on the current set of parts.

  -f --force <MIME>

    Force the post content type to be MIME, overriding what's specified therein.

    This is done before any transformations.

    Some feeds specify they're text/plain but are HTML,
    this can be used to massage them right.

## EXIT VALUES

    1 - option parse error
    2 - feed file open failed
    3 - feed parse failed
    4 - maildir subdirectory read failed
    5 - existing mail open(2)/mmap(2) failed
    6 - creating a MAILDIR/tmp or MAILDIR/new failed
    7 - formatting mail failed
    8 - creating/writing/delivering mail failed

## EXAMPLES

  Turndown (here: https://github.com/domchristie/turndown/pull/209 lightly patched to always read from stdin),
  can be used to turn HTML feeds (i.e. most of them) into usually pretty readable plaintext:

    P:\Rust\feembox>cat test-data/util-linux-newer.atom |   target\debug\feembox -vt text/html;text/plain;turndown feedir
    ~/code/feembox$ cat test-data/util-linux-newer.atom | ./target/debug/feembox -vt 'text/html:text/plain;charset=utf-8:~/code/turndown/bin/turndown.js' feedir
    <stdin>: feed ID mailto:util-linux@vger.kernel.org, title "Util-Linux Archive on lore.kernel.org", updated 2020-05-18T11:41:20+00:00
    25 entries:
        entry ID                                       title                                                                    updated                    published
        urn:uuid:d2c69230-d7ba-e4cf-ee51-2b25d53119a1  "Re: [PATCH] util-linux: Some minor fixes in some manuals"               2020-05-18T11:40:38+00:00  N/A
        urn:uuid:d9e31d9a-5d6c-8403-9661-2d303e6fb24a  "Re: [PATCH] Fix dead references to kernel documentation"                2020-05-18T11:39:59+00:00  N/A
        urn:uuid:dccd47a2-d327-df9b-7ad1-9218d08a8349  "Re: Consistency fixes in util-linux man pages"                          2020-05-18T10:36:15+00:00  N/A
        urn:uuid:ba1cf039-280f-f33d-199d-86f5a9c1bb1b  "Re: Consistency fixes in util-linux man pages"                          2020-05-18T08:28:25+00:00  N/A
        urn:uuid:98e3d4cd-17c1-ac1d-3dd9-7bbe87f6402e  "[PATCH] Fix dead references to kernel documentation"                    2020-05-17T15:13:35+00:00  N/A
        urn:uuid:68946069-8d16-0362-57e6-feba21ebbecb  "Consistency fixes in util-linux man pages"                              2020-05-16T08:25:11+00:00  N/A
        urn:uuid:94158972-3786-e3c6-7e0a-dec2988cd32b  "[PATCH] ipcs.1: ipcs no longer needs read permission on IPC resources"  2020-05-16T08:10:32+00:00  N/A
        urn:uuid:37f8f0a9-d285-b8f9-5269-6eeb2475f9ba  "plan for v2.35.2"                                                       2020-05-15T13:05:16+00:00  N/A

    Delivering urn:uuid:ba1cf039-280f-f33d-199d-86f5a9c1bb1b to feedir/new/1591610344.M981513P12500Q1.nabuter
    Delivering urn:uuid:d2c69230-d7ba-e4cf-ee51-2b25d53119a1 to feedir/new/1591610346.M86312P12500Q2.nabuter
    Delivering urn:uuid:d9e31d9a-5d6c-8403-9661-2d303e6fb24a to feedir/new/1591610347.M339148P12500Q3.nabuter
    Delivering urn:uuid:dccd47a2-d327-df9b-7ad1-9218d08a8349 to feedir/new/1591610348.M505433P12500Q4.nabuter

## AUTHOR

Written by наб &lt;<nabijaczleweli@nabijaczleweli.xyz>&gt;

## SPECIAL THANKS

To all who support further development, in particular:

  * ThePhD
  * Embark Studios

## REPORTING BUGS

&lt;<https://github.com/nabijaczleweli/feembox/issues>&gt;

## SEE ALSO

&lt;<https://github.com/nabijaczleweli/feembox>&gt;

&lt;<https://cr.yp.to/proto/maildir.html>&gt;

M klapki.sublime-project => klapki.sublime-project +5 -0
@@ 42,6 42,11 @@
		},
		{
			"follow_symlinks": true,
			"name": "Manpages",
			"path": "man"
		},
		{
			"follow_symlinks": true,
			"name": "Build scripts",
			"path": ".",
			"file_include_patterns": [".build.yml", "*Makefile"],

A man/index.txt => man/index.txt +10 -0
@@ 0,0 1,10 @@
klapki(8)          klapki.8.ronn

SHA1(3ssl)         https://manpages.debian.org/buster/libssl-doc/SHA1.3ssl.en.html
efibootmgr(8)      https://manpages.debian.org/buster/efibootmgr/efibootmgr.8.en.html
efivar(1)          https://manpages.debian.org/buster/efivar/efivar.1.en.html
execl(3)           https://manpages.debian.org/buster/manpages-dev/execl.3.en.html
gethostname(2)     https://manpages.debian.org/buster/manpages-dev/gethostname.2.en.html
memfd_create(2)    https://manpages.debian.org/buster/manpages-dev/memfd_create.2.en.html

kernel-install(8)  https://www.freedesktop.org/software/systemd/man/kernel-install.html

A man/klapki.md => man/klapki.md +252 -0
@@ 0,0 1,252 @@
klapki(8) -- EFI boot manager; or, well, an EFI bootorder compiler.
===================================================================

## SYNOPSIS

`klapki` [`-nvVEh`]… [`op` [`arg`…]]…

## DESCRIPTION
<!-- TODO: klapki-internals(7) maybe? -->

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][].

## OPTIONS

  * `-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` and `LOG_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.

## ENVIRONMENT

  * `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)).

## OPS

  * `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` &lt;`position`&gt;:
    Change the boot position to 0-based &lt;`position`&gt;.

    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` &lt;`version`&gt; &lt;`image`&gt; [`initrd`…] &lt;`""`&gt;:
    Allocate entries for the kernel with version &lt;`version`&gt; whose image resides at &lt;`image`&gt; and initrds at [`initrd`]….
    The list of initrds is terminated with an empty argument.

    This directive is ignored if a kernel with version &lt;`version`&gt; 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` &lt;`version`&gt;:
    Purge all entries for which the version is &lt;`version`&gt;.

    The kernel image, initrds and containing folder will, if not used, be removed from the ESP during context commit.

  * `addvariant` &lt;`variant`&gt;:
    Add an explicit variant &lt;`variant`&gt; 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:<br />
    &nbsp;&nbsp;5.8.0-2-amd64<br />
    &nbsp;&nbsp;5.8.0-2-amd64 debug<br />
    &nbsp;&nbsp;5.8.0-2-amd64 silent<br />
    &nbsp;&nbsp;5.8.0-1-amd64<br />
    &nbsp;&nbsp;5.8.0-1-amd64 debug<br />
    &nbsp;&nbsp;5.8.0-1-amd64 silent

    After 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:<br />
    &nbsp;&nbsp;5.8.0-2-amd64<br />
    &nbsp;&nbsp;5.8.0-2-amd64 silent<br />
    &nbsp;&nbsp;5.8.0-2-amd64 debug<br />
    &nbsp;&nbsp;5.8.0-1-amd64<br />
    &nbsp;&nbsp;5.8.0-1-amd64 silent<br />
    &nbsp;&nbsp;5.8.0-1-amd64 debug

  * `delvariant` &lt;`variant`&gt;:
    Remove explicit variant &lt;`variant`&gt;, if any; accompanying boot entries will purged in the derivation phase as needed.

## WISDOM

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:<br />
0: "description" or "cmdline"<br />
1: kernel version<br />
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.

## EXIT VALUES

    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.

## EXAMPLES

  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.

## AUTHOR

Written by наб &lt;<nabijaczleweli@nabijaczleweli.xyz>&gt;

## SPECIAL THANKS

To all who support further development, in particular:

  * ThePhD
  * Embark Studios

## REPORTING BUGS

&lt;<https://todo.sr.ht/~nabijaczleweli/klapki>&gt;

&lt;<mailto:~nabijaczleweli/klapki@lists.sr.ht>&gt;, archived at &lt;<https://lists.sr.ht/~nabijaczleweli/klapki>&gt;

## SEE ALSO

&lt;<https://git.sr.ht/~nabijaczleweli/klapki>&gt;

UEFI specification, Sexion 3.1.3 Load Options and related:
&lt;<https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf>&gt;

libefivar verbosity levels:
&lt;<https://github.com/rhboot/efivar/blob/36297adcb266f07bb06e725a0da377bc6e6aedd0/src/util.h#L328>&gt;

M src/config.cpp => src/config.cpp +2 -1
@@ 107,7 107,7 @@ std::string klapki::config::news_efi_dir() const {
		while(*root == '\\' || *root == '/')
			++root;
		broot = root;
		std::transform(std::begin(broot), std::end(broot), std::begin(broot), [](auto c) { return c == '/' ? '\\' : c; });
		std::replace(std::begin(broot), std::end(broot), '/', '\\');
		while(!broot.empty() && broot.back() == '\\')
			broot.pop_back();
		vroot = broot;


@@ 143,6 143,7 @@ std::variant<klapki::config, std::string> klapki::config::read(const char ** arg
					break;

				case 'h':
					// Remember to sync to klapki(8)
					return fmt::format("klapki {}\n"
					                   "Usage: {} [-nvVEh]… [op [arg…]]…\n"
					                   "\n"

M src/context_commit.cpp => src/context_commit.cpp +1 -1
@@ 130,7 130,7 @@ std::optional<std::string> klapki::context::context::commit(const config & cfg, 
	auto adddir = [&](auto && ddir, auto && cbk) -> std::optional<std::string> {
		if(esp_dirs.find(ddir) == std::end(esp_dirs)) {
			auto dir = ddir;
			std::transform(std::begin(dir), std::end(dir), std::begin(dir), [](auto c) { return c == '\\' ? '/' : c; });
			std::replace(std::begin(dir), std::end(dir), '\\', '/');
			dir.erase(std::remove_if(std::begin(dir), std::end(dir),
			                         [prev = '\0'](auto c) mutable {
				                         if(prev == '/' && c == '/')

M src/context_save.cpp => src/context_save.cpp +1 -1
@@ 123,7 123,7 @@ std::optional<std::string> klapki::context::context::save(const config & cfg, st
			                                }
		                                }),
		                 std::end(image_path));
		std::transform(std::begin(image_path), std::end(image_path), std::begin(image_path), [](auto c) { return isslash(c) ? '\\' : c; });
		std::replace(std::begin(image_path), std::end(image_path), '/', '\\');

		std::vector<std::uint8_t> devpath_file_node(efidp_make_file(nullptr, 0, image_path.data()));
		if(efidp_make_file(devpath_file_node.data(), devpath_file_node.size(), image_path.data()) < 0)

M src/context_wisen.cpp => src/context_wisen.cpp +5 -1
@@ 49,7 49,7 @@ namespace {
			if(fstat(fd, &sb) < 0)
				return fmt::format("{} file stat(): {}", for_whom, strerror(errno));

			auto map = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
			auto map = mmap(nullptr, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
			if(map == MAP_FAILED)
				return fmt::format("{} file mmap(): {}", for_whom, strerror(errno));



@@ 95,6 95,8 @@ namespace {
		str.remove_suffix(std::min(str.size() - str.find_last_not_of(" \f\n\r\t\v") - 1, str.size()));
	}

	static void denewline(std::string_view & str) { std::replace(const_cast<char *>(std::begin(str)), const_cast<char *>(std::end(str)), '\n', ' '); }

	static void validate_cmdline(const std::string_view & cmdline) {
		klapki::context::detail::tokenise_cmdline(cmdline, [&](auto && arg) {
			if(arg.substr(0, std::strlen("initrd=")) == "initrd=") {  // string_view::starts_with() is C++20


@@ 117,6 119,8 @@ std::optional<std::string> klapki::context::context::wisen(const config & cfg, s
		auto cmdline     = TRY(gain_wisdom(cfg, "cmdline", skern->version.c_str(), skern->variant.c_str()));
		trim(description.data);
		trim(cmdline.data);
		denewline(description.data);
		denewline(cmdline.data);
		validate_cmdline(cmdline.data);

		kern.description = description.data;

M src/ops_execute.cpp => src/ops_execute.cpp +17 -17
@@ 42,6 42,22 @@ std::optional<std::string> klapki::ops::execute(const klapki::ops::dump &, const
	           "Boot order: {}\n"
	           "{} boot entries\n"
	           "Desired boot position: {}\n"
	           "Boot variants: ");

	if(state.statecfg.variants.empty())
		fmt::print("(none)");
	else {
		bool first = true;
		for(auto && el : state.statecfg.variants) {
			if(!first)
				fmt::print(", ");
			else
				first = false;

			fmt::print("{}", el);
		}
	}
	fmt::print("\n"
	           "Wanted entries: [",
	           state.order, state.entries.size(), state.statecfg.boot_position);



@@ 58,25 74,9 @@ std::optional<std::string> klapki::ops::execute(const klapki::ops::dump &, const
	fmt::print("]\n"
	           "\n"
	           "{}\n"
	           "\n"
	           "Boot variants: ",
	           "\n",
	           context);

	if(state.statecfg.variants.empty())
		fmt::print("(none)");
	else {
		bool first = true;
		for(auto && el : state.statecfg.variants) {
			if(!first)
				fmt::print(", ");
			else
				first = false;

			fmt::print("{}", el);
		}
	}
	fmt::print("\n\n");

	return {};
}