diff --git a/bin/proxmox-boot-tool b/bin/proxmox-boot-tool index 93760fb..329df42 100755 --- a/bin/proxmox-boot-tool +++ b/bin/proxmox-boot-tool @@ -286,6 +286,13 @@ list_kernels() { echo "" echo "Automatically selected kernels:" echo "$boot_kernels" + + pinned_kernel="$(get_first_line "$PINNED_KERNEL_CONF")" + if [ -n "$pinned_kernel" ]; then + echo "" + echo "Pinned kernel:" + echo "${pinned_kernel}" + fi } usage() { @@ -296,6 +303,8 @@ usage() { warn " $0 clean [--dry-run]" warn " $0 refresh [--hook ]" warn " $0 kernel " + warn " $0 kernel pin " + warn " $0 kernel unpin" warn " $0 kernel list" warn " $0 status [--quiet]" warn " $0 help" @@ -323,6 +332,15 @@ help() { echo " add/remove pve-kernel with ABI 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 "" + echo "USAGE: $0 kernel pin " + echo "" + echo " pin pve-kernel with ABI as the default entry to be booted." + echo " NOTE: you need to manually run 'refresh' once you're finished with pinning kernels" + echo "" + echo "USAGE: $0 kernel unpin" + echo "" + echo " unpin sets the latest kernel as the default entry (undoes a previous pin)" + echo "" echo "USAGE: $0 kernel list" echo "" echo " list kernel versions currently selected for inclusion on ESPs." @@ -392,6 +410,28 @@ status() { fi } +pin_kernel() { + ver="$1" + + if [ -z "$ver" ]; then + warn "E: 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 +500,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..5a56b74 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 is independent of the ESP (or grub location) + # take 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 + + # replaces the current default entry, if one exists else append it at the end of the file + 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