systemd v261 lists the new systemd-boot-fallback binary in `bootctl
status`, so our regex picked up the older fallback version and ran
`bootctl update` on an already current ESP, which then exits non-zero.
Switch from parsing the `bootctl --status` output to having bootctl
compare versions itself by calling io.systemd.BootControl.Install over
Varlink. bootctl is spawned as a stdio Varlink server so that we always
use the bootctl from the target closure. An already current binary comes
back as an io.systemd.System error carrying ESTALE, which we treat as
success.
This is for instance useful on Asahi where an additional initrd archive
containing firmware blobs and per-device calibration files is placed
on the ESP and updated by the Asahi Linux Installer.
These need to be loaded alongside the NixOS initrd.
Implemented for systemd-boot and Limine. Grub is left out since its
install script does not use bootspec yet.
Co-authored-by: Florian Klink <flokli@flokli.de>
The CalledProcessError can only come from the append-initrd-secrets
script, so catching it in the generic write loop and then asserting
on the writer type to reach back into its `source` is the wrong
layer. Move the catch, the pristine-initrd fallback and the warning
into InitrdWithSecretsWriter itself, and pass `critical` through the
writer protocol so it can decide between aborting and falling back.
The writer carries the generation number so the warning can still
name the affected generation. write_boot_files no longer knows
anything about secrets and the isinstance assertion is gone.
Suggested-by: Will Fancher <elvishjerricco@gmail.com>
Whether a write failure must be fatal is a property of the destination
path (is it needed by the configuration we are switching to?), not of
the particular BootFile instance that happened to survive
deduplication. Compute the set of critical paths up front and look it
up in write_boot_files, so the dedup loop no longer needs to pick the
"right" instance and becomes a plain order-preserving seen-set walk.
This leaves BootFile.current unused.
Suggested-by: Will Fancher <elvishjerricco@gmail.com>
This guarantees the descriptor is closed even when copyfileobj raises,
matching the other writer implementations. The append-initrd-secrets
script reopens the file by path, so flush() is enough before invoking
it and the explicit close() is no longer needed.
Tell the user what actually happens (the old secrets stay in place) and
how to get rid of the warning, instead of just saying it is "normal".
Suggested-by: Will Fancher <elvishjerricco@gmail.com>
Shared kernels and initrds appear once per generation in boot_files, so
InitrdWithSecretsWriter rebuilt the same file repeatedly. Prefer the
current configuration's entry so its failures stay fatal.
has_gc_root() iterated the entire BootFileList for every file on the
ESP, giving O(files * roots) comparisons. Build the set of kept paths
once and use O(1) membership tests instead.
After removing or renaming a file in boot.initrd.secrets, older
generations' append scripts start failing. Aborting on that blocks
deploying the new configuration, so only treat a failure as fatal when
it belongs to the configuration being switched to.
Appending to the existing file made it grow on every rebuild and a
failed script could leave it half-written. Always rebuild from the
pristine initrd into a temp file and rename into place.
We absolutely do not want to leave an incomplete file behind in /boot
since an incomplete initrd would render the machine unbootable. Errors
while writing are relatively common, mostly due to full /boot
partitions.
systemd-boot-builder does never attempt to re-write already existing
files which means that such situations are not fixable by re-running
nixos-rebuild etc. Instead the user needs to know about internals of the
systemd-boot and manually delete the correct file to recover from a
partially written kernel or (more commonly) initrd in /boot.
Note that this used to be a non issue since systemd-boot-builder used to
always delete all kernels and initrds before copying kernels and
initrds, so dest.exist() would always return False. This was fixed in
f2ca990558, revealing the underlying bad
assumption (that copyfile() always succeeds or fails without writing
anything).
The solution is to write to a temporary file first and move it to the
destination path only after this has succeeded. This way, if an error
occurs during copying, only a file distinct from dest is left behind
which would be cleaned up by subsequent runs of remove_old_entries().
Resolves#444066.
Otherwise this gets printed to stdout when running things like
nixos-rebuild, while people rely on the toplevel store path being the only
output of such commands.
`switch-to-configuration boot` was taking suspiciously long on a machine
of mine where the boot partition is on a slow SD card. Some tracing led
me to discover that it was in fact deleting all the kernels and initrds
every time, only to rewrite them.
This turned out to be because of the naive (non-path-normalising) string
concatenation used to construct paths in `known_paths`, so all the files
were recognised as obsolete and deleted:
known_paths=['/EFI/nixos/5jz3m9df1cbxn4hzjjs3aaz8lb9vvimc-linux-6.15.7-Image.efi', '/EFI/nixos/xri8qzfvzclf89x7nfwgq248miw7jbp0-initrd-linux-6.15.7-initrd.efi', '/EFI/nixos/b18llskzrcdgw2nbib58qqcaabiik6yc-linux-6.16-Image.efi', '/EFI/nixos/mdj53j746bii1vw227dfhkyd8ajwab2w-initrd-linux-6.16-initrd.efi', '/EFI/nixos/b18llskzrcdgw2nbib58qqcaabiik6yc-linux-6.16-Image.efi', '/EFI/nixos/mdj53j746bii1vw227dfhkyd8ajwab2w-initrd-linux-6.16-initrd.efi', '/EFI/nixos/b18llskzrcdgw2nbib58qqcaabiik6yc-linux-6.16-Image.efi', '/EFI/nixos/mdj53j746bii1vw227dfhkyd8ajwab2w-initrd-linux-6.16-initrd.efi', '/EFI/nixos/5jz3m9df1cbxn4hzjjs3aaz8lb9vvimc-linux-6.15.7-Image.efi', '/EFI/nixos/1ihk03c1i5518hlgm5mnhrig2hy3hq24-initrd-linux-6.15.7-initrd.efi', '/EFI/nixos/5jz3m9df1cbxn4hzjjs3aaz8lb9vvimc-linux-6.15.7-Image.efi', '/EFI/nixos/1ihk03c1i5518hlgm5mnhrig2hy3hq24-initrd-linux-6.15.7-initrd.efi', '/EFI/nixos/5jz3m9df1cbxn4hzjjs3aaz8lb9vvimc-linux-6.15.7-Image.efi', '/EFI/nixos/1ihk03c1i5518hlgm5mnhrig2hy3hq24-initrd-linux-6.15.7-initrd.efi']
path='/boot//EFI/nixos/5jz3m9df1cbxn4hzjjs3aaz8lb9vvimc-linux-6.15.7-Image.efi'
path='/boot//EFI/nixos/xri8qzfvzclf89x7nfwgq248miw7jbp0-initrd-linux-6.15.7-initrd.efi'
path='/boot//EFI/nixos/b18llskzrcdgw2nbib58qqcaabiik6yc-linux-6.16-Image.efi'
path='/boot//EFI/nixos/mdj53j746bii1vw227dfhkyd8ajwab2w-initrd-linux-6.16-initrd.efi'
path='/boot//EFI/nixos/1ihk03c1i5518hlgm5mnhrig2hy3hq24-initrd-linux-6.15.7-initrd.efi'
This can be avoided by using pathlib.Path, which normalises paths and
generally provides a more consistent and convenient API. I therefore
went ahead and replaced all use of `str` for path handling with `Path`
in the builder. This may fix some other, similar bugs, as well, but I
haven't checked in detail.
After RFC-0125 implementation, Determinate Systems was pinged multiple
times to transfer the repository ownership of the tooling to a
vendor-neutral repository.
Unfortunately, this never manifested. Additionally, the leadership of
the NixOS project was too dysfunctional to deal with this sort of
problem. It might even still be the case up to this day.
Nonetheless, nixpkgs is about enabling end users to enact their own
policies. It would be better to live in a world where there is one
obvious choice of bootspec tooling, in the meantime, we can live in a
world where people can choose their bootspec tooling.
The Lix forge possess one fork of the Bootspec tooling:
https://git.lix.systems/lix-community/bootspec which will live its own
life from now on.
Change-Id: I00c4dd64e00b4c24f6641472902e7df60ed13b55
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
When installing NixOS on a machine with Windows, the "easiest" solution
to dual-boot is re-using the existing EFI System Partition (ESP), which
allows systemd-boot to detect Windows automatically.
However, if there are multiple ESPs, maybe even on multiple disks,
systemd-boot is unable to detect the other OSes, and you either have to
use Grub and os-prober, or do a tedious manual configuration as
described in the wiki:
https://wiki.nixos.org/w/index.php?title=Dual_Booting_NixOS_and_Windows&redirect=no#EFI_with_multiple_disks
This commit automates and documents this properly so only a single line
like
boot.loader.systemd-boot.windows."10".efiDeviceHandle = "HD0c2";
is required.
In the future, we might want to try automatically detecting this
during installation, but finding the correct device handle while the
kernel is running is tricky.
This reverts commit 4daccf208f
The reverted commit claims that bootctl fails if machine-id is not set.
I can not reproduce this. I think this has since been fixed now that
bootctl has support for arbtirary entry-tokens and not just machine-id.
In the case of NixOS the entry-token is the string "nixos"