From 5241998eee3deefb3098e9f3b5363e5b629b65bb Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Tue, 6 Feb 2024 22:49:49 +0100 Subject: [PATCH] pkg: fedora: Update kernel-watchdog service It seems that I just can't stop finding weird btrfs setups that break the current script. When you use btrfs with a default subvolume, GRUB will use normal paths, like on ext4. So instead of /@/boot/vmlinuz you have /boot/vmlinuz. The issue is that grub2-mkrelpath doesn't know about that. The script would have to parse the GRUB configuration and fstab, to figure out if btrfs is used with a default subvolume, and then add a flag to the command depending on that. However, instead of adding yet another workaround, let's just forget about grubby and do what we want to do ourselves. Because, as it turns out, not taking a weird detour over the path that GRUB uses to load the kernel makes everything much easier. Since this means that we are dealing with even more filepath and string mangling, I decided to rewrite the script in python. It would work fine in bash, but it would be even more spaghetti code that I don't really want to write. And python is always available in Fedora anyway, because DNF is written in it. --- .../files/linux-surface-default-watchdog.py | 87 +++++++++++++++++++ .../linux-surface-default-watchdog.service | 2 +- .../files/linux-surface-default-watchdog.sh | 23 ----- .../patches/0004-default-kernel.patch | 4 +- 4 files changed, 90 insertions(+), 26 deletions(-) create mode 100755 pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.py delete mode 100755 pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.sh diff --git a/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.py b/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.py new file mode 100755 index 000000000..6b786bb07 --- /dev/null +++ b/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path +from typing import Any + + +def grub2_editenv(*args: Any, **kwargs: Any) -> str: + subprocess.run(["grub2-editenv", *args], check=True, **kwargs) + + +def main() -> int: + boot: Path = Path("/boot") + mid: Path = Path("/etc/machine-id") + + if not boot.exists(): + print("ERROR: /boot directory does not exist") + return 1 + + if not mid.exists(): + print("ERROR: /etc/machine-id does not exist") + return 1 + + blsdir: Path = boot / "loader" / "entries" + + if not blsdir.exists(): + print("ERROR: /boot/loader/entries does not exist") + return 1 + + try: + grub2_editenv("--help", capture_output=True) + except: + print("ERROR: grub2-editenv is not working") + return 1 + + # Get list of surface kernels sorted by timestamp. + # + # We use creation time here because it represents when the kernel was installed. + # Modification time can be a bit wonky and seems to correspond to the build date. + kernels: list[Path] = sorted( + boot.glob("vmlinuz-*.surface.*"), + key=lambda x: x.stat().st_ctime, + reverse=True, + ) + + if len(kernels) == 0: + print("ERROR: Failed to find a surface kernel") + return 1 + + # The saved_entry property from grubenv determines what kernel is booted by default. + # Its value is the filename of the BLS entry in /boot/loader/entries minus the file extension. + # + # The BLS files are named using a combination of the machine ID and the version string + # of the kernel that is being booted. Since we have the vmlinux, we can get the version + # from its name, and the machine ID from /etc/machine-id. + # + # This allows setting the default kernel without calling grubby or having to figure out + # which path GRUB will use to boot the kernel. + + kernel: Path = kernels[0] + + machineid: str = mid.read_text().strip() + version: str = kernel.name.lstrip("vmlinuz-") + + blscfg: Path = blsdir / "{}-{}.conf".format(machineid, version) + + # Make sure the config really exists + if not blscfg.exists(): + print("ERROR: {} does not exist".format(blscfg)) + return 1 + + print("Kernel: {}".format(kernel)) + print("BLS entry: {}".format(blscfg)) + + grub2_editenv("-", "set", "saved_entry={}".format(blscfg.stem)) + + # Update timestamp for rEFInd and ensure it is marked as latest across all kernels + kernel.touch(exist_ok=True) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.service b/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.service index 2c8e6c2a4..008b00ee0 100644 --- a/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.service +++ b/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.service @@ -3,4 +3,4 @@ Description=linux-surface default kernel watchdog [Service] Type=oneshot -ExecStart=/usr/bin/linux-surface-default-watchdog.sh +ExecStart=/usr/bin/linux-surface-default-watchdog.py diff --git a/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.sh b/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.sh deleted file mode 100755 index b0a55dc68..000000000 --- a/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Get list of surface kernels with timestamp -KERNELS="$( - find /boot -maxdepth 1 -name 'vmlinuz-*.surface.*' -print0 | xargs -0 -I '{}' \ - stat -c "%W %n" {} -)" - -# Sort by timestamp -KERNELS="$(echo "${KERNELS}" | sort -n)" - -# Get latest kernel (last line) and extract the path -VMLINUX="$(echo "${KERNELS}" | tail -n1 | cut -d' ' -f2)" - -echo "${VMLINUX}" - -# update GRUB config -grubby --set-default "$(grub2-mkrelpath "${VMLINUX}")" - -# Update timestamp for rEFInd -# Ensure it's marked as latest across all kernels, not just surface ones -touch "${VMLINUX}" diff --git a/pkg/fedora/kernel-surface/patches/0004-default-kernel.patch b/pkg/fedora/kernel-surface/patches/0004-default-kernel.patch index bd0b940d5..7b273304e 100644 --- a/pkg/fedora/kernel-surface/patches/0004-default-kernel.patch +++ b/pkg/fedora/kernel-surface/patches/0004-default-kernel.patch @@ -19,7 +19,7 @@ index 43dce82a9d36..28df94e561d4 100644 +Source4100: linux-surface-default-watchdog.path +Source4101: linux-surface-default-watchdog.service -+Source4102: linux-surface-default-watchdog.sh ++Source4102: linux-surface-default-watchdog.py +Source4103: 90-linux-surface-default-watchdog.preset + ## Patches needed for building this package @@ -81,7 +81,7 @@ index 43dce82a9d36..28df94e561d4 100644 %endif +%files default-watchdog -+%{_bindir}/linux-surface-default-watchdog.sh ++%{_bindir}/linux-surface-default-watchdog.py +%{_unitdir}/linux-surface-default-watchdog.path +%{_unitdir}/linux-surface-default-watchdog.service +%{_presetdir}/90-linux-surface-default-watchdog.preset