On February 4, 2022 7:45 pm, Stoiko Ivanov wrote: > The 2 commands follow the mechanics of p-b-t kernel add/remove in > writing the desired abi-version to a config-file in /etc/kernel and > actually modifying the boot-loader configuration upon p-b-t refresh. > > A dedicated new file is used instead of writing the version (with some > kind of annotation) to the manual kernel list to keep parsing the file > simple (and hopefully also cause fewer problems with manually edited > files) > > For systemd-boot we write the entry into the loader.conf on the ESP(s) > instead of relying on the `bootctl set-default` mechanics (bootctl(1)) > which write the entry in an EFI-var. This was preferred, because of a > few reports of unwriteable EFI-vars on some systems (e.g. DELL servers > have a setting preventing writing EFI-vars from the OS). The rationale > in `Why not simply rely on the EFI boot menu logic?` from [0] also > makes a few points in that direction. > > For grub the following choices were made: > * write the pinned version (or actually the menu-path leading to it) > to a snippet in /etc/default/grub.d instead of editing the grub.cfg > files on the partition. Mostly to divert as little as possible from > the grub-workflow I assume people are used to. > * the 'root-device-id' part of the menu-entries is parsed from > /boot/grub/grug.cfg since it was stable (the same on all ESPs and in > /boot/grub), saves us from copying the part of "find device behind > /, mangle it if zfs/btrfs, call grub_probe a few times" part of > grub-mkconfig - and seems a bit more robust > > Tested with a BIOS and an UEFI VM with / on ZFS. > > [0] https://systemd.io/BOOT_LOADER_SPECIFICATION/ > > Signed-off-by: Stoiko Ivanov <s.iva...@proxmox.com> > --- > bin/proxmox-boot-tool | 46 ++++++++++++++++++++++++++++++++---- > proxmox-boot/functions | 37 +++++++++++++++++++++++++++++ > proxmox-boot/zz-proxmox-boot | 5 ++++ > 3 files changed, 83 insertions(+), 5 deletions(-) > > diff --git a/bin/proxmox-boot-tool b/bin/proxmox-boot-tool > index 93760fb..31342a6 100755 > --- a/bin/proxmox-boot-tool > +++ b/bin/proxmox-boot-tool > @@ -280,12 +280,16 @@ list_kernels() { > if [ -z "$manual_kernels" ]; then > manual_kernels="None." > fi > + pinned_kernel="$(get_first_line "$PINNED_KERNEL_CONF")" > > echo "Manually selected kernels:" > echo "$manual_kernels" > echo "" > echo "Automatically selected kernels:" > echo "$boot_kernels" > + echo "" > + echo "Pinned kernel:" > + echo "${pinned_kernel:-None}" > } > > usage() { > @@ -295,8 +299,8 @@ usage() { > warn " $0 init <partition>" > warn " $0 clean [--dry-run]" > warn " $0 refresh [--hook <name>]" > - warn " $0 kernel <add|remove> <kernel-version>" > - warn " $0 kernel list" > + warn " $0 kernel <add|remove|pin> <kernel-version>" > + warn " $0 kernel <list|unpin>" > warn " $0 status [--quiet]" > warn " $0 help" > } > @@ -318,14 +322,16 @@ help() { > echo "" > echo " refresh all configured EFI system partitions. Use --hook to > only run the specified hook, omit to run all." > echo "" > - echo "USAGE: $0 kernel <add|remove> <kernel-version>" > + echo "USAGE: $0 kernel <add|remove|pin> <kernel-version>" > echo "" > echo " add/remove pve-kernel with ABI <kernel-version> to list of > synced kernels, in addition to automatically selected ones." > - echo " NOTE: you need to manually run 'refresh' once you're finished > with adding/removing kernels from the list" > + echo " pin pve-kernel with ABI <kernel-version> sets it as the > default entry to be booted." > + echo " NOTE: you need to manually run 'refresh' once you're finished > with adding/removing/pinning kernels from the list"
I wonder if this should be split? e.g., echo "USAGE: $0 kernel <add|remove> <kernel-version>" echo "" echo " add/remove pve-kernel with ABI <kernel-version> to list of synced kernels, in addition to automatically selected ones." echo " NOTE: you need to manually run 'refresh' once you're finished with adding/removing kernels from the list" + echi "" + echo "USAGE: $0 kernel pin <kernel-version>" + echo " pin pve-kernel with ABI <kernel-version> as the default entry to be booted." + echo " NOTE: you need to manually run 'refresh' once you're finished with adding/removing/pinning kernels from the list" and then adding next-boot and unpin to this section? while pin/next-boot share the argument with add/remove, they are kind of a different operation? e.g., the full thing could then be: echo "" echo "USAGE: $0 kernel <add|remove> <kernel-version>" echo "" echo " add/remove pve-kernel with ABI <kernel-version> to list of synced kernels, in addition to automatically selected ones." echo "" echo "USAGE: $0 kernel <pin|next-boot> <kernel-version>" echo " pin pve-kernel with ABI <kernel-version> sets the default entry to be booted until unpinned." echo " next-boot pve-kernel with ABI <kernel-version> sets the kernel version for the next boot." echo " NOTE: you need to manually run 'refresh' once you're finished with pinning kernels" echo "" echo "USAGE: $0 kernel unpin [--next-boot]" echo "" echo " unpin removes pinned and next-boot kernel settings. Use --next-boot to only remove a next-boot setting." echo " NOTE: you need to manually run 'refresh' once you're finished with unpinning kernels" echo "" echo "USAGE: $0 kernel list" echo "" echo " list kernel versions currently selected for inclusion on ESPs." and reading it like that - maybe it makes sense to have 'pin --next-boot' like with unpin? > echo "" > - echo "USAGE: $0 kernel list" > + echo "USAGE: $0 kernel <list|unpin>" > echo "" > echo " list kernel versions currently selected for inclusion on > ESPs." > + echo " unpin sets the latest kernel as the default entry (undoes a > previous pin)" > echo "" > echo "USAGE: $0 status [--quiet]" > echo "" > @@ -392,6 +398,28 @@ status() { > fi > } > > +pin_kernel() { > + ver="$1" > + > + if [ -z "$ver" ]; then > + warn "E: <kernel-version> is mandatory" > + warn "" > + exit 1 > + fi > + > + if [ ! -e "/boot/vmlinuz-$ver" ]; then > + warn "E: no kernel image found in /boot for '$ver', not setting > default." > + exit 1 > + fi > + echo "$ver" > "$PINNED_KERNEL_CONF" > + echo "Set kernel '$ver' $PINNED_KERNEL_CONF. Use the 'refresh' command > to update the ESPs." > +} > + > +unpin_kernel() { > + rm -f "$PINNED_KERNEL_CONF" > + echo "Removed $PINNED_KERNEL_CONF. Use the 'refresh' command to update > the ESPs." > +} > + > if [ -z "$1" ]; then > usage > exit 0 > @@ -460,6 +488,14 @@ case "$1" in > list_kernels > exit 0 > ;; > + 'pin') > + pin_kernel "$2" > + exit 0 > + ;; > + 'unpin') > + unpin_kernel "$2" > + exit 0 > + ;; > *) > warn "E: invalid 'kernel' subcommand '$cmd'." > warn "" > diff --git a/proxmox-boot/functions b/proxmox-boot/functions > index 27da363..d97a7a1 100755 > --- a/proxmox-boot/functions > +++ b/proxmox-boot/functions > @@ -5,11 +5,13 @@ ESP_LIST="/etc/kernel/proxmox-boot-uuids" > ESPTYPE='c12a7328-f81f-11d2-ba4b-00a0c93ec93b' > > MANUAL_KERNEL_LIST="/etc/kernel/pve-efiboot-manual-kernels" > +PINNED_KERNEL_CONF="/etc/kernel/proxmox-boot-pin" > > MOUNTROOT="${TMPDIR:-/var/tmp}/espmounts" > # relative to the ESP mountpoint > PMX_ESP_DIR="EFI/proxmox" > PMX_LOADER_CONF="loader/loader.conf" > +GRUB_PIN_SNIPPET="/etc/default/grub.d/proxmox-kernel-pin.cfg" > > # adapted from /etc/kernel/postinst.d/apt-auto-removal as present in > # debian's apt package: > @@ -21,6 +23,7 @@ PMX_LOADER_CONF="loader/loader.conf" > # - the second-latest kernel version > # - the latest kernel version of each series (e.g. 4.13, 4.15, 5.0) by > # marking the meta-packages > +# - the currently pinned kernel if any > > kernel_keep_versions() { > eval "$(apt-config shell DPKG Dir::bin::dpkg/f)" > @@ -56,6 +59,8 @@ kernel_keep_versions() { > manual_kernels="$(cat "$MANUAL_KERNEL_LIST")" > fi > > + pinned_kernel="$(get_first_line "$PINNED_KERNEL_CONF")" > + > kernels="$(cat <<-EOF > $running_version > $install_version > @@ -63,6 +68,7 @@ kernel_keep_versions() { > $latest_2_versions > $series_metapackages > $oldseries_latest_kernel > + $pinned_kernel > EOF > )" > > @@ -114,3 +120,34 @@ get_first_line() { > done < "${file}" > echo "$line" > } > + > +set_grub_default() { > + kver="$1" > + > + if [ -z "${kver}" ]; then > + rm -f "${GRUB_PIN_SNIPPET}" > + else > + # grub menu entry ids contain the internal root-device id > + # (e.g. for zfs the GUID of the pool printed in hex) as this > + # as this is independent of the ESP (or grub location) take nit: doubled 'as this' > + # it from /boot/grub/grub.cfg > + root_devid=$(sed -rn "s/.*gnulinux-advanced-(.+)['] \{$/\1/p" \ > + /boot/grub/grub.cfg) > + > entry="gnulinux-advanced-${root_devid}>gnulinux-${kver}-advanced-${root_devid}" > + echo "GRUB_DEFAULT=\"${entry}\"" > "${GRUB_PIN_SNIPPET}" > + fi > +} > + > +set_systemd_boot_default() { > + mountpoint="$1" > + kver="$2" > + if [ -z "${kver}" ]; then > + entry="proxmox-*" > + else > + entry="proxmox-${kver}.conf" > + fi > + > + sed -ri "/^default /{h;s/ .*\$/ ${entry}/};\${x;/^$/{s//default > ${entry}/;H};x}" \ > + "${mountpoint}/$PMX_LOADER_CONF" > + > +} > diff --git a/proxmox-boot/zz-proxmox-boot b/proxmox-boot/zz-proxmox-boot > index db73166..7958a5d 100755 > --- a/proxmox-boot/zz-proxmox-boot > +++ b/proxmox-boot/zz-proxmox-boot > @@ -90,9 +90,14 @@ update_esp_func() { > fi > warn "Copying and configuring kernels on ${path}" > copy_and_config_kernels "${mountpoint}" > + > + pinned_kernel=$(get_first_line "${PINNED_KERNEL_CONF}") > + > if [ -d /sys/firmware/efi ]; then > + set_systemd_boot_default "${mountpoint}" "${pinned_kernel}" > remove_old_kernels_efi "${mountpoint}" > else > + set_grub_default "${pinned_kernel}" > remove_old_kernels_legacy "${mountpoint}" > mount --bind "${mountpoint}" "/boot" > update-grub > -- > 2.30.2 > > > > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel > > > _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel