limine-install: cleanup, improve type hinting (#416188)

This commit is contained in:
Masum Reza 2025-06-23 23:08:45 +05:30 committed by GitHub
parent 3d23a55dbc
commit a2b5af4710
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 53 additions and 59 deletions

View File

@ -6,7 +6,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple
import datetime
import hashlib
import json
import ctypes
from ctypes import CDLL
import os
import psutil
import re
@ -16,20 +16,31 @@ import sys
import tempfile
import textwrap
@dataclass
class BootSpec:
system: str
init: str
kernel: str
kernelParams: List[str]
label: str
toplevel: str
specialisations: Dict[str, "BootSpec"]
initrd: str | None = None
initrdSecrets: str | None = None
limine_dir = None
can_use_direct_paths = False
install_config = json.load(open('@configPath@', 'r'))
libc = ctypes.CDLL("libc.so.6")
libc = CDLL("libc.so.6")
limine_install_dir: Optional[str] = None
can_use_direct_paths = False
paths: Dict[str, bool] = {}
def config(*path: List[str]) -> Optional[Any]:
def config(*path: str) -> Optional[Any]:
result = install_config
for component in path:
result = result[component]
return result
def get_system_path(profile: str = 'system', gen: Optional[str] = None, spec: Optional[str] = None) -> str:
basename = f'{profile}-{gen}-link' if gen is not None else profile
profiles_dir = '/nix/var/nix/profiles'
@ -52,7 +63,7 @@ def get_profiles() -> List[str]:
def get_gens(profile: str = 'system') -> List[Tuple[int, List[str]]]:
nix_env = os.path.join(config('nixPath'), 'bin', 'nix-env')
nix_env = os.path.join(str(config('nixPath')), 'bin', 'nix-env')
output = subprocess.check_output([
nix_env, '--list-generations',
'-p', get_system_path(profile),
@ -66,7 +77,7 @@ def get_gens(profile: str = 'system') -> List[Tuple[int, List[str]]]:
def is_encrypted(device: str) -> bool:
for name, _ in config('luksDevices').items():
for name in config('luksDevices'):
if os.readlink(os.path.join('/dev/mapper', name)) == os.readlink(device):
return True
@ -76,15 +87,13 @@ def is_encrypted(device: str) -> bool:
def is_fs_type_supported(fs_type: str) -> bool:
return fs_type.startswith('vfat')
paths = {}
def get_copied_path_uri(path: str, target: str) -> str:
result = ''
package_id = os.path.basename(os.path.dirname(path))
suffix = os.path.basename(path)
dest_file = f'{package_id}-{suffix}'
dest_path = os.path.join(limine_dir, target, dest_file)
dest_path = os.path.join(str(limine_install_dir), target, dest_file)
if not os.path.exists(dest_path):
copy_file(path, dest_path)
@ -117,20 +126,6 @@ def get_file_uri(profile: str, gen: Optional[str], spec: Optional[str], name: st
def get_kernel_uri(kernel_path: str) -> str:
return get_copied_path_uri(kernel_path, "kernels")
@dataclass
class BootSpec:
system: str
init: str
kernel: str
kernelParams: List[str]
label: str
toplevel: str
specialisations: Dict[str, "BootSpec"]
initrd: str | None = None
initrdSecrets: str | None = None
def bootjson_to_bootspec(bootjson: dict) -> BootSpec:
specialisations = bootjson['org.nixos.specialisation.v1']
specialisations = {k: bootjson_to_bootspec(v) for k, v in specialisations.items()}
@ -150,12 +145,11 @@ def config_entry(levels: int, bootspec: BootSpec, label: str, time: str) -> str:
entry += f'module_path: ' + get_kernel_uri(bootspec.initrd) + '\n'
if bootspec.initrdSecrets:
initrd_secrets_path = limine_dir + '/kernels/' + os.path.basename(toplevel) + '-secrets'
initrd_secrets_path = str(limine_install_dir) + '/kernels/' + os.path.basename(bootspec.toplevel) + '-secrets'
os.makedirs(initrd_secrets_path)
old_umask = os.umask()
os.umask(0o137)
initrd_secrets_path_temp = tempfile.mktemp(os.path.basename(toplevel) + '-secrets')
old_umask = os.umask(0o137)
initrd_secrets_path_temp = tempfile.mktemp(os.path.basename(bootspec.toplevel) + '-secrets')
if os.system(bootspec.initrdSecrets + " " + initrd_secrets_path_temp) != 0:
print(f'warning: failed to create initrd secrets for "{label}"', file=sys.stderr)
@ -235,7 +229,7 @@ def option_from_config(name: str, config_path: List[str], conversion: Callable[[
def install_bootloader() -> None:
global limine_dir
global limine_install_dir
boot_fs = None
@ -244,9 +238,9 @@ def install_bootloader() -> None:
boot_fs = fs
if config('efiSupport'):
limine_dir = os.path.join(config('efiMountPoint'), 'limine')
limine_install_dir = os.path.join(str(config('efiMountPoint')), 'limine')
elif boot_fs and is_fs_type_supported(boot_fs['fsType']) and not is_encrypted(boot_fs['device']):
limine_dir = '/boot/limine'
limine_install_dir = '/boot/limine'
else:
possible_causes = []
if not boot_fs:
@ -266,14 +260,14 @@ def install_bootloader() -> None:
partition formatted as FAT.
'''))
if config('secureBoot')['enable'] and not config('secureBoot')['createAndEnrollKeys'] and not os.path.exists("/var/lib/sbctl"):
if config('secureBoot', 'enable') and not config('secureBoot', 'createAndEnrollKeys') and not os.path.exists("/var/lib/sbctl"):
print("There are no sbctl secure boot keys present. Please generate some.")
sys.exit(1)
if not os.path.exists(limine_dir):
os.makedirs(limine_dir)
if not os.path.exists(limine_install_dir):
os.makedirs(limine_install_dir)
else:
for dir, dirs, files in os.walk(limine_dir, topdown=True):
for dir, dirs, files in os.walk(limine_install_dir, topdown=True):
for file in files:
paths[os.path.join(dir, file)] = False
@ -290,7 +284,7 @@ def install_bootloader() -> None:
last_gen_json = json.load(open(os.path.join(get_system_path('system', last_gen), 'boot.json'), 'r'))
last_gen_boot_spec = bootjson_to_bootspec(last_gen_json)
config_file = config('extraConfig') + '\n'
config_file = str(config('extraConfig')) + '\n'
config_file += textwrap.dedent(f'''
timeout: {timeout}
editor_enabled: {editor_enabled}
@ -334,10 +328,10 @@ def install_bootloader() -> None:
config_file += generate_config_entry(profile, gen, isFirst)
isFirst = False
config_file_path = os.path.join(limine_dir, 'limine.conf')
config_file_path = os.path.join(limine_install_dir, 'limine.conf')
config_file += '\n# NixOS boot entries end here\n\n'
config_file += config('extraEntries')
config_file += str(config('extraEntries'))
with open(f"{config_file_path}.tmp", 'w') as file:
file.truncate()
@ -349,13 +343,14 @@ def install_bootloader() -> None:
paths[config_file_path] = True
for dest_path, source_path in config('additionalFiles').items():
dest_path = os.path.join(limine_dir, dest_path)
dest_path = os.path.join(limine_install_dir, dest_path)
copy_file(source_path, dest_path)
limine_binary = os.path.join(config('liminePath'), 'bin', 'limine')
limine_binary = os.path.join(str(config('liminePath')), 'bin', 'limine')
cpu_family = config('hostArchitecture', 'family')
if config('efiSupport'):
boot_file = ""
if cpu_family == 'x86':
if config('hostArchitecture', 'bits') == 32:
boot_file = 'BOOTIA32.EFI'
@ -369,8 +364,8 @@ def install_bootloader() -> None:
else:
raise Exception(f'Unsupported CPU family: {cpu_family}')
efi_path = os.path.join(config('liminePath'), 'share', 'limine', boot_file)
dest_path = os.path.join(config('efiMountPoint'), 'efi', 'boot' if config('efiRemovable') else 'limine', boot_file)
efi_path = os.path.join(str(config('liminePath')), 'share', 'limine', boot_file)
dest_path = os.path.join(str(config('efiMountPoint')), 'efi', 'boot' if config('efiRemovable') else 'limine', boot_file)
copy_file(efi_path, dest_path)
@ -383,9 +378,9 @@ def install_bootloader() -> None:
print('error: failed to enroll limine config.', file=sys.stderr)
sys.exit(1)
if config('secureBoot')['enable']:
sbctl = os.path.join(config('secureBoot')['sbctl'], 'bin', 'sbctl')
if config('secureBoot')['createAndEnrollKeys']:
if config('secureBoot', 'enable'):
sbctl = os.path.join(str(config('secureBoot', 'sbctl')), 'bin', 'sbctl')
if config('secureBoot', 'createAndEnrollKeys'):
print("TEST MODE: creating and enrolling keys")
try:
subprocess.run([sbctl, 'create-keys'])
@ -412,8 +407,8 @@ def install_bootloader() -> None:
if config('efiRemovable'):
print('note: boot.loader.limine.efiInstallAsRemovable is true, no need to add EFI entry.')
else:
efibootmgr = os.path.join(config('efiBootMgrPath'), 'bin', 'efibootmgr')
efi_partition = find_mounted_device(config('efiMountPoint'))
efibootmgr = os.path.join(str(config('efiBootMgrPath')), 'bin', 'efibootmgr')
efi_partition = find_mounted_device(str(config('efiMountPoint')))
efi_disk = find_disk_device(efi_partition)
efibootmgr_output = subprocess.check_output([efibootmgr], stderr=subprocess.STDOUT, universal_newlines=True)
@ -457,24 +452,24 @@ def install_bootloader() -> None:
if cpu_family != 'x86':
raise Exception(f'Unsupported CPU family for BIOS install: {cpu_family}')
limine_sys = os.path.join(config('liminePath'), 'share', 'limine', 'limine-bios.sys')
limine_sys_dest = os.path.join(limine_dir, 'limine-bios.sys')
limine_sys = os.path.join(str(config('liminePath')), 'share', 'limine', 'limine-bios.sys')
limine_sys_dest = os.path.join(limine_install_dir, 'limine-bios.sys')
copy_file(limine_sys, limine_sys_dest)
device = config('biosDevice')
device = str(config('biosDevice'))
if device == 'nodev':
print("note: boot.loader.limine.biosSupport is set, but device is set to nodev, only the stage 2 bootloader will be installed.", file=sys.stderr)
return
else:
limine_deploy_args = [limine_binary, 'bios-install', device]
limine_deploy_args: List[str] = [limine_binary, 'bios-install', device]
if config('partitionIndex'):
limine_deploy_args.append(str(config('partitionIndex')))
if config('forceMbr'):
limine_deploy_args += '--force-mbr'
limine_deploy_args.append('--force-mbr')
try:
subprocess.run(limine_deploy_args)
@ -496,9 +491,9 @@ def main() -> None:
# it can leave the system in an unbootable state, when a crash/outage
# happens shortly after an update. To decrease the likelihood of this
# event sync the efi filesystem after each update.
rc = libc.syncfs(os.open(f"{config('efiMountPoint')}", os.O_RDONLY))
rc = libc.syncfs(os.open(f"{str(config('efiMountPoint'))}", os.O_RDONLY))
if rc != 0:
print(f"could not sync {config('efiMountPoint')}: {os.strerror(rc)}", file=sys.stderr)
print(f"could not sync {str(config('efiMountPoint'))}: {os.strerror(rc)}", file=sys.stderr)
if __name__ == '__main__':
main()

View File

@ -14,7 +14,7 @@ let
liminePath = cfg.package;
efiMountPoint = efi.efiSysMountPoint;
fileSystems = config.fileSystems;
luksDevices = config.boot.initrd.luks.devices;
luksDevices = builtins.attrNames config.boot.initrd.luks.devices;
canTouchEfiVariables = efi.canTouchEfiVariables;
efiSupport = cfg.efiSupport;
efiRemovable = cfg.efiInstallAsRemovable;
@ -45,6 +45,7 @@ in
options.boot.loader.limine = {
enable = lib.mkEnableOption "the Limine Bootloader";
package = lib.mkPackageOption pkgs "limine" { };
enableEditor = lib.mkEnableOption null // {
description = ''
@ -117,8 +118,6 @@ in
'';
};
package = lib.mkPackageOption pkgs "limine" { };
efiSupport = lib.mkEnableOption null // {
default = pkgs.stdenv.hostPlatform.isEfi;
defaultText = lib.literalExpression "pkgs.stdenv.hostPlatform.isEfi";