Merge pull request #323421 from Mic92/systemd-boot-builder
This commit is contained in:
@@ -12,7 +12,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
import json
|
import json
|
||||||
from typing import NamedTuple, Dict, List
|
from typing import NamedTuple, Any
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
# These values will be replaced with actual values during the package build
|
# These values will be replaced with actual values during the package build
|
||||||
@@ -21,7 +21,7 @@ BOOT_MOUNT_POINT = "@bootMountPoint@"
|
|||||||
LOADER_CONF = f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf" # Always stored on the ESP
|
LOADER_CONF = f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf" # Always stored on the ESP
|
||||||
NIXOS_DIR = "@nixosDir@"
|
NIXOS_DIR = "@nixosDir@"
|
||||||
TIMEOUT = "@timeout@"
|
TIMEOUT = "@timeout@"
|
||||||
EDITOR = "@editor@" == "1"
|
EDITOR = "@editor@" == "1" # noqa: PLR0133
|
||||||
CONSOLE_MODE = "@consoleMode@"
|
CONSOLE_MODE = "@consoleMode@"
|
||||||
BOOTSPEC_TOOLS = "@bootspecTools@"
|
BOOTSPEC_TOOLS = "@bootspecTools@"
|
||||||
DISTRO_NAME = "@distroName@"
|
DISTRO_NAME = "@distroName@"
|
||||||
@@ -38,17 +38,22 @@ class BootSpec:
|
|||||||
init: str
|
init: str
|
||||||
initrd: str
|
initrd: str
|
||||||
kernel: str
|
kernel: str
|
||||||
kernelParams: List[str]
|
kernelParams: list[str] # noqa: N815
|
||||||
label: str
|
label: str
|
||||||
system: str
|
system: str
|
||||||
toplevel: str
|
toplevel: str
|
||||||
specialisations: Dict[str, "BootSpec"]
|
specialisations: dict[str, "BootSpec"]
|
||||||
sortKey: str
|
sortKey: str # noqa: N815
|
||||||
initrdSecrets: str | None = None
|
initrdSecrets: str | None = None # noqa: N815
|
||||||
|
|
||||||
|
|
||||||
libc = ctypes.CDLL("libc.so.6")
|
libc = ctypes.CDLL("libc.so.6")
|
||||||
|
|
||||||
|
FILE = None | int
|
||||||
|
|
||||||
|
def run(cmd: list[str], stdout: FILE = None) -> subprocess.CompletedProcess[str]:
|
||||||
|
return subprocess.run(cmd, check=True, text=True, stdout=stdout)
|
||||||
|
|
||||||
class SystemIdentifier(NamedTuple):
|
class SystemIdentifier(NamedTuple):
|
||||||
profile: str | None
|
profile: str | None
|
||||||
generation: int
|
generation: int
|
||||||
@@ -112,17 +117,20 @@ def get_bootspec(profile: str | None, generation: int) -> BootSpec:
|
|||||||
boot_json_f = open(boot_json_path, 'r')
|
boot_json_f = open(boot_json_path, 'r')
|
||||||
bootspec_json = json.load(boot_json_f)
|
bootspec_json = json.load(boot_json_f)
|
||||||
else:
|
else:
|
||||||
boot_json_str = subprocess.check_output([
|
boot_json_str = run(
|
||||||
f"{BOOTSPEC_TOOLS}/bin/synthesize",
|
[
|
||||||
"--version",
|
f"{BOOTSPEC_TOOLS}/bin/synthesize",
|
||||||
"1",
|
"--version",
|
||||||
system_directory,
|
"1",
|
||||||
"/dev/stdout"],
|
system_directory,
|
||||||
universal_newlines=True)
|
"/dev/stdout",
|
||||||
|
],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
).stdout
|
||||||
bootspec_json = json.loads(boot_json_str)
|
bootspec_json = json.loads(boot_json_str)
|
||||||
return bootspec_from_json(bootspec_json)
|
return bootspec_from_json(bootspec_json)
|
||||||
|
|
||||||
def bootspec_from_json(bootspec_json: Dict) -> BootSpec:
|
def bootspec_from_json(bootspec_json: dict[str, Any]) -> BootSpec:
|
||||||
specialisations = bootspec_json['org.nixos.specialisation.v1']
|
specialisations = bootspec_json['org.nixos.specialisation.v1']
|
||||||
specialisations = {k: bootspec_from_json(v) for k, v in specialisations.items()}
|
specialisations = {k: bootspec_from_json(v) for k, v in specialisations.items()}
|
||||||
systemdBootExtension = bootspec_json.get('org.nixos.systemd-boot', {})
|
systemdBootExtension = bootspec_json.get('org.nixos.systemd-boot', {})
|
||||||
@@ -157,7 +165,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if bootspec.initrdSecrets is not None:
|
if bootspec.initrdSecrets is not None:
|
||||||
subprocess.check_call([bootspec.initrdSecrets, f"{BOOT_MOUNT_POINT}%s" % (initrd)])
|
run([bootspec.initrdSecrets, f"{BOOT_MOUNT_POINT}%s" % (initrd)])
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
if current:
|
if current:
|
||||||
print("failed to create initrd secrets!", file=sys.stderr)
|
print("failed to create initrd secrets!", file=sys.stderr)
|
||||||
@@ -192,13 +200,17 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
|
|||||||
|
|
||||||
|
|
||||||
def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
|
def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
|
||||||
gen_list = subprocess.check_output([
|
gen_list = run(
|
||||||
f"{NIX}/bin/nix-env",
|
[
|
||||||
"--list-generations",
|
f"{NIX}/bin/nix-env",
|
||||||
"-p",
|
"--list-generations",
|
||||||
"/nix/var/nix/profiles/%s" % ("system-profiles/" + profile if profile else "system")],
|
"-p",
|
||||||
universal_newlines=True)
|
"/nix/var/nix/profiles/%s"
|
||||||
gen_lines = gen_list.split('\n')
|
% ("system-profiles/" + profile if profile else "system"),
|
||||||
|
],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
).stdout
|
||||||
|
gen_lines = gen_list.split("\n")
|
||||||
gen_lines.pop()
|
gen_lines.pop()
|
||||||
|
|
||||||
configurationLimit = CONFIGURATION_LIMIT
|
configurationLimit = CONFIGURATION_LIMIT
|
||||||
@@ -230,10 +242,10 @@ def remove_old_entries(gens: list[SystemIdentifier]) -> None:
|
|||||||
gen_number = int(rex_generation.sub(r"\1", path))
|
gen_number = int(rex_generation.sub(r"\1", path))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
continue
|
||||||
if not (prof, gen_number, None) in gens:
|
if (prof, gen_number, None) not in gens:
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
for path in glob.iglob(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/*"):
|
for path in glob.iglob(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/*"):
|
||||||
if not path in known_paths and not os.path.isdir(path):
|
if path not in known_paths and not os.path.isdir(path):
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
|
|
||||||
@@ -263,9 +275,7 @@ def install_bootloader(args: argparse.Namespace) -> None:
|
|||||||
# be there on newly installed systems, so let's generate one so that
|
# be there on newly installed systems, so let's generate one so that
|
||||||
# bootctl can find it and we can also pass it to write_entry() later.
|
# bootctl can find it and we can also pass it to write_entry() later.
|
||||||
cmd = [f"{SYSTEMD}/bin/systemd-machine-id-setup", "--print"]
|
cmd = [f"{SYSTEMD}/bin/systemd-machine-id-setup", "--print"]
|
||||||
machine_id = subprocess.run(
|
machine_id = run(cmd, stdout=subprocess.PIPE).stdout.rstrip()
|
||||||
cmd, text=True, check=True, stdout=subprocess.PIPE
|
|
||||||
).stdout.rstrip()
|
|
||||||
|
|
||||||
if os.getenv("NIXOS_INSTALL_GRUB") == "1":
|
if os.getenv("NIXOS_INSTALL_GRUB") == "1":
|
||||||
warnings.warn("NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER", DeprecationWarning)
|
warnings.warn("NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER", DeprecationWarning)
|
||||||
@@ -288,11 +298,20 @@ def install_bootloader(args: argparse.Namespace) -> None:
|
|||||||
if os.path.exists(LOADER_CONF):
|
if os.path.exists(LOADER_CONF):
|
||||||
os.unlink(LOADER_CONF)
|
os.unlink(LOADER_CONF)
|
||||||
|
|
||||||
subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["install"])
|
run(
|
||||||
|
[f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"]
|
||||||
|
+ bootctl_flags
|
||||||
|
+ ["install"]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# Update bootloader to latest if needed
|
# Update bootloader to latest if needed
|
||||||
available_out = subprocess.check_output([f"{SYSTEMD}/bin/bootctl", "--version"], universal_newlines=True).split()[2]
|
available_out = run(
|
||||||
installed_out = subprocess.check_output([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}", "status"], universal_newlines=True)
|
[f"{SYSTEMD}/bin/bootctl", "--version"], stdout=subprocess.PIPE
|
||||||
|
).stdout.split()[2]
|
||||||
|
installed_out = run(
|
||||||
|
[f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}", "status"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
).stdout
|
||||||
|
|
||||||
# See status_binaries() in systemd bootctl.c for code which generates this
|
# See status_binaries() in systemd bootctl.c for code which generates this
|
||||||
installed_match = re.search(r"^\W+File:.*/EFI/(?:BOOT|systemd)/.*\.efi \(systemd-boot ([\d.]+[^)]*)\)$",
|
installed_match = re.search(r"^\W+File:.*/EFI/(?:BOOT|systemd)/.*\.efi \(systemd-boot ([\d.]+[^)]*)\)$",
|
||||||
@@ -311,7 +330,11 @@ def install_bootloader(args: argparse.Namespace) -> None:
|
|||||||
|
|
||||||
if installed_version < available_version:
|
if installed_version < available_version:
|
||||||
print("updating systemd-boot from %s to %s" % (installed_version, available_version))
|
print("updating systemd-boot from %s to %s" % (installed_version, available_version))
|
||||||
subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["update"])
|
run(
|
||||||
|
[f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"]
|
||||||
|
+ bootctl_flags
|
||||||
|
+ ["update"]
|
||||||
|
)
|
||||||
|
|
||||||
os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}", exist_ok=True)
|
os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}", exist_ok=True)
|
||||||
os.makedirs(f"{BOOT_MOUNT_POINT}/loader/entries", exist_ok=True)
|
os.makedirs(f"{BOOT_MOUNT_POINT}/loader/entries", exist_ok=True)
|
||||||
@@ -362,7 +385,7 @@ def install_bootloader(args: argparse.Namespace) -> None:
|
|||||||
|
|
||||||
os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files", exist_ok=True)
|
os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files", exist_ok=True)
|
||||||
|
|
||||||
subprocess.check_call(COPY_EXTRA_FILES)
|
run([COPY_EXTRA_FILES])
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
@@ -370,7 +393,7 @@ def main() -> None:
|
|||||||
parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f"The default {DISTRO_NAME} config to boot")
|
parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f"The default {DISTRO_NAME} config to boot")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
subprocess.check_call(CHECK_MOUNTPOINTS)
|
run([CHECK_MOUNTPOINTS])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
install_bootloader(args)
|
install_bootloader(args)
|
||||||
|
|||||||
Reference in New Issue
Block a user