Unverified Commit a2b5af47 authored by Masum Reza's avatar Masum Reza Committed by GitHub
Browse files

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

parent 3d23a55d
Loading
Loading
Loading
Loading
+51 −56
Original line number Diff line number Diff line
@@ -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()
+2 −3
Original line number Diff line number Diff line
@@ -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";