|
@@ -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())
|