linux-surface-default-watchdog.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. #!/usr/bin/env python3
  2. from __future__ import annotations
  3. import subprocess
  4. import sys
  5. from pathlib import Path
  6. from typing import Any
  7. def grub2_editenv(*args: Any, **kwargs: Any) -> str:
  8. subprocess.run(["grub2-editenv", *args], check=True, **kwargs)
  9. def main() -> int:
  10. boot: Path = Path("/boot")
  11. mid: Path = Path("/etc/machine-id")
  12. if not boot.exists():
  13. print("ERROR: /boot directory does not exist")
  14. return 1
  15. if not mid.exists():
  16. print("ERROR: /etc/machine-id does not exist")
  17. return 1
  18. blsdir: Path = boot / "loader" / "entries"
  19. if not blsdir.exists():
  20. print("ERROR: /boot/loader/entries does not exist")
  21. return 1
  22. try:
  23. grub2_editenv("--help", capture_output=True)
  24. except:
  25. print("ERROR: grub2-editenv is not working")
  26. return 1
  27. # Get list of surface kernels sorted by timestamp.
  28. #
  29. # We use creation time here because it represents when the kernel was installed.
  30. # Modification time can be a bit wonky and seems to correspond to the build date.
  31. kernels: list[Path] = sorted(
  32. boot.glob("vmlinuz-*.surface.*"),
  33. key=lambda x: x.stat().st_ctime,
  34. reverse=True,
  35. )
  36. if len(kernels) == 0:
  37. print("ERROR: Failed to find a surface kernel")
  38. return 1
  39. # The saved_entry property from grubenv determines what kernel is booted by default.
  40. # Its value is the filename of the BLS entry in /boot/loader/entries minus the file extension.
  41. #
  42. # The BLS files are named using a combination of the machine ID and the version string
  43. # of the kernel that is being booted. Since we have the vmlinux, we can get the version
  44. # from its name, and the machine ID from /etc/machine-id.
  45. #
  46. # This allows setting the default kernel without calling grubby or having to figure out
  47. # which path GRUB will use to boot the kernel.
  48. kernel: Path = kernels[0]
  49. machineid: str = mid.read_text().strip()
  50. version: str = kernel.name.lstrip("vmlinuz-")
  51. blscfg: Path = blsdir / "{}-{}.conf".format(machineid, version)
  52. # Make sure the config really exists
  53. if not blscfg.exists():
  54. print("ERROR: {} does not exist".format(blscfg))
  55. return 1
  56. print("Kernel: {}".format(kernel))
  57. print("BLS entry: {}".format(blscfg))
  58. grub2_editenv("-", "set", "saved_entry={}".format(blscfg.stem))
  59. # Update timestamp for rEFInd and ensure it is marked as latest across all kernels
  60. kernel.touch(exist_ok=True)
  61. return 0
  62. if __name__ == "__main__":
  63. sys.exit(main())