linux-surface/pkg/fedora/kernel-surface/files/linux-surface-default-watchdog.py
Dorian Stoll 5241998eee
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.
2024-02-06 23:25:26 +01:00

88 lines
2.3 KiB
Python
Executable file

#!/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())