limine-install: cleanup, improve type hinting (#416188)
This commit is contained in:
parent
3d23a55dbc
commit
a2b5af4710
@ -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()
|
||||
|
@ -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";
|
||||
|
Loading…
x
Reference in New Issue
Block a user