From 5183f69b3966ffeea8e1272f4a2bfc1488740f80 Mon Sep 17 00:00:00 2001 From: NullBite Date: Wed, 18 Dec 2024 02:28:39 -0500 Subject: [PATCH] system: add bootnext script with sudoers cfg --- system/common/bootnext.nix | 119 +++++++++++++++++++++++++++++++++++++ system/common/default.nix | 1 + 2 files changed, 120 insertions(+) create mode 100644 system/common/bootnext.nix diff --git a/system/common/bootnext.nix b/system/common/bootnext.nix new file mode 100644 index 0000000..5dea5bd --- /dev/null +++ b/system/common/bootnext.nix @@ -0,0 +1,119 @@ +{ config, lib, pkgs, options, ... }: +let + inherit (lib) types escapeShellArg; + cfg = config.nixfiles.common.bootnext; + bootNextScriptMain = pkgs.writeShellScript "bootnext-wrapped" '' + set -Eeuxo pipefail + + PATH=${lib.escapeShellArg (with pkgs; lib.makeBinPath [ gnugrep coreutils efibootmgr ])} + export PATH + + function do_bootnext() { + uuid="$1" + shift + entryName="$1" + shift + + efibootmgr -n "$(efibootmgr | grep -Fi "$uuid" | grep -F "$entryName" | cut -d' ' -f1 | tr -dc '[:digit:]')" + } + + case "$1" in + ${lib.concatStringsSep "\n" ( + lib.mapAttrsToList (name: value: + " ${escapeShellArg name}) do_bootnext ${escapeShellArg value.efiPartUUID} ${escapeShellArg value.name} ;;" + ) cfg.entries + )} + *) echo "Boot entry \"$1\" not configured."; exit 1;; + esac + ''; + + bootNextScript = pkgs.writeShellScriptBin "bootnext" '' + # this wrapper is needed because the sudoers config needs the path to the + # actual script and self referencing is a pain. this way we can guarantee + # that the script passed is exactly the same as the one in the sudoers + # config. i could use realpath but this is probably safer since it is not + # evaluated at runtime. who knows. + if [[ "$(id -u)" -ne 0 ]]; then + exec sudo ${escapeShellArg bootNextScriptMain} "$@" + else + exec ${escapeShellArg bootNextScriptMain} "$@" + fi + ''; + +in +{ + options = { + nixfiles.common.bootnext = { + enable = lib.mkOption { + description = '' + Whether to enable the bootnext wrapper command for controlling boot order + ''; + type = types.bool; + default = false; + example = true; + }; + enableDesktopEntries = lib.mkEnableOption "generation of bootnext Desktop entries"; + entries = let + entryModule = {name, config, ... }: { + options = let + uuidType = with types; lib.mkOptionType { + name = "uuid"; + description = "UUID"; + descriptionClass = "noun"; + check = let + uuidRegex = "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$"; + in x: str.check x && (builtins.match uuidRegex x) != null; + inherit (str) merge; + }; + in { + efiPartUUID = lib.mkOption { + description = "UUID of EFI partition containing boot entry"; + type = uuidType; + apply = lib.strings.toLower; + }; + name = lib.mkOption { + description = "Name of boot entry as it appears in efibootmgr"; + type = types.str; + example = "Windows Boot Manager"; + }; + desktopEntry = { + enable = lib.mkOption { + description = "Whether to generate this desktop entry."; + type = types.bool; + default = true; + example = false; + }; + name = lib.mkOption { + description = "Display name of boot entry for desktop entry."; + type = types.str; + default = config.name; + example = "Windows"; + }; + icon = lib.mkOption { + description = "Path or name of icon to use for desktop entry"; + type = with types; nullOr str; + default = null; + }; + }; + }; + }; + in lib.mkOption { + description = "bootnext entry"; + type = with types; attrsOf (submodule entryModule); + }; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ bootNextScript ]; + + security.sudo.extraRules = lib.mkAfter [ + { + commands = [ + { command = "${bootNextScriptMain}"; options = [ "NOPASSWD" ]; } + ]; + groups = [ "wheel" ]; + } + ]; + }; +} diff --git a/system/common/default.nix b/system/common/default.nix index e048c72..02b7218 100644 --- a/system/common/default.nix +++ b/system/common/default.nix @@ -5,5 +5,6 @@ ./remote.nix ./wm.nix ./nix.nix + ./bootnext.nix ]; }