aboutsummaryrefslogtreecommitdiff
path: root/infra/libkookie/nixpkgs/nixos/modules/system/boot
diff options
context:
space:
mode:
authorMx Kookie <kookie@spacekookie.de>2020-10-31 19:35:09 +0100
committerMx Kookie <kookie@spacekookie.de>2020-10-31 19:35:09 +0100
commitc4625b175f8200f643fd6e11010932ea44c78433 (patch)
treebce3f89888c8ac3991fa5569a878a9eab6801ccc /infra/libkookie/nixpkgs/nixos/modules/system/boot
parent49f735974dd103039ddc4cb576bb76555164a9e7 (diff)
parentd661aa56a8843e991261510c1bb28fdc2f6975ae (diff)
Add 'infra/libkookie/' from commit 'd661aa56a8843e991261510c1bb28fdc2f6975ae'
git-subtree-dir: infra/libkookie git-subtree-mainline: 49f735974dd103039ddc4cb576bb76555164a9e7 git-subtree-split: d661aa56a8843e991261510c1bb28fdc2f6975ae
Diffstat (limited to 'infra/libkookie/nixpkgs/nixos/modules/system/boot')
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/binfmt.nix277
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/emergency-mode.nix37
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/grow-partition.nix53
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-network.nix148
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-openvpn.nix81
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix210
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/kernel.nix335
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/kernel_config.nix137
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/kexec.nix32
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/efi.nix20
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh106
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix65
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix65
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix8
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh151
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix821
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl754
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/ipxe.nix64
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/memtest.nix116
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/init-script/init-script-builder.sh93
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/init-script/init-script.nix51
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/loader.nix20
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.nix10
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh143
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix108
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix37
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.sh38
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py256
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix143
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/luksroot.nix897
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/modprobe.nix68
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/networkd.nix1578
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/pbkdf2-sha512.c38
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/plymouth.nix173
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/resolved.nix181
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/shutdown.nix27
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-1-init.sh623
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-1.nix619
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-2-init.sh172
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-2.nix95
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-lib.nix235
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-nspawn.nix133
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-unit-options.nix528
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd.nix1178
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/timesyncd.nix71
-rw-r--r--infra/libkookie/nixpkgs/nixos/modules/system/boot/tmp.nix39
46 files changed, 11034 insertions, 0 deletions
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/binfmt.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/binfmt.nix
new file mode 100644
index 000000000000..9eeae0c3ef44
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/binfmt.nix
@@ -0,0 +1,277 @@
+{ config, lib, pkgs, ... }:
+let
+ inherit (lib) mkOption types optionalString;
+
+ cfg = config.boot.binfmt;
+
+ makeBinfmtLine = name: { recognitionType, offset, magicOrExtension
+ , mask, preserveArgvZero, openBinary
+ , matchCredentials, fixBinary, ...
+ }: let
+ type = if recognitionType == "magic" then "M" else "E";
+ offset' = toString offset;
+ mask' = toString mask;
+ interpreter = "/run/binfmt/${name}";
+ flags = if !(matchCredentials -> openBinary)
+ then throw "boot.binfmt.registrations.${name}: you can't specify openBinary = false when matchCredentials = true."
+ else optionalString preserveArgvZero "P" +
+ optionalString (openBinary && !matchCredentials) "O" +
+ optionalString matchCredentials "C" +
+ optionalString fixBinary "F";
+ in ":${name}:${type}:${offset'}:${magicOrExtension}:${mask'}:${interpreter}:${flags}";
+
+ activationSnippet = name: { interpreter, ... }:
+ "ln -sf ${interpreter} /run/binfmt/${name}";
+
+ getEmulator = system: (lib.systems.elaborate { inherit system; }).emulator pkgs;
+
+ # Mapping of systems to “magicOrExtension” and “mask”. Mostly taken from:
+ # - https://github.com/cleverca22/nixos-configs/blob/master/qemu.nix
+ # and
+ # - https://github.com/qemu/qemu/blob/master/scripts/qemu-binfmt-conf.sh
+ # TODO: maybe put these in a JSON file?
+ magics = {
+ armv6l-linux = {
+ magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff'';
+ };
+ armv7l-linux = {
+ magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff'';
+ };
+ aarch64-linux = {
+ magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff'';
+ };
+ aarch64_be-linux = {
+ magicOrExtension = ''\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'';
+ };
+ i386-linux = {
+ magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+ i486-linux = {
+ magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+ i586-linux = {
+ magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+ i686-linux = {
+ magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+ x86_64-linux = {
+ magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+ alpha-linux = {
+ magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90'';
+ mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+ sparc64-linux = {
+ magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'';
+ };
+ sparc-linux = {
+ magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x12'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'';
+ };
+ powerpc-linux = {
+ magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'';
+ };
+ powerpc64-linux = {
+ magicOrExtension = ''\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'';
+ };
+ powerpc64le-linux = {
+ magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\x00'';
+ };
+ mips-linux = {
+ magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'';
+ };
+ mipsel-linux = {
+ magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+ mips64-linux = {
+ magicOrExtension = ''\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'';
+ };
+ mips64el-linux = {
+ magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+ riscv32-linux = {
+ magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+ riscv64-linux = {
+ magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'';
+ mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
+ };
+ wasm32-wasi = {
+ magicOrExtension = ''\x00asm'';
+ mask = ''\xff\xff\xff\xff'';
+ };
+ wasm64-wasi = {
+ magicOrExtension = ''\x00asm'';
+ mask = ''\xff\xff\xff\xff'';
+ };
+ x86_64-windows = {
+ magicOrExtension = ".exe";
+ recognitionType = "extension";
+ };
+ i686-windows = {
+ magicOrExtension = ".exe";
+ recognitionType = "extension";
+ };
+ };
+
+in {
+ imports = [
+ (lib.mkRenamedOptionModule [ "boot" "binfmtMiscRegistrations" ] [ "boot" "binfmt" "registrations" ])
+ ];
+
+ options = {
+ boot.binfmt = {
+ registrations = mkOption {
+ default = {};
+
+ description = ''
+ Extra binary formats to register with the kernel.
+ See https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html for more details.
+ '';
+
+ type = types.attrsOf (types.submodule ({ config, ... }: {
+ options = {
+ recognitionType = mkOption {
+ default = "magic";
+ description = "Whether to recognize executables by magic number or extension.";
+ type = types.enum [ "magic" "extension" ];
+ };
+
+ offset = mkOption {
+ default = null;
+ description = "The byte offset of the magic number used for recognition.";
+ type = types.nullOr types.int;
+ };
+
+ magicOrExtension = mkOption {
+ description = "The magic number or extension to match on.";
+ type = types.str;
+ };
+
+ mask = mkOption {
+ default = null;
+ description =
+ "A mask to be ANDed with the byte sequence of the file before matching";
+ type = types.nullOr types.str;
+ };
+
+ interpreter = mkOption {
+ description = ''
+ The interpreter to invoke to run the program.
+
+ Note that the actual registration will point to
+ /run/binfmt/''${name}, so the kernel interpreter length
+ limit doesn't apply.
+ '';
+ type = types.path;
+ };
+
+ preserveArgvZero = mkOption {
+ default = false;
+ description = ''
+ Whether to pass the original argv[0] to the interpreter.
+
+ See the description of the 'P' flag in the kernel docs
+ for more details;
+ '';
+ type = types.bool;
+ };
+
+ openBinary = mkOption {
+ default = config.matchCredentials;
+ description = ''
+ Whether to pass the binary to the interpreter as an open
+ file descriptor, instead of a path.
+ '';
+ type = types.bool;
+ };
+
+ matchCredentials = mkOption {
+ default = false;
+ description = ''
+ Whether to launch with the credentials and security
+ token of the binary, not the interpreter (e.g. setuid
+ bit).
+
+ See the description of the 'C' flag in the kernel docs
+ for more details.
+
+ Implies/requires openBinary = true.
+ '';
+ type = types.bool;
+ };
+
+ fixBinary = mkOption {
+ default = false;
+ description = ''
+ Whether to open the interpreter file as soon as the
+ registration is loaded, rather than waiting for a
+ relevant file to be invoked.
+
+ See the description of the 'F' flag in the kernel docs
+ for more details.
+ '';
+ type = types.bool;
+ };
+ };
+ }));
+ };
+
+ emulatedSystems = mkOption {
+ default = [];
+ example = [ "wasm32-wasi" "x86_64-windows" "aarch64-linux" ];
+ description = ''
+ List of systems to emulate. Will also configure Nix to
+ support your new systems.
+ '';
+ type = types.listOf types.str;
+ };
+ };
+ };
+
+ config = {
+ boot.binfmt.registrations = builtins.listToAttrs (map (system: {
+ name = system;
+ value = {
+ interpreter = getEmulator system;
+ } // (magics.${system} or (throw "Cannot create binfmt registration for system ${system}"));
+ }) cfg.emulatedSystems);
+ # TODO: add a nix.extraPlatforms option to NixOS!
+ nix.extraOptions = lib.mkIf (cfg.emulatedSystems != []) ''
+ extra-platforms = ${toString (cfg.emulatedSystems ++ lib.optional pkgs.stdenv.hostPlatform.isx86_64 "i686-linux")}
+ '';
+ nix.sandboxPaths = lib.mkIf (cfg.emulatedSystems != [])
+ ([ "/run/binfmt" ] ++ (map (system: dirOf (dirOf (getEmulator system))) cfg.emulatedSystems));
+
+ environment.etc."binfmt.d/nixos.conf".source = builtins.toFile "binfmt_nixos.conf"
+ (lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine config.boot.binfmt.registrations));
+ system.activationScripts.binfmt = ''
+ mkdir -p -m 0755 /run/binfmt
+ ${lib.concatStringsSep "\n" (lib.mapAttrsToList activationSnippet config.boot.binfmt.registrations)}
+ '';
+ systemd.additionalUpstreamSystemUnits = lib.mkIf (config.boot.binfmt.registrations != {}) [
+ "proc-sys-fs-binfmt_misc.automount"
+ "proc-sys-fs-binfmt_misc.mount"
+ "systemd-binfmt.service"
+ ];
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/emergency-mode.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/emergency-mode.nix
new file mode 100644
index 000000000000..ec697bcee268
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/emergency-mode.nix
@@ -0,0 +1,37 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+
+ ###### interface
+
+ options = {
+
+ systemd.enableEmergencyMode = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to enable emergency mode, which is an
+ <command>sulogin</command> shell started on the console if
+ mounting a filesystem fails. Since some machines (like EC2
+ instances) have no console of any kind, emergency mode doesn't
+ make sense, and it's better to continue with the boot insofar
+ as possible.
+ '';
+ };
+
+ };
+
+ ###### implementation
+
+ config = {
+
+ systemd.additionalUpstreamSystemUnits = optionals
+ config.systemd.enableEmergencyMode [
+ "emergency.target" "emergency.service"
+ ];
+
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/grow-partition.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/grow-partition.nix
new file mode 100644
index 000000000000..be70c4ad9c8d
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/grow-partition.nix
@@ -0,0 +1,53 @@
+# This module automatically grows the root partition.
+# This allows an instance to be created with a bigger root filesystem
+# than provided by the machine image.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ imports = [
+ (mkRenamedOptionModule [ "virtualisation" "growPartition" ] [ "boot" "growPartition" ])
+ ];
+
+ options = {
+ boot.growPartition = mkEnableOption "grow the root partition on boot";
+ };
+
+ config = mkIf config.boot.growPartition {
+
+ boot.initrd.extraUtilsCommands = ''
+ copy_bin_and_libs ${pkgs.gawk}/bin/gawk
+ copy_bin_and_libs ${pkgs.gnused}/bin/sed
+ copy_bin_and_libs ${pkgs.utillinux}/sbin/sfdisk
+ copy_bin_and_libs ${pkgs.utillinux}/sbin/lsblk
+
+ substitute "${pkgs.cloud-utils.guest}/bin/.growpart-wrapped" "$out/bin/growpart" \
+ --replace "${pkgs.bash}/bin/sh" "/bin/sh" \
+ --replace "awk" "gawk" \
+ --replace "sed" "gnused"
+
+ ln -s sed $out/bin/gnused
+ '';
+
+ boot.initrd.postDeviceCommands = ''
+ rootDevice="${config.fileSystems."/".device}"
+ if waitDevice "$rootDevice"; then
+ rootDevice="$(readlink -f "$rootDevice")"
+ parentDevice="$rootDevice"
+ while [ "''${parentDevice%[0-9]}" != "''${parentDevice}" ]; do
+ parentDevice="''${parentDevice%[0-9]}";
+ done
+ partNum="''${rootDevice#''${parentDevice}}"
+ if [ "''${parentDevice%[0-9]p}" != "''${parentDevice}" ] && [ -b "''${parentDevice%p}" ]; then
+ parentDevice="''${parentDevice%p}"
+ fi
+ TMPDIR=/run sh $(type -P growpart) "$parentDevice" "$partNum"
+ udevadm settle
+ fi
+ '';
+
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-network.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-network.nix
new file mode 100644
index 000000000000..ec794d6eb014
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-network.nix
@@ -0,0 +1,148 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.boot.initrd.network;
+
+ dhcpInterfaces = lib.attrNames (lib.filterAttrs (iface: v: v.useDHCP == true) (config.networking.interfaces or {}));
+ doDhcp = config.networking.useDHCP || dhcpInterfaces != [];
+ dhcpIfShellExpr = if config.networking.useDHCP
+ then "$(ls /sys/class/net/ | grep -v ^lo$)"
+ else lib.concatMapStringsSep " " lib.escapeShellArg dhcpInterfaces;
+
+ udhcpcScript = pkgs.writeScript "udhcp-script"
+ ''
+ #! /bin/sh
+ if [ "$1" = bound ]; then
+ ip address add "$ip/$mask" dev "$interface"
+ if [ -n "$mtu" ]; then
+ ip link set mtu "$mtu" dev "$interface"
+ fi
+ if [ -n "$staticroutes" ]; then
+ echo "$staticroutes" \
+ | sed -r "s@(\S+) (\S+)@ ip route add \"\1\" via \"\2\" dev \"$interface\" ; @g" \
+ | sed -r "s@ via \"0\.0\.0\.0\"@@g" \
+ | /bin/sh
+ fi
+ if [ -n "$router" ]; then
+ ip route add "$router" dev "$interface" # just in case if "$router" is not within "$ip/$mask" (e.g. Hetzner Cloud)
+ ip route add default via "$router" dev "$interface"
+ fi
+ if [ -n "$dns" ]; then
+ rm -f /etc/resolv.conf
+ for i in $dns; do
+ echo "nameserver $dns" >> /etc/resolv.conf
+ done
+ fi
+ fi
+ '';
+
+ udhcpcArgs = toString cfg.udhcpc.extraArgs;
+
+in
+
+{
+
+ options = {
+
+ boot.initrd.network.enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Add network connectivity support to initrd. The network may be
+ configured using the <literal>ip</literal> kernel parameter,
+ as described in <link
+ xlink:href="https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt">the
+ kernel documentation</link>. Otherwise, if
+ <option>networking.useDHCP</option> is enabled, an IP address
+ is acquired using DHCP.
+
+ You should add the module(s) required for your network card to
+ boot.initrd.availableKernelModules.
+ <literal>lspci -v | grep -iA8 'network\|ethernet'</literal>
+ will tell you which.
+ '';
+ };
+
+ boot.initrd.network.flushBeforeStage2 = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether to clear the configuration of the interfaces that were set up in
+ the initrd right before stage 2 takes over. Stage 2 will do the regular network
+ configuration based on the NixOS networking options.
+ '';
+ };
+
+ boot.initrd.network.udhcpc.extraArgs = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ Additional command-line arguments passed verbatim to udhcpc if
+ <option>boot.initrd.network.enable</option> and <option>networking.useDHCP</option>
+ are enabled.
+ '';
+ };
+
+ boot.initrd.network.postCommands = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Shell commands to be executed after stage 1 of the
+ boot has initialised the network.
+ '';
+ };
+
+
+ };
+
+ config = mkIf cfg.enable {
+
+ boot.initrd.kernelModules = [ "af_packet" ];
+
+ boot.initrd.extraUtilsCommands = ''
+ copy_bin_and_libs ${pkgs.klibc}/lib/klibc/bin.static/ipconfig
+ '';
+
+ boot.initrd.preLVMCommands = mkBefore (
+ # Search for interface definitions in command line.
+ ''
+ ifaces=""
+ for o in $(cat /proc/cmdline); do
+ case $o in
+ ip=*)
+ ipconfig $o && ifaces="$ifaces $(echo $o | cut -d: -f6)"
+ ;;
+ esac
+ done
+ ''
+
+ # Otherwise, use DHCP.
+ + optionalString doDhcp ''
+ # Bring up all interfaces.
+ for iface in ${dhcpIfShellExpr}; do
+ echo "bringing up network interface $iface..."
+ ip link set "$iface" up && ifaces="$ifaces $iface"
+ done
+
+ # Acquire DHCP leases.
+ for iface in ${dhcpIfShellExpr}; do
+ echo "acquiring IP address via DHCP on $iface..."
+ udhcpc --quit --now -i $iface -O staticroutes --script ${udhcpcScript} ${udhcpcArgs}
+ done
+ ''
+
+ + cfg.postCommands);
+
+ boot.initrd.postMountCommands = mkIf cfg.flushBeforeStage2 ''
+ for iface in $ifaces; do
+ ip address flush "$iface"
+ ip link set "$iface" down
+ done
+ '';
+
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-openvpn.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-openvpn.nix
new file mode 100644
index 000000000000..e59bc7b6678f
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-openvpn.nix
@@ -0,0 +1,81 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.boot.initrd.network.openvpn;
+
+in
+
+{
+
+ options = {
+
+ boot.initrd.network.openvpn.enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Starts an OpenVPN client during initrd boot. It can be used to e.g.
+ remotely accessing the SSH service controlled by
+ <option>boot.initrd.network.ssh</option> or other network services
+ included. Service is killed when stage-1 boot is finished.
+ '';
+ };
+
+ boot.initrd.network.openvpn.configuration = mkOption {
+ type = types.path; # Same type as boot.initrd.secrets
+ description = ''
+ The configuration file for OpenVPN.
+
+ <warning>
+ <para>
+ Unless your bootloader supports initrd secrets, this configuration
+ is stored insecurely in the global Nix store.
+ </para>
+ </warning>
+ '';
+ example = "./configuration.ovpn";
+ };
+
+ };
+
+ config = mkIf (config.boot.initrd.network.enable && cfg.enable) {
+ assertions = [
+ {
+ assertion = cfg.configuration != null;
+ message = "You should specify a configuration for initrd OpenVPN";
+ }
+ ];
+
+ # Add kernel modules needed for OpenVPN
+ boot.initrd.kernelModules = [ "tun" "tap" ];
+
+ # Add openvpn and ip binaries to the initrd
+ # The shared libraries are required for DNS resolution
+ boot.initrd.extraUtilsCommands = ''
+ copy_bin_and_libs ${pkgs.openvpn}/bin/openvpn
+ copy_bin_and_libs ${pkgs.iproute}/bin/ip
+
+ cp -pv ${pkgs.glibc}/lib/libresolv.so.2 $out/lib
+ cp -pv ${pkgs.glibc}/lib/libnss_dns.so.2 $out/lib
+ '';
+
+ boot.initrd.secrets = {
+ "/etc/initrd.ovpn" = cfg.configuration;
+ };
+
+ # openvpn --version would exit with 1 instead of 0
+ boot.initrd.extraUtilsCommandsTest = ''
+ $out/bin/openvpn --show-gateway
+ '';
+
+ # Add `iproute /bin/ip` to the config, to ensure that openvpn
+ # is able to set the routes
+ boot.initrd.network.postCommands = ''
+ (cat /etc/initrd.ovpn; echo -e '\niproute /bin/ip') | \
+ openvpn /dev/stdin &
+ '';
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix
new file mode 100644
index 000000000000..f7ef26103709
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix
@@ -0,0 +1,210 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.boot.initrd.network.ssh;
+
+in
+
+{
+
+ options.boot.initrd.network.ssh = {
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Start SSH service during initrd boot. It can be used to debug failing
+ boot on a remote server, enter pasphrase for an encrypted partition etc.
+ Service is killed when stage-1 boot is finished.
+
+ The sshd configuration is largely inherited from
+ <option>services.openssh</option>.
+ '';
+ };
+
+ port = mkOption {
+ type = types.int;
+ default = 22;
+ description = ''
+ Port on which SSH initrd service should listen.
+ '';
+ };
+
+ shell = mkOption {
+ type = types.str;
+ default = "/bin/ash";
+ description = ''
+ Login shell of the remote user. Can be used to limit actions user can do.
+ '';
+ };
+
+ hostKeys = mkOption {
+ type = types.listOf (types.either types.str types.path);
+ default = [];
+ example = [
+ "/etc/secrets/initrd/ssh_host_rsa_key"
+ "/etc/secrets/initrd/ssh_host_ed25519_key"
+ ];
+ description = ''
+ Specify SSH host keys to import into the initrd.
+
+ To generate keys, use
+ <citerefentry><refentrytitle>ssh-keygen</refentrytitle><manvolnum>1</manvolnum></citerefentry>:
+
+ <screen>
+ <prompt># </prompt>ssh-keygen -t rsa -N "" -f /etc/secrets/initrd/ssh_host_rsa_key
+ <prompt># </prompt>ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key
+ </screen>
+
+ <warning>
+ <para>
+ Unless your bootloader supports initrd secrets, these keys
+ are stored insecurely in the global Nix store. Do NOT use
+ your regular SSH host private keys for this purpose or
+ you'll expose them to regular users!
+ </para>
+ <para>
+ Additionally, even if your initrd supports secrets, if
+ you're using initrd SSH to unlock an encrypted disk then
+ using your regular host keys exposes the private keys on
+ your unencrypted boot partition.
+ </para>
+ </warning>
+ '';
+ };
+
+ authorizedKeys = mkOption {
+ type = types.listOf types.str;
+ default = config.users.users.root.openssh.authorizedKeys.keys;
+ defaultText = "config.users.users.root.openssh.authorizedKeys.keys";
+ description = ''
+ Authorized keys for the root user on initrd.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Verbatim contents of <filename>sshd_config</filename>.";
+ };
+ };
+
+ imports =
+ map (opt: mkRemovedOptionModule ([ "boot" "initrd" "network" "ssh" ] ++ [ opt ]) ''
+ The initrd SSH functionality now uses OpenSSH rather than Dropbear.
+
+ If you want to keep your existing initrd SSH host keys, convert them with
+ $ dropbearconvert dropbear openssh dropbear_host_$type_key ssh_host_$type_key
+ and then set options.boot.initrd.network.ssh.hostKeys.
+ '') [ "hostRSAKey" "hostDSSKey" "hostECDSAKey" ];
+
+ config = let
+ # Nix complains if you include a store hash in initrd path names, so
+ # as an awful hack we drop the first character of the hash.
+ initrdKeyPath = path: if isString path
+ then path
+ else let name = builtins.baseNameOf path; in
+ builtins.unsafeDiscardStringContext ("/etc/ssh/" +
+ substring 1 (stringLength name) name);
+
+ sshdCfg = config.services.openssh;
+
+ sshdConfig = ''
+ Port ${toString cfg.port}
+
+ PasswordAuthentication no
+ ChallengeResponseAuthentication no
+
+ ${flip concatMapStrings cfg.hostKeys (path: ''
+ HostKey ${initrdKeyPath path}
+ '')}
+
+ KexAlgorithms ${concatStringsSep "," sshdCfg.kexAlgorithms}
+ Ciphers ${concatStringsSep "," sshdCfg.ciphers}
+ MACs ${concatStringsSep "," sshdCfg.macs}
+
+ LogLevel ${sshdCfg.logLevel}
+
+ ${if sshdCfg.useDns then ''
+ UseDNS yes
+ '' else ''
+ UseDNS no
+ ''}
+
+ ${cfg.extraConfig}
+ '';
+ in mkIf (config.boot.initrd.network.enable && cfg.enable) {
+ assertions = [
+ {
+ assertion = cfg.authorizedKeys != [];
+ message = "You should specify at least one authorized key for initrd SSH";
+ }
+
+ {
+ assertion = cfg.hostKeys != [];
+ message = ''
+ You must now pre-generate the host keys for initrd SSH.
+ See the boot.initrd.network.ssh.hostKeys documentation
+ for instructions.
+ '';
+ }
+ ];
+
+ boot.initrd.extraUtilsCommands = ''
+ copy_bin_and_libs ${pkgs.openssh}/bin/sshd
+ cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib
+ '';
+
+ boot.initrd.extraUtilsCommandsTest = ''
+ # sshd requires a host key to check config, so we pass in the test's
+ echo -n ${escapeShellArg sshdConfig} |
+ $out/bin/sshd -t -f /dev/stdin \
+ -h ${../../../tests/initrd-network-ssh/ssh_host_ed25519_key}
+ '';
+
+ boot.initrd.network.postCommands = ''
+ echo '${cfg.shell}' > /etc/shells
+ echo 'root:x:0:0:root:/root:${cfg.shell}' > /etc/passwd
+ echo 'sshd:x:1:1:sshd:/var/empty:/bin/nologin' >> /etc/passwd
+ echo 'passwd: files' > /etc/nsswitch.conf
+
+ mkdir -p /var/log /var/empty
+ touch /var/log/lastlog
+
+ mkdir -p /etc/ssh
+ echo -n ${escapeShellArg sshdConfig} > /etc/ssh/sshd_config
+
+ echo "export PATH=$PATH" >> /etc/profile
+ echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> /etc/profile
+
+ mkdir -p /root/.ssh
+ ${concatStrings (map (key: ''
+ echo ${escapeShellArg key} >> /root/.ssh/authorized_keys
+ '') cfg.authorizedKeys)}
+
+ ${flip concatMapStrings cfg.hostKeys (path: ''
+ # keys from Nix store are world-readable, which sshd doesn't like
+ chmod 0600 "${initrdKeyPath path}"
+ '')}
+
+ /bin/sshd -e
+ '';
+
+ boot.initrd.postMountCommands = ''
+ # Stop sshd cleanly before stage 2.
+ #
+ # If you want to keep it around to debug post-mount SSH issues,
+ # run `touch /.keep_sshd` (either from an SSH session or in
+ # another initrd hook like preDeviceCommands).
+ if ! [ -e /.keep_sshd ]; then
+ pkill -x sshd
+ fi
+ '';
+
+ boot.initrd.secrets = listToAttrs
+ (map (path: nameValuePair (initrdKeyPath path) path) cfg.hostKeys);
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/kernel.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/kernel.nix
new file mode 100644
index 000000000000..43871f439f7f
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/kernel.nix
@@ -0,0 +1,335 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ inherit (config.boot) kernelPatches;
+ inherit (config.boot.kernel) features randstructSeed;
+ inherit (config.boot.kernelPackages) kernel;
+
+ kernelModulesConf = pkgs.writeText "nixos.conf"
+ ''
+ ${concatStringsSep "\n" config.boot.kernelModules}
+ '';
+
+in
+
+{
+
+ ###### interface
+
+ options = {
+
+ boot.kernel.features = mkOption {
+ default = {};
+ example = literalExample "{ debug = true; }";
+ internal = true;
+ description = ''
+ This option allows to enable or disable certain kernel features.
+ It's not API, because it's about kernel feature sets, that
+ make sense for specific use cases. Mostly along with programs,
+ which would have separate nixos options.
+ `grep features pkgs/os-specific/linux/kernel/common-config.nix`
+ '';
+ };
+
+ boot.kernelPackages = mkOption {
+ default = pkgs.linuxPackages;
+ type = types.unspecified // { merge = mergeEqualOption; };
+ apply = kernelPackages: kernelPackages.extend (self: super: {
+ kernel = super.kernel.override {
+ inherit randstructSeed;
+ kernelPatches = super.kernel.kernelPatches ++ kernelPatches;
+ features = lib.recursiveUpdate super.kernel.features features;
+ };
+ });
+ # We don't want to evaluate all of linuxPackages for the manual
+ # - some of it might not even evaluate correctly.
+ defaultText = "pkgs.linuxPackages";
+ example = literalExample "pkgs.linuxPackages_2_6_25";
+ description = ''
+ This option allows you to override the Linux kernel used by
+ NixOS. Since things like external kernel module packages are
+ tied to the kernel you're using, it also overrides those.
+ This option is a function that takes Nixpkgs as an argument
+ (as a convenience), and returns an attribute set containing at
+ the very least an attribute <varname>kernel</varname>.
+ Additional attributes may be needed depending on your
+ configuration. For instance, if you use the NVIDIA X driver,
+ then it also needs to contain an attribute
+ <varname>nvidia_x11</varname>.
+ '';
+ };
+
+ boot.kernelPatches = mkOption {
+ type = types.listOf types.attrs;
+ default = [];
+ example = literalExample "[ pkgs.kernelPatches.ubuntu_fan_4_4 ]";
+ description = "A list of additional patches to apply to the kernel.";
+ };
+
+ boot.kernel.randstructSeed = mkOption {
+ type = types.str;
+ default = "";
+ example = "my secret seed";
+ description = ''
+ Provides a custom seed for the <varname>RANDSTRUCT</varname> security
+ option of the Linux kernel. Note that <varname>RANDSTRUCT</varname> is
+ only enabled in NixOS hardened kernels. Using a custom seed requires
+ building the kernel and dependent packages locally, since this
+ customization happens at build time.
+ '';
+ };
+
+ boot.kernelParams = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ description = "Parameters added to the kernel command line.";
+ };
+
+ boot.consoleLogLevel = mkOption {
+ type = types.int;
+ default = 4;
+ description = ''
+ The kernel console <literal>loglevel</literal>. All Kernel Messages with a log level smaller
+ than this setting will be printed to the console.
+ '';
+ };
+
+ boot.vesa = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ (Deprecated) This option, if set, activates the VESA 800x600 video
+ mode on boot and disables kernel modesetting. It is equivalent to
+ specifying <literal>[ "vga=0x317" "nomodeset" ]</literal> in the
+ <option>boot.kernelParams</option> option. This option is
+ deprecated as of 2020: Xorg now works better with modesetting, and
+ you might want a different VESA vga setting, anyway.
+ '';
+ };
+
+ boot.extraModulePackages = mkOption {
+ type = types.listOf types.package;
+ default = [];
+ example = literalExample "[ config.boot.kernelPackages.nvidia_x11 ]";
+ description = "A list of additional packages supplying kernel modules.";
+ };
+
+ boot.kernelModules = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ description = ''
+ The set of kernel modules to be loaded in the second stage of
+ the boot process. Note that modules that are needed to
+ mount the root file system should be added to
+ <option>boot.initrd.availableKernelModules</option> or
+ <option>boot.initrd.kernelModules</option>.
+ '';
+ };
+
+ boot.initrd.availableKernelModules = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ example = [ "sata_nv" "ext3" ];
+ description = ''
+ The set of kernel modules in the initial ramdisk used during the
+ boot process. This set must include all modules necessary for
+ mounting the root device. That is, it should include modules
+ for the physical device (e.g., SCSI drivers) and for the file
+ system (e.g., ext3). The set specified here is automatically
+ closed under the module dependency relation, i.e., all
+ dependencies of the modules list here are included
+ automatically. The modules listed here are available in the
+ initrd, but are only loaded on demand (e.g., the ext3 module is
+ loaded automatically when an ext3 filesystem is mounted, and
+ modules for PCI devices are loaded when they match the PCI ID
+ of a device in your system). To force a module to be loaded,
+ include it in <option>boot.initrd.kernelModules</option>.
+ '';
+ };
+
+ boot.initrd.kernelModules = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ description = "List of modules that are always loaded by the initrd.";
+ };
+
+ system.modulesTree = mkOption {
+ type = types.listOf types.path;
+ internal = true;
+ default = [];
+ description = ''
+ Tree of kernel modules. This includes the kernel, plus modules
+ built outside of the kernel. Combine these into a single tree of
+ symlinks because modprobe only supports one directory.
+ '';
+ # Convert the list of path to only one path.
+ apply = pkgs.aggregateModules;
+ };
+
+ system.requiredKernelConfig = mkOption {
+ default = [];
+ example = literalExample ''
+ with config.lib.kernelConfig; [
+ (isYes "MODULES")
+ (isEnabled "FB_CON_DECOR")
+ (isEnabled "BLK_DEV_INITRD")
+ ]
+ '';
+ internal = true;
+ type = types.listOf types.attrs;
+ description = ''
+ This option allows modules to specify the kernel config options that
+ must be set (or unset) for the module to work. Please use the
+ lib.kernelConfig functions to build list elements.
+ '';
+ };
+
+ };
+
+
+ ###### implementation
+
+ config = mkMerge
+ [ (mkIf config.boot.initrd.enable {
+ boot.initrd.availableKernelModules =
+ [ # Note: most of these (especially the SATA/PATA modules)
+ # shouldn't be included by default since nixos-generate-config
+ # detects them, but I'm keeping them for now for backwards
+ # compatibility.
+
+ # Some SATA/PATA stuff.
+ "ahci"
+ "sata_nv"
+ "sata_via"
+ "sata_sis"
+ "sata_uli"
+ "ata_piix"
+ "pata_marvell"
+
+ # Standard SCSI stuff.
+ "sd_mod"
+ "sr_mod"
+
+ # SD cards and internal eMMC drives.
+ "mmc_block"
+
+ # Support USB keyboards, in case the boot fails and we only have
+ # a USB keyboard, or for LUKS passphrase prompt.
+ "uhci_hcd"
+ "ehci_hcd"
+ "ehci_pci"
+ "ohci_hcd"
+ "ohci_pci"
+ "xhci_hcd"
+ "xhci_pci"
+ "usbhid"
+ "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat"
+ "hid_logitech_hidpp" "hid_logitech_dj"
+
+ ] ++ optionals (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [
+ # Misc. x86 keyboard stuff.
+ "pcips2" "atkbd" "i8042"
+
+ # x86 RTC needed by the stage 2 init script.
+ "rtc_cmos"
+ ];
+
+ boot.initrd.kernelModules =
+ [ # For LVM.
+ "dm_mod"
+ ];
+ })
+
+ (mkIf (!config.boot.isContainer) {
+ system.build = { inherit kernel; };
+
+ system.modulesTree = [ kernel ] ++ config.boot.extraModulePackages;
+
+ # Implement consoleLogLevel both in early boot and using sysctl
+ # (so you don't need to reboot to have changes take effect).
+ boot.kernelParams =
+ [ "loglevel=${toString config.boot.consoleLogLevel}" ] ++
+ optionals config.boot.vesa [ "vga=0x317" "nomodeset" ];
+
+ boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel;
+
+ boot.kernelModules = [ "loop" "atkbd" ];
+
+ # The Linux kernel >= 2.6.27 provides firmware.
+ hardware.firmware = [ kernel ];
+
+ # Create /etc/modules-load.d/nixos.conf, which is read by
+ # systemd-modules-load.service to load required kernel modules.
+ environment.etc =
+ { "modules-load.d/nixos.conf".source = kernelModulesConf;
+ };
+
+ systemd.services.systemd-modules-load =
+ { wantedBy = [ "multi-user.target" ];
+ restartTriggers = [ kernelModulesConf ];
+ serviceConfig =
+ { # Ignore failed module loads. Typically some of the
+ # modules in ‘boot.kernelModules’ are "nice to have but
+ # not required" (e.g. acpi-cpufreq), so we don't want to
+ # barf on those.
+ SuccessExitStatus = "0 1";
+ };
+ };
+
+ lib.kernelConfig = {
+ isYes = option: {
+ assertion = config: config.isYes option;
+ message = "CONFIG_${option} is not yes!";
+ configLine = "CONFIG_${option}=y";
+ };
+
+ isNo = option: {
+ assertion = config: config.isNo option;
+ message = "CONFIG_${option} is not no!";
+ configLine = "CONFIG_${option}=n";
+ };
+
+ isModule = option: {
+ assertion = config: config.isModule option;
+ message = "CONFIG_${option} is not built as a module!";
+ configLine = "CONFIG_${option}=m";
+ };
+
+ ### Usually you will just want to use these two
+ # True if yes or module
+ isEnabled = option: {
+ assertion = config: config.isEnabled option;
+ message = "CONFIG_${option} is not enabled!";
+ configLine = "CONFIG_${option}=y";
+ };
+
+ # True if no or omitted
+ isDisabled = option: {
+ assertion = config: config.isDisabled option;
+ message = "CONFIG_${option} is not disabled!";
+ configLine = "CONFIG_${option}=n";
+ };
+ };
+
+ # The config options that all modules can depend upon
+ system.requiredKernelConfig = with config.lib.kernelConfig;
+ [
+ # !!! Should this really be needed?
+ (isYes "MODULES")
+ (isYes "BINFMT_ELF")
+ ] ++ (optional (randstructSeed != "") (isYes "GCC_PLUGIN_RANDSTRUCT"));
+
+ # nixpkgs kernels are assumed to have all required features
+ assertions = if config.boot.kernelPackages.kernel ? features then [] else
+ let cfg = config.boot.kernelPackages.kernel.config; in map (attrs:
+ { assertion = attrs.assertion cfg; inherit (attrs) message; }
+ ) config.system.requiredKernelConfig;
+
+ })
+
+ ];
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/kernel_config.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/kernel_config.nix
new file mode 100644
index 000000000000..783685c9dfe4
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/kernel_config.nix
@@ -0,0 +1,137 @@
+{ lib, config, ... }:
+
+with lib;
+let
+ findWinner = candidates: winner:
+ any (x: x == winner) candidates;
+
+ # winners is an ordered list where first item wins over 2nd etc
+ mergeAnswer = winners: locs: defs:
+ let
+ values = map (x: x.value) defs;
+ inter = intersectLists values winners;
+ winner = head winners;
+ in
+ if defs == [] then abort "This case should never happen."
+ else if winner == [] then abort "Give a valid list of winner"
+ else if inter == [] then mergeOneOption locs defs
+ else if findWinner values winner then
+ winner
+ else
+ mergeAnswer (tail winners) locs defs;
+
+ mergeFalseByDefault = locs: defs:
+ if defs == [] then abort "This case should never happen."
+ else if any (x: x == false) (getValues defs) then false
+ else true;
+
+ kernelItem = types.submodule {
+ options = {
+ tristate = mkOption {
+ type = types.enum [ "y" "m" "n" null ] // {
+ merge = mergeAnswer [ "y" "m" "n" ];
+ };
+ default = null;
+ internal = true;
+ visible = true;
+ description = ''
+ Use this field for tristate kernel options expecting a "y" or "m" or "n".
+ '';
+ };
+
+ freeform = mkOption {
+ type = types.nullOr types.str // {
+ merge = mergeEqualOption;
+ };
+ default = null;
+ example = ''MMC_BLOCK_MINORS.freeform = "32";'';
+ description = ''
+ Freeform description of a kernel configuration item value.
+ '';
+ };
+
+ optional = mkOption {
+ type = types.bool // { merge = mergeFalseByDefault; };
+ default = false;
+ description = ''
+ Whether option should generate a failure when unused.
+ Upon merging values, mandatory wins over optional.
+ '';
+ };
+ };
+ };
+
+ mkValue = with lib; val:
+ let
+ isNumber = c: elem c ["0" "1" "2" "3" "4" "5" "6" "7" "8" "9"];
+
+ in
+ if (val == "") then "\"\""
+ else if val == "y" || val == "m" || val == "n" then val
+ else if all isNumber (stringToCharacters val) then val
+ else if substring 0 2 val == "0x" then val
+ else val; # FIXME: fix quoting one day
+
+
+ # generate nix intermediate kernel config file of the form
+ #
+ # VIRTIO_MMIO m
+ # VIRTIO_BLK y
+ # VIRTIO_CONSOLE n
+ # NET_9P_VIRTIO? y
+ #
+ # Borrowed from copumpkin https://github.com/NixOS/nixpkgs/pull/12158
+ # returns a string, expr should be an attribute set
+ # Use mkValuePreprocess to preprocess option values, aka mark 'modules' as 'yes' or vice-versa
+ # use the identity if you don't want to override the configured values
+ generateNixKConf = exprs:
+ let
+ mkConfigLine = key: item:
+ let
+ val = if item.freeform != null then item.freeform else item.tristate;
+ in
+ if val == null
+ then ""
+ else if (item.optional)
+ then "${key}? ${mkValue val}\n"
+ else "${key} ${mkValue val}\n";
+
+ mkConf = cfg: concatStrings (mapAttrsToList mkConfigLine cfg);
+ in mkConf exprs;
+
+in
+{
+
+ options = {
+
+ intermediateNixConfig = mkOption {
+ readOnly = true;
+ type = types.lines;
+ example = ''
+ USB? y
+ DEBUG n
+ '';
+ description = ''
+ The result of converting the structured kernel configuration in settings
+ to an intermediate string that can be parsed by generate-config.pl to
+ answer the kernel `make defconfig`.
+ '';
+ };
+
+ settings = mkOption {
+ type = types.attrsOf kernelItem;
+ example = literalExample '' with lib.kernel; {
+ "9P_NET" = yes;
+ USB = option yes;
+ MMC_BLOCK_MINORS = freeform "32";
+ }'';
+ description = ''
+ Structured kernel configuration.
+ '';
+ };
+ };
+
+ config = {
+ intermediateNixConfig = generateNixKConf config.settings;
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/kexec.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/kexec.nix
new file mode 100644
index 000000000000..27a8e0217c55
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/kexec.nix
@@ -0,0 +1,32 @@
+{ pkgs, lib, ... }:
+
+{
+ config = lib.mkIf (lib.any (lib.meta.platformMatch pkgs.stdenv.hostPlatform) pkgs.kexectools.meta.platforms) {
+ environment.systemPackages = [ pkgs.kexectools ];
+
+ systemd.services.prepare-kexec =
+ { description = "Preparation for kexec";
+ wantedBy = [ "kexec.target" ];
+ before = [ "systemd-kexec.service" ];
+ unitConfig.DefaultDependencies = false;
+ serviceConfig.Type = "oneshot";
+ path = [ pkgs.kexectools ];
+ script =
+ ''
+ # Don't load the current system profile if we already have a kernel loaded
+ if [[ 1 = "$(</sys/kernel/kexec_loaded)" ]] ; then
+ echo "kexec kernel has already been loaded, prepare-kexec skipped"
+ exit 0
+ fi
+
+ p=$(readlink -f /nix/var/nix/profiles/system)
+ if ! [[ -d $p ]]; then
+ echo "Could not find system profile for prepare-kexec"
+ exit 1
+ fi
+ echo "Loading NixOS system via kexec."
+ exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init"
+ '';
+ };
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/efi.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/efi.nix
new file mode 100644
index 000000000000..6043c904c450
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/efi.nix
@@ -0,0 +1,20 @@
+{ lib, ... }:
+
+with lib;
+
+{
+ options.boot.loader.efi = {
+
+ canTouchEfiVariables = mkOption {
+ default = false;
+ type = types.bool;
+ description = "Whether the installation process is allowed to modify EFI boot variables.";
+ };
+
+ efiSysMountPoint = mkOption {
+ default = "/boot";
+ type = types.str;
+ description = "Where the EFI System Partition is mounted.";
+ };
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh
new file mode 100644
index 000000000000..8ae23dc988c2
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh
@@ -0,0 +1,106 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+default=$1
+if test -z "$1"; then
+ echo "Syntax: generations-dir-builder.sh <DEFAULT-CONFIG>"
+ exit 1
+fi
+
+echo "updating the boot generations directory..."
+
+mkdir -p /boot
+
+rm -Rf /boot/system* || true
+
+target=/boot/grub/menu.lst
+tmp=$target.tmp
+
+# Convert a path to a file in the Nix store such as
+# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
+cleanName() {
+ local path="$1"
+ echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g'
+}
+
+# Copy a file from the Nix store to /boot/kernels.
+declare -A filesCopied
+
+copyToKernelsDir() {
+ local src="$1"
+ local dst="/boot/kernels/$(cleanName $src)"
+ # Don't copy the file if $dst already exists. This means that we
+ # have to create $dst atomically to prevent partially copied
+ # kernels or initrd if this script is ever interrupted.
+ if ! test -e $dst; then
+ local dstTmp=$dst.tmp.$$
+ cp $src $dstTmp
+ mv $dstTmp $dst
+ fi
+ filesCopied[$dst]=1
+ result=$dst
+}
+
+
+# Copy its kernel and initrd to /boot/kernels.
+addEntry() {
+ local path="$1"
+ local generation="$2"
+ local outdir=/boot/system-$generation
+
+ if ! test -e $path/kernel -a -e $path/initrd; then
+ return
+ fi
+
+ local kernel=$(readlink -f $path/kernel)
+ local initrd=$(readlink -f $path/initrd)
+
+ if test -n "@copyKernels@"; then
+ copyToKernelsDir $kernel; kernel=$result
+ copyToKernelsDir $initrd; initrd=$result
+ fi
+
+ mkdir -p $outdir
+ ln -sf $(readlink -f $path) $outdir/system
+ ln -sf $(readlink -f $path/init) $outdir/init
+ ln -sf $initrd $outdir/initrd
+ ln -sf $kernel $outdir/kernel
+
+ if test $(readlink -f "$path") = "$default"; then
+ cp "$kernel" /boot/nixos-kernel
+ cp "$initrd" /boot/nixos-initrd
+ cp "$(readlink -f "$path/init")" /boot/nixos-init
+
+ mkdir -p /boot/default
+ # ln -sfT: overrides target even if it exists.
+ ln -sfT $(readlink -f $path) /boot/default/system
+ ln -sfT $(readlink -f $path/init) /boot/default/init
+ ln -sfT $initrd /boot/default/initrd
+ ln -sfT $kernel /boot/default/kernel
+ fi
+}
+
+if test -n "@copyKernels@"; then
+ mkdir -p /boot/kernels
+fi
+
+# Add all generations of the system profile to the menu, in reverse
+# (most recent to least recent) order.
+for generation in $(
+ (cd /nix/var/nix/profiles && ls -d system-*-link) \
+ | sed 's/system-\([0-9]\+\)-link/\1/' \
+ | sort -n -r); do
+ link=/nix/var/nix/profiles/system-$generation-link
+ addEntry $link $generation
+done
+
+# Remove obsolete files from /boot/kernels.
+for fn in /boot/kernels/*; do
+ if ! test "${filesCopied[$fn]}" = 1; then
+ rm -vf -- "$fn"
+ fi
+done
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
new file mode 100644
index 000000000000..2d27611946e2
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
@@ -0,0 +1,65 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ generationsDirBuilder = pkgs.substituteAll {
+ src = ./generations-dir-builder.sh;
+ isExecutable = true;
+ inherit (pkgs) bash;
+ path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+ inherit (config.boot.loader.generationsDir) copyKernels;
+ };
+
+ # Temporary check, for nixos to cope both with nixpkgs stdenv-updates and trunk
+ inherit (pkgs.stdenv.hostPlatform) platform;
+
+in
+
+{
+ options = {
+
+ boot.loader.generationsDir = {
+
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to create symlinks to the system generations under
+ <literal>/boot</literal>. When enabled,
+ <literal>/boot/default/kernel</literal>,
+ <literal>/boot/default/initrd</literal>, etc., are updated to
+ point to the current generation's kernel image, initial RAM
+ disk, and other bootstrap files.
+
+ This optional is not necessary with boot loaders such as GNU GRUB
+ for which the menu is updated to point to the latest bootstrap
+ files. However, it is needed for U-Boot on platforms where the
+ boot command line is stored in flash memory rather than in a
+ menu file.
+ '';
+ };
+
+ copyKernels = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether copy the necessary boot files into /boot, so
+ /nix/store is not needed by the boot loader.
+ '';
+ };
+
+ };
+
+ };
+
+
+ config = mkIf config.boot.loader.generationsDir.enable {
+
+ system.build.installBootLoader = generationsDirBuilder;
+ system.boot.loader.id = "generationsDir";
+ system.boot.loader.kernelFile = platform.kernelTarget;
+
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix
new file mode 100644
index 000000000000..bd508bbe8eaa
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix
@@ -0,0 +1,65 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ blCfg = config.boot.loader;
+ dtCfg = config.hardware.deviceTree;
+ cfg = blCfg.generic-extlinux-compatible;
+
+ timeoutStr = if blCfg.timeout == null then "-1" else toString blCfg.timeout;
+
+ # The builder used to write during system activation
+ builder = import ./extlinux-conf-builder.nix { inherit pkgs; };
+ # The builder exposed in populateCmd, which runs on the build architecture
+ populateBuilder = import ./extlinux-conf-builder.nix { pkgs = pkgs.buildPackages; };
+in
+{
+ options = {
+ boot.loader.generic-extlinux-compatible = {
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to generate an extlinux-compatible configuration file
+ under <literal>/boot/extlinux.conf</literal>. For instance,
+ U-Boot's generic distro boot support uses this file format.
+
+ See <link xlink:href="http://git.denx.de/?p=u-boot.git;a=blob;f=doc/README.distro;hb=refs/heads/master">U-boot's documentation</link>
+ for more information.
+ '';
+ };
+
+ configurationLimit = mkOption {
+ default = 20;
+ example = 10;
+ type = types.int;
+ description = ''
+ Maximum number of configurations in the boot menu.
+ '';
+ };
+
+ populateCmd = mkOption {
+ type = types.str;
+ readOnly = true;
+ description = ''
+ Contains the builder command used to populate an image,
+ honoring all options except the <literal>-c &lt;path-to-default-configuration&gt;</literal>
+ argument.
+ Useful to have for sdImage.populateRootCommands
+ '';
+ };
+
+ };
+ };
+
+ config = let
+ builderArgs = "-g ${toString cfg.configurationLimit} -t ${timeoutStr}" + lib.optionalString (dtCfg.name != null) " -n ${dtCfg.name}";
+ in
+ mkIf cfg.enable {
+ system.build.installBootLoader = "${builder} ${builderArgs} -c";
+ system.boot.loader.id = "generic-extlinux-compatible";
+
+ boot.loader.generic-extlinux-compatible.populateCmd = "${populateBuilder} ${builderArgs}";
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix
new file mode 100644
index 000000000000..576a07c1d272
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix
@@ -0,0 +1,8 @@
+{ pkgs }:
+
+pkgs.substituteAll {
+ src = ./extlinux-conf-builder.sh;
+ isExecutable = true;
+ path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+ inherit (pkgs) bash;
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
new file mode 100644
index 000000000000..854684b87fac
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
@@ -0,0 +1,151 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+usage() {
+ echo "usage: $0 -t <timeout> -c <path-to-default-configuration> [-d <boot-dir>] [-g <num-generations>] [-n <dtbName>]" >&2
+ exit 1
+}
+
+timeout= # Timeout in centiseconds
+default= # Default configuration
+target=/boot # Target directory
+numGenerations=0 # Number of other generations to include in the menu
+
+while getopts "t:c:d:g:n:" opt; do
+ case "$opt" in
+ t) # U-Boot interprets '0' as infinite and negative as instant boot
+ if [ "$OPTARG" -lt 0 ]; then
+ timeout=0
+ elif [ "$OPTARG" = 0 ]; then
+ timeout=-10
+ else
+ timeout=$((OPTARG * 10))
+ fi
+ ;;
+ c) default="$OPTARG" ;;
+ d) target="$OPTARG" ;;
+ g) numGenerations="$OPTARG" ;;
+ n) dtbName="$OPTARG" ;;
+ \?) usage ;;
+ esac
+done
+
+[ "$timeout" = "" -o "$default" = "" ] && usage
+
+mkdir -p $target/nixos
+mkdir -p $target/extlinux
+
+# Convert a path to a file in the Nix store such as
+# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
+cleanName() {
+ local path="$1"
+ echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g'
+}
+
+# Copy a file from the Nix store to $target/nixos.
+declare -A filesCopied
+
+copyToKernelsDir() {
+ local src=$(readlink -f "$1")
+ local dst="$target/nixos/$(cleanName $src)"
+ # Don't copy the file if $dst already exists. This means that we
+ # have to create $dst atomically to prevent partially copied
+ # kernels or initrd if this script is ever interrupted.
+ if ! test -e $dst; then
+ local dstTmp=$dst.tmp.$$
+ cp -r $src $dstTmp
+ mv $dstTmp $dst
+ fi
+ filesCopied[$dst]=1
+ result=$dst
+}
+
+# Copy its kernel, initrd and dtbs to $target/nixos, and echo out an
+# extlinux menu entry
+addEntry() {
+ local path=$(readlink -f "$1")
+ local tag="$2" # Generation number or 'default'
+
+ if ! test -e $path/kernel -a -e $path/initrd; then
+ return
+ fi
+
+ copyToKernelsDir "$path/kernel"; kernel=$result
+ copyToKernelsDir "$path/initrd"; initrd=$result
+ dtbDir=$(readlink -m "$path/dtbs")
+ if [ -e "$dtbDir" ]; then
+ copyToKernelsDir "$dtbDir"; dtbs=$result
+ fi
+
+ timestampEpoch=$(stat -L -c '%Z' $path)
+
+ timestamp=$(date "+%Y-%m-%d %H:%M" -d @$timestampEpoch)
+ nixosLabel="$(cat $path/nixos-version)"
+ extraParams="$(cat $path/kernel-params)"
+
+ echo
+ echo "LABEL nixos-$tag"
+ if [ "$tag" = "default" ]; then
+ echo " MENU LABEL NixOS - Default"
+ else
+ echo " MENU LABEL NixOS - Configuration $tag ($timestamp - $nixosLabel)"
+ fi
+ echo " LINUX ../nixos/$(basename $kernel)"
+ echo " INITRD ../nixos/$(basename $initrd)"
+ if [ -d "$dtbDir" ]; then
+ # if a dtbName was specified explicitly, use that, else use FDTDIR
+ if [ -n "$dtbName" ]; then
+ echo " FDT ../nixos/$(basename $dtbs)/${dtbName}"
+ else
+ echo " FDTDIR ../nixos/$(basename $dtbs)"
+ fi
+ else
+ if [ -n "$dtbName" ]; then
+ echo "Explicitly requested dtbName $dtbName, but there's no FDTDIR - bailing out." >&2
+ exit 1
+ fi
+ fi
+ echo " APPEND systemConfig=$path init=$path/init $extraParams"
+}
+
+tmpFile="$target/extlinux/extlinux.conf.tmp.$$"
+
+cat > $tmpFile <<EOF
+# Generated file, all changes will be lost on nixos-rebuild!
+
+# Change this to e.g. nixos-42 to temporarily boot to an older configuration.
+DEFAULT nixos-default
+
+MENU TITLE ------------------------------------------------------------
+TIMEOUT $timeout
+EOF
+
+addEntry $default default >> $tmpFile
+
+if [ "$numGenerations" -gt 0 ]; then
+ # Add up to $numGenerations generations of the system profile to the menu,
+ # in reverse (most recent to least recent) order.
+ for generation in $(
+ (cd /nix/var/nix/profiles && ls -d system-*-link) \
+ | sed 's/system-\([0-9]\+\)-link/\1/' \
+ | sort -n -r \
+ | head -n $numGenerations); do
+ link=/nix/var/nix/profiles/system-$generation-link
+ addEntry $link $generation
+ done >> $tmpFile
+fi
+
+mv -f $tmpFile $target/extlinux/extlinux.conf
+
+# Remove obsolete files from $target/nixos.
+for fn in $target/nixos/*; do
+ if ! test "${filesCopied[$fn]}" = 1; then
+ echo "Removing no longer needed boot file: $fn"
+ chmod +w -- "$fn"
+ rm -rf -- "$fn"
+ fi
+done
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
new file mode 100644
index 000000000000..20e39628eabb
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
@@ -0,0 +1,821 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.boot.loader.grub;
+
+ efi = config.boot.loader.efi;
+
+ grubPkgs =
+ # Package set of targeted architecture
+ if cfg.forcei686 then pkgs.pkgsi686Linux else pkgs;
+
+ realGrub = if cfg.version == 1 then grubPkgs.grub
+ else if cfg.zfsSupport then grubPkgs.grub2.override { zfsSupport = true; }
+ else if cfg.trustedBoot.enable
+ then if cfg.trustedBoot.isHPLaptop
+ then grubPkgs.trustedGrub-for-HP
+ else grubPkgs.trustedGrub
+ else grubPkgs.grub2;
+
+ grub =
+ # Don't include GRUB if we're only generating a GRUB menu (e.g.,
+ # in EC2 instances).
+ if cfg.devices == ["nodev"]
+ then null
+ else realGrub;
+
+ grubEfi =
+ # EFI version of Grub v2
+ if cfg.efiSupport && (cfg.version == 2)
+ then realGrub.override { efiSupport = cfg.efiSupport; }
+ else null;
+
+ f = x: if x == null then "" else "" + x;
+
+ grubConfig = args:
+ let
+ efiSysMountPoint = if args.efiSysMountPoint == null then args.path else args.efiSysMountPoint;
+ efiSysMountPoint' = replaceChars [ "/" ] [ "-" ] efiSysMountPoint;
+ in
+ pkgs.writeText "grub-config.xml" (builtins.toXML
+ { splashImage = f cfg.splashImage;
+ splashMode = f cfg.splashMode;
+ backgroundColor = f cfg.backgroundColor;
+ grub = f grub;
+ grubTarget = f (grub.grubTarget or "");
+ shell = "${pkgs.runtimeShell}";
+ fullName = lib.getName realGrub;
+ fullVersion = lib.getVersion realGrub;
+ grubEfi = f grubEfi;
+ grubTargetEfi = if cfg.efiSupport && (cfg.version == 2) then f (grubEfi.grubTarget or "") else "";
+ bootPath = args.path;
+ storePath = config.boot.loader.grub.storePath;
+ bootloaderId = if args.efiBootloaderId == null then "NixOS${efiSysMountPoint'}" else args.efiBootloaderId;
+ timeout = if config.boot.loader.timeout == null then -1 else config.boot.loader.timeout;
+ users = if cfg.users == {} || cfg.version != 1 then cfg.users else throw "GRUB version 1 does not support user accounts.";
+ theme = f cfg.theme;
+ inherit efiSysMountPoint;
+ inherit (args) devices;
+ inherit (efi) canTouchEfiVariables;
+ inherit (cfg)
+ version extraConfig extraPerEntryConfig extraEntries forceInstall useOSProber
+ extraGrubInstallArgs
+ extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels
+ default fsIdentifier efiSupport efiInstallAsRemovable gfxmodeEfi gfxmodeBios gfxpayloadEfi gfxpayloadBios;
+ path = with pkgs; makeBinPath (
+ [ coreutils gnused gnugrep findutils diffutils btrfs-progs utillinux mdadm ]
+ ++ optional (cfg.efiSupport && (cfg.version == 2)) efibootmgr
+ ++ optionals cfg.useOSProber [ busybox os-prober ]);
+ font = if cfg.font == null then ""
+ else (if lib.last (lib.splitString "." cfg.font) == "pf2"
+ then cfg.font
+ else "${convertedFont}");
+ });
+
+ bootDeviceCounters = fold (device: attr: attr // { ${device} = (attr.${device} or 0) + 1; }) {}
+ (concatMap (args: args.devices) cfg.mirroredBoots);
+
+ convertedFont = (pkgs.runCommand "grub-font-converted.pf2" {}
+ (builtins.concatStringsSep " "
+ ([ "${realGrub}/bin/grub-mkfont"
+ cfg.font
+ "--output" "$out"
+ ] ++ (optional (cfg.fontSize!=null) "--size ${toString cfg.fontSize}")))
+ );
+
+ defaultSplash = pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader.gnomeFilePath;
+in
+
+{
+
+ ###### interface
+
+ options = {
+
+ boot.loader.grub = {
+
+ enable = mkOption {
+ default = !config.boot.isContainer;
+ type = types.bool;
+ description = ''
+ Whether to enable the GNU GRUB boot loader.
+ '';
+ };
+
+ version = mkOption {
+ default = 2;
+ example = 1;
+ type = types.int;
+ description = ''
+ The version of GRUB to use: <literal>1</literal> for GRUB
+ Legacy (versions 0.9x), or <literal>2</literal> (the
+ default) for GRUB 2.
+ '';
+ };
+
+ device = mkOption {
+ default = "";
+ example = "/dev/disk/by-id/wwn-0x500001234567890a";
+ type = types.str;
+ description = ''
+ The device on which the GRUB boot loader will be installed.
+ The special value <literal>nodev</literal> means that a GRUB
+ boot menu will be generated, but GRUB itself will not
+ actually be installed. To install GRUB on multiple devices,
+ use <literal>boot.loader.grub.devices</literal>.
+ '';
+ };
+
+ devices = mkOption {
+ default = [];
+ example = [ "/dev/disk/by-id/wwn-0x500001234567890a" ];
+ type = types.listOf types.str;
+ description = ''
+ The devices on which the boot loader, GRUB, will be
+ installed. Can be used instead of <literal>device</literal> to
+ install GRUB onto multiple devices.
+ '';
+ };
+
+ users = mkOption {
+ default = {};
+ example = {
+ root = { hashedPasswordFile = "/path/to/file"; };
+ };
+ description = ''
+ User accounts for GRUB. When specified, the GRUB command line and
+ all boot options except the default are password-protected.
+ All passwords and hashes provided will be stored in /boot/grub/grub.cfg,
+ and will be visible to any local user who can read this file. Additionally,
+ any passwords and hashes provided directly in a Nix configuration
+ (as opposed to external files) will be copied into the Nix store, and
+ will be visible to all local users.
+ '';
+ type = with types; attrsOf (submodule {
+ options = {
+ hashedPasswordFile = mkOption {
+ example = "/path/to/file";
+ default = null;
+ type = with types; uniq (nullOr str);
+ description = ''
+ Specifies the path to a file containing the password hash
+ for the account, generated with grub-mkpasswd-pbkdf2.
+ This hash will be stored in /boot/grub/grub.cfg, and will
+ be visible to any local user who can read this file.
+ '';
+ };
+ hashedPassword = mkOption {
+ example = "grub.pbkdf2.sha512.10000.674DFFDEF76E13EA...2CC972B102CF4355";
+ default = null;
+ type = with types; uniq (nullOr str);
+ description = ''
+ Specifies the password hash for the account,
+ generated with grub-mkpasswd-pbkdf2.
+ This hash will be copied to the Nix store, and will be visible to all local users.
+ '';
+ };
+ passwordFile = mkOption {
+ example = "/path/to/file";
+ default = null;
+ type = with types; uniq (nullOr str);
+ description = ''
+ Specifies the path to a file containing the
+ clear text password for the account.
+ This password will be stored in /boot/grub/grub.cfg, and will
+ be visible to any local user who can read this file.
+ '';
+ };
+ password = mkOption {
+ example = "Pa$$w0rd!";
+ default = null;
+ type = with types; uniq (nullOr str);
+ description = ''
+ Specifies the clear text password for the account.
+ This password will be copied to the Nix store, and will be visible to all local users.
+ '';
+ };
+ };
+ });
+ };
+
+ mirroredBoots = mkOption {
+ default = [ ];
+ example = [
+ { path = "/boot1"; devices = [ "/dev/disk/by-id/wwn-0x500001234567890a" ]; }
+ { path = "/boot2"; devices = [ "/dev/disk/by-id/wwn-0x500009876543210a" ]; }
+ ];
+ description = ''
+ Mirror the boot configuration to multiple partitions and install grub
+ to the respective devices corresponding to those partitions.
+ '';
+
+ type = with types; listOf (submodule {
+ options = {
+
+ path = mkOption {
+ example = "/boot1";
+ type = types.str;
+ description = ''
+ The path to the boot directory where GRUB will be written. Generally
+ this boot path should double as an EFI path.
+ '';
+ };
+
+ efiSysMountPoint = mkOption {
+ default = null;
+ example = "/boot1/efi";
+ type = types.nullOr types.str;
+ description = ''
+ The path to the efi system mount point. Usually this is the same
+ partition as the above path and can be left as null.
+ '';
+ };
+
+ efiBootloaderId = mkOption {
+ default = null;
+ example = "NixOS-fsid";
+ type = types.nullOr types.str;
+ description = ''
+ The id of the bootloader to store in efi nvram.
+ The default is to name it NixOS and append the path or efiSysMountPoint.
+ This is only used if <literal>boot.loader.efi.canTouchEfiVariables</literal> is true.
+ '';
+ };
+
+ devices = mkOption {
+ default = [ ];
+ example = [ "/dev/disk/by-id/wwn-0x500001234567890a" "/dev/disk/by-id/wwn-0x500009876543210a" ];
+ type = types.listOf types.str;
+ description = ''
+ The path to the devices which will have the GRUB MBR written.
+ Note these are typically device paths and not paths to partitions.
+ '';
+ };
+
+ };
+ });
+ };
+
+ configurationName = mkOption {
+ default = "";
+ example = "Stable 2.6.21";
+ type = types.str;
+ description = ''
+ GRUB entry name instead of default.
+ '';
+ };
+
+ storePath = mkOption {
+ default = "/nix/store";
+ type = types.str;
+ description = ''
+ Path to the Nix store when looking for kernels at boot.
+ Only makes sense when copyKernels is false.
+ '';
+ };
+
+ extraPrepareConfig = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Additional bash commands to be run at the script that
+ prepares the GRUB menu entries.
+ '';
+ };
+
+ extraConfig = mkOption {
+ default = "";
+ example = ''
+ serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
+ terminal_input --append serial
+ terminal_output --append serial
+ '';
+ type = types.lines;
+ description = ''
+ Additional GRUB commands inserted in the configuration file
+ just before the menu entries.
+ '';
+ };
+
+ extraGrubInstallArgs = mkOption {
+ default = [ ];
+ example = [ "--modules=nativedisk ahci pata part_gpt part_msdos diskfilter mdraid1x lvm ext2" ];
+ type = types.listOf types.str;
+ description = ''
+ Additional arguments passed to <literal>grub-install</literal>.
+
+ A use case for this is to build specific GRUB2 modules
+ directly into the GRUB2 kernel image, so that they are available
+ and activated even in the <literal>grub rescue</literal> shell.
+
+ They are also necessary when the BIOS/UEFI is bugged and cannot
+ correctly read large disks (e.g. above 2 TB), so GRUB2's own
+ <literal>nativedisk</literal> and related modules can be used
+ to use its own disk drivers. The example shows one such case.
+ This is also useful for booting from USB.
+ See the
+ <link xlink:href="http://git.savannah.gnu.org/cgit/grub.git/tree/grub-core/commands/nativedisk.c?h=grub-2.04#n326">
+ GRUB source code
+ </link>
+ for which disk modules are available.
+
+ The list elements are passed directly as <literal>argv</literal>
+ arguments to the <literal>grub-install</literal> program, in order.
+ '';
+ };
+
+ extraPerEntryConfig = mkOption {
+ default = "";
+ example = "root (hd0)";
+ type = types.lines;
+ description = ''
+ Additional GRUB commands inserted in the configuration file
+ at the start of each NixOS menu entry.
+ '';
+ };
+
+ extraEntries = mkOption {
+ default = "";
+ type = types.lines;
+ example = ''
+ # GRUB 1 example (not GRUB 2 compatible)
+ title Windows
+ chainloader (hd0,1)+1
+
+ # GRUB 2 example
+ menuentry "Windows 7" {
+ chainloader (hd0,4)+1
+ }
+
+ # GRUB 2 with UEFI example, chainloading another distro
+ menuentry "Fedora" {
+ set root=(hd1,1)
+ chainloader /efi/fedora/grubx64.efi
+ }
+ '';
+ description = ''
+ Any additional entries you want added to the GRUB boot menu.
+ '';
+ };
+
+ extraEntriesBeforeNixOS = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether extraEntries are included before the default option.
+ '';
+ };
+
+ extraFiles = mkOption {
+ type = types.attrsOf types.path;
+ default = {};
+ example = literalExample ''
+ { "memtest.bin" = "''${pkgs.memtest86plus}/memtest.bin"; }
+ '';
+ description = ''
+ A set of files to be copied to <filename>/boot</filename>.
+ Each attribute name denotes the destination file name in
+ <filename>/boot</filename>, while the corresponding
+ attribute value specifies the source file.
+ '';
+ };
+
+ useOSProber = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ If set to true, append entries for other OSs detected by os-prober.
+ '';
+ };
+
+ splashImage = mkOption {
+ type = types.nullOr types.path;
+ example = literalExample "./my-background.png";
+ description = ''
+ Background image used for GRUB.
+ Set to <literal>null</literal> to run GRUB in text mode.
+
+ <note><para>
+ For grub 1:
+ It must be a 640x480,
+ 14-colour image in XPM format, optionally compressed with
+ <command>gzip</command> or <command>bzip2</command>.
+ </para></note>
+
+ <note><para>
+ For grub 2:
+ File must be one of .png, .tga, .jpg, or .jpeg. JPEG images must
+ not be progressive.
+ The image will be scaled if necessary to fit the screen.
+ </para></note>
+ '';
+ };
+
+ backgroundColor = mkOption {
+ type = types.nullOr types.str;
+ example = "#7EBAE4";
+ default = null;
+ description = ''
+ Background color to be used for GRUB to fill the areas the image isn't filling.
+
+ <note><para>
+ This options has no effect for GRUB 1.
+ </para></note>
+ '';
+ };
+
+ theme = mkOption {
+ type = types.nullOr types.path;
+ example = literalExample "pkgs.nixos-grub2-theme";
+ default = null;
+ description = ''
+ Grub theme to be used.
+
+ <note><para>
+ This options has no effect for GRUB 1.
+ </para></note>
+ '';
+ };
+
+ splashMode = mkOption {
+ type = types.enum [ "normal" "stretch" ];
+ default = "stretch";
+ description = ''
+ Whether to stretch the image or show the image in the top-left corner unstretched.
+
+ <note><para>
+ This options has no effect for GRUB 1.
+ </para></note>
+ '';
+ };
+
+ font = mkOption {
+ type = types.nullOr types.path;
+ default = "${realGrub}/share/grub/unicode.pf2";
+ defaultText = ''"''${pkgs.grub2}/share/grub/unicode.pf2"'';
+ description = ''
+ Path to a TrueType, OpenType, or pf2 font to be used by Grub.
+ '';
+ };
+
+ fontSize = mkOption {
+ type = types.nullOr types.int;
+ example = literalExample 16;
+ default = null;
+ description = ''
+ Font size for the grub menu. Ignored unless <literal>font</literal>
+ is set to a ttf or otf font.
+ '';
+ };
+
+ gfxmodeEfi = mkOption {
+ default = "auto";
+ example = "1024x768";
+ type = types.str;
+ description = ''
+ The gfxmode to pass to GRUB when loading a graphical boot interface under EFI.
+ '';
+ };
+
+ gfxmodeBios = mkOption {
+ default = "1024x768";
+ example = "auto";
+ type = types.str;
+ description = ''
+ The gfxmode to pass to GRUB when loading a graphical boot interface under BIOS.
+ '';
+ };
+
+ gfxpayloadEfi = mkOption {
+ default = "keep";
+ example = "text";
+ type = types.str;
+ description = ''
+ The gfxpayload to pass to GRUB when loading a graphical boot interface under EFI.
+ '';
+ };
+
+ gfxpayloadBios = mkOption {
+ default = "text";
+ example = "keep";
+ type = types.str;
+ description = ''
+ The gfxpayload to pass to GRUB when loading a graphical boot interface under BIOS.
+ '';
+ };
+
+ configurationLimit = mkOption {
+ default = 100;
+ example = 120;
+ type = types.int;
+ description = ''
+ Maximum of configurations in boot menu. GRUB has problems when
+ there are too many entries.
+ '';
+ };
+
+ copyKernels = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether the GRUB menu builder should copy kernels and initial
+ ramdisks to /boot. This is done automatically if /boot is
+ on a different partition than /.
+ '';
+ };
+
+ default = mkOption {
+ default = "0";
+ type = types.either types.int types.str;
+ apply = toString;
+ description = ''
+ Index of the default menu item to be booted.
+ '';
+ };
+
+ fsIdentifier = mkOption {
+ default = "uuid";
+ type = types.enum [ "uuid" "label" "provided" ];
+ description = ''
+ Determines how GRUB will identify devices when generating the
+ configuration file. A value of uuid / label signifies that grub
+ will always resolve the uuid or label of the device before using
+ it in the configuration. A value of provided means that GRUB will
+ use the device name as show in <command>df</command> or
+ <command>mount</command>. Note, zfs zpools / datasets are ignored
+ and will always be mounted using their labels.
+ '';
+ };
+
+ zfsSupport = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether GRUB should be built against libzfs.
+ ZFS support is only available for GRUB v2.
+ This option is ignored for GRUB v1.
+ '';
+ };
+
+ efiSupport = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether GRUB should be built with EFI support.
+ EFI support is only available for GRUB v2.
+ This option is ignored for GRUB v1.
+ '';
+ };
+
+ efiInstallAsRemovable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to invoke <literal>grub-install</literal> with
+ <literal>--removable</literal>.</para>
+
+ <para>Unless you turn this on, GRUB will install itself somewhere in
+ <literal>boot.loader.efi.efiSysMountPoint</literal> (exactly where
+ depends on other config variables). If you've set
+ <literal>boot.loader.efi.canTouchEfiVariables</literal> *AND* you
+ are currently booted in UEFI mode, then GRUB will use
+ <literal>efibootmgr</literal> to modify the boot order in the
+ EFI variables of your firmware to include this location. If you are
+ *not* booted in UEFI mode at the time GRUB is being installed, the
+ NVRAM will not be modified, and your system will not find GRUB at
+ boot time. However, GRUB will still return success so you may miss
+ the warning that gets printed ("<literal>efibootmgr: EFI variables
+ are not supported on this system.</literal>").</para>
+
+ <para>If you turn this feature on, GRUB will install itself in a
+ special location within <literal>efiSysMountPoint</literal> (namely
+ <literal>EFI/boot/boot$arch.efi</literal>) which the firmwares
+ are hardcoded to try first, regardless of NVRAM EFI variables.</para>
+
+ <para>To summarize, turn this on if:
+ <itemizedlist>
+ <listitem><para>You are installing NixOS and want it to boot in UEFI mode,
+ but you are currently booted in legacy mode</para></listitem>
+ <listitem><para>You want to make a drive that will boot regardless of
+ the NVRAM state of the computer (like a USB "removable" drive)</para></listitem>
+ <listitem><para>You simply dislike the idea of depending on NVRAM
+ state to make your drive bootable</para></listitem>
+ </itemizedlist>
+ '';
+ };
+
+ enableCryptodisk = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Enable support for encrypted partitions. GRUB should automatically
+ unlock the correct encrypted partition and look for filesystems.
+ '';
+ };
+
+ forceInstall = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to try and forcibly install GRUB even if problems are
+ detected. It is not recommended to enable this unless you know what
+ you are doing.
+ '';
+ };
+
+ forcei686 = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to force the use of a ia32 boot loader on x64 systems. Required
+ to install and run NixOS on 64bit x86 systems with 32bit (U)EFI.
+ '';
+ };
+
+ trustedBoot = {
+
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Enable trusted boot. GRUB will measure all critical components during
+ the boot process to offer TCG (TPM) support.
+ '';
+ };
+
+ systemHasTPM = mkOption {
+ default = "";
+ example = "YES_TPM_is_activated";
+ type = types.str;
+ description = ''
+ Assertion that the target system has an activated TPM. It is a safety
+ check before allowing the activation of 'trustedBoot.enable'. TrustedBoot
+ WILL FAIL TO BOOT YOUR SYSTEM if no TPM is available.
+ '';
+ };
+
+ isHPLaptop = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Use a special version of TrustedGRUB that is needed by some HP laptops
+ and works only for the HP laptops.
+ '';
+ };
+
+ };
+
+ };
+
+ };
+
+
+ ###### implementation
+
+ config = mkMerge [
+
+ { boot.loader.grub.splashImage = mkDefault (
+ if cfg.version == 1 then pkgs.fetchurl {
+ url = "http://www.gnome-look.org/CONTENT/content-files/36909-soft-tux.xpm.gz";
+ sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59";
+ }
+ # GRUB 1.97 doesn't support gzipped XPMs.
+ else defaultSplash);
+ }
+
+ (mkIf (cfg.splashImage == defaultSplash) {
+ boot.loader.grub.backgroundColor = mkDefault "#2F302F";
+ boot.loader.grub.splashMode = mkDefault "normal";
+ })
+
+ (mkIf cfg.enable {
+
+ boot.loader.grub.devices = optional (cfg.device != "") cfg.device;
+
+ boot.loader.grub.mirroredBoots = optionals (cfg.devices != [ ]) [
+ { path = "/boot"; inherit (cfg) devices; inherit (efi) efiSysMountPoint; }
+ ];
+
+ boot.loader.supportsInitrdSecrets = true;
+
+ system.build.installBootLoader =
+ let
+ install-grub-pl = pkgs.substituteAll {
+ src = ./install-grub.pl;
+ inherit (pkgs) utillinux;
+ btrfsprogs = pkgs.btrfs-progs;
+ };
+ in pkgs.writeScript "install-grub.sh" (''
+ #!${pkgs.runtimeShell}
+ set -e
+ export PERL5LIB=${with pkgs.perlPackages; makePerlPath [ FileSlurp FileCopyRecursive XMLLibXML XMLSAX XMLSAXBase ListCompare JSON ]}
+ ${optionalString cfg.enableCryptodisk "export GRUB_ENABLE_CRYPTODISK=y"}
+ '' + flip concatMapStrings cfg.mirroredBoots (args: ''
+ ${pkgs.perl}/bin/perl ${install-grub-pl} ${grubConfig args} $@
+ ''));
+
+ system.build.grub = grub;
+
+ # Common attribute for boot loaders so only one of them can be
+ # set at once.
+ system.boot.loader.id = "grub";
+
+ environment.systemPackages = optional (grub != null) grub;
+
+ boot.loader.grub.extraPrepareConfig =
+ concatStrings (mapAttrsToList (n: v: ''
+ ${pkgs.coreutils}/bin/cp -pf "${v}" "@bootPath@/${n}"
+ '') config.boot.loader.grub.extraFiles);
+
+ assertions = [
+ {
+ assertion = !cfg.zfsSupport || cfg.version == 2;
+ message = "Only GRUB version 2 provides ZFS support";
+ }
+ {
+ assertion = cfg.mirroredBoots != [ ];
+ message = "You must set the option ‘boot.loader.grub.devices’ or "
+ + "'boot.loader.grub.mirroredBoots' to make the system bootable.";
+ }
+ {
+ assertion = cfg.efiSupport || all (c: c < 2) (mapAttrsToList (_: c: c) bootDeviceCounters);
+ message = "You cannot have duplicated devices in mirroredBoots";
+ }
+ {
+ assertion = !cfg.trustedBoot.enable || cfg.version == 2;
+ message = "Trusted GRUB is only available for GRUB 2";
+ }
+ {
+ assertion = !cfg.efiSupport || !cfg.trustedBoot.enable;
+ message = "Trusted GRUB does not have EFI support";
+ }
+ {
+ assertion = !cfg.zfsSupport || !cfg.trustedBoot.enable;
+ message = "Trusted GRUB does not have ZFS support";
+ }
+ {
+ assertion = !cfg.trustedBoot.enable || cfg.trustedBoot.systemHasTPM == "YES_TPM_is_activated";
+ message = "Trusted GRUB can break the system! Confirm that the system has an activated TPM by setting 'systemHasTPM'.";
+ }
+ {
+ assertion = cfg.efiInstallAsRemovable -> cfg.efiSupport;
+ message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn on boot.loader.grub.efiSupport";
+ }
+ {
+ assertion = cfg.efiInstallAsRemovable -> !config.boot.loader.efi.canTouchEfiVariables;
+ message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn off boot.loader.efi.canTouchEfiVariables";
+ }
+ ] ++ flip concatMap cfg.mirroredBoots (args: [
+ {
+ assertion = args.devices != [ ];
+ message = "A boot path cannot have an empty devices string in ${args.path}";
+ }
+ {
+ assertion = hasPrefix "/" args.path;
+ message = "Boot paths must be absolute, not ${args.path}";
+ }
+ {
+ assertion = if args.efiSysMountPoint == null then true else hasPrefix "/" args.efiSysMountPoint;
+ message = "EFI paths must be absolute, not ${args.efiSysMountPoint}";
+ }
+ ] ++ forEach args.devices (device: {
+ assertion = device == "nodev" || hasPrefix "/" device;
+ message = "GRUB devices must be absolute paths, not ${device} in ${args.path}";
+ }));
+ })
+
+ ];
+
+
+ imports =
+ [ (mkRemovedOptionModule [ "boot" "loader" "grub" "bootDevice" ] "")
+ (mkRenamedOptionModule [ "boot" "copyKernels" ] [ "boot" "loader" "grub" "copyKernels" ])
+ (mkRenamedOptionModule [ "boot" "extraGrubEntries" ] [ "boot" "loader" "grub" "extraEntries" ])
+ (mkRenamedOptionModule [ "boot" "extraGrubEntriesBeforeNixos" ] [ "boot" "loader" "grub" "extraEntriesBeforeNixOS" ])
+ (mkRenamedOptionModule [ "boot" "grubDevice" ] [ "boot" "loader" "grub" "device" ])
+ (mkRenamedOptionModule [ "boot" "bootMount" ] [ "boot" "loader" "grub" "bootDevice" ])
+ (mkRenamedOptionModule [ "boot" "grubSplashImage" ] [ "boot" "loader" "grub" "splashImage" ])
+ (mkRemovedOptionModule [ "boot" "loader" "grub" "extraInitrd" ] ''
+ This option has been replaced with the bootloader agnostic
+ boot.initrd.secrets option. To migrate to the initrd secrets system,
+ extract the extraInitrd archive into your main filesystem:
+
+ # zcat /boot/extra_initramfs.gz | cpio -idvmD /etc/secrets/initrd
+ /path/to/secret1
+ /path/to/secret2
+
+ then replace boot.loader.grub.extraInitrd with boot.initrd.secrets:
+
+ boot.initrd.secrets = {
+ "/path/to/secret1" = "/etc/secrets/initrd/path/to/secret1";
+ "/path/to/secret2" = "/etc/secrets/initrd/path/to/secret2";
+ };
+
+ See the boot.initrd.secrets option documentation for more information.
+ '')
+ ];
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
new file mode 100644
index 000000000000..59f5638044fe
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -0,0 +1,754 @@
+use strict;
+use warnings;
+use Class::Struct;
+use XML::LibXML;
+use File::Basename;
+use File::Path;
+use File::stat;
+use File::Copy;
+use File::Copy::Recursive qw(rcopy pathrm);
+use File::Slurp;
+use File::Temp;
+use JSON;
+use File::Find;
+require List::Compare;
+use POSIX;
+use Cwd;
+
+# system.build.toplevel path
+my $defaultConfig = $ARGV[1] or die;
+
+# Grub config XML generated by grubConfig function in grub.nix
+my $dom = XML::LibXML->load_xml(location => $ARGV[0]);
+
+sub get { my ($name) = @_; return $dom->findvalue("/expr/attrs/attr[\@name = '$name']/*/\@value"); }
+
+sub getList {
+ my ($name) = @_;
+ my @list = ();
+ foreach my $entry ($dom->findnodes("/expr/attrs/attr[\@name = '$name']/list/string/\@value")) {
+ $entry = $entry->findvalue(".") or die;
+ push(@list, $entry);
+ }
+ return @list;
+}
+
+sub readFile {
+ my ($fn) = @_; local $/ = undef;
+ open FILE, "<$fn" or return undef; my $s = <FILE>; close FILE;
+ local $/ = "\n"; chomp $s; return $s;
+}
+
+sub writeFile {
+ my ($fn, $s) = @_;
+ open FILE, ">$fn" or die "cannot create $fn: $!\n";
+ print FILE $s or die;
+ close FILE or die;
+}
+
+sub runCommand {
+ my ($cmd) = @_;
+ open FILE, "$cmd 2>/dev/null |" or die "Failed to execute: $cmd\n";
+ my @ret = <FILE>;
+ close FILE;
+ return ($?, @ret);
+}
+
+my $grub = get("grub");
+my $grubVersion = int(get("version"));
+my $grubTarget = get("grubTarget");
+my $extraConfig = get("extraConfig");
+my $extraPrepareConfig = get("extraPrepareConfig");
+my $extraPerEntryConfig = get("extraPerEntryConfig");
+my $extraEntries = get("extraEntries");
+my $extraEntriesBeforeNixOS = get("extraEntriesBeforeNixOS") eq "true";
+my $splashImage = get("splashImage");
+my $splashMode = get("splashMode");
+my $backgroundColor = get("backgroundColor");
+my $configurationLimit = int(get("configurationLimit"));
+my $copyKernels = get("copyKernels") eq "true";
+my $timeout = int(get("timeout"));
+my $defaultEntry = get("default");
+my $fsIdentifier = get("fsIdentifier");
+my $grubEfi = get("grubEfi");
+my $grubTargetEfi = get("grubTargetEfi");
+my $bootPath = get("bootPath");
+my $storePath = get("storePath");
+my $canTouchEfiVariables = get("canTouchEfiVariables");
+my $efiInstallAsRemovable = get("efiInstallAsRemovable");
+my $efiSysMountPoint = get("efiSysMountPoint");
+my $gfxmodeEfi = get("gfxmodeEfi");
+my $gfxmodeBios = get("gfxmodeBios");
+my $gfxpayloadEfi = get("gfxpayloadEfi");
+my $gfxpayloadBios = get("gfxpayloadBios");
+my $bootloaderId = get("bootloaderId");
+my $forceInstall = get("forceInstall");
+my $font = get("font");
+my $theme = get("theme");
+$ENV{'PATH'} = get("path");
+
+die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
+
+print STDERR "updating GRUB $grubVersion menu...\n";
+
+mkpath("$bootPath/grub", 0, 0700);
+
+# Discover whether the bootPath is on the same filesystem as / and
+# /nix/store. If not, then all kernels and initrds must be copied to
+# the bootPath.
+if (stat($bootPath)->dev != stat("/nix/store")->dev) {
+ $copyKernels = 1;
+}
+
+# Discover information about the location of the bootPath
+struct(Fs => {
+ device => '$',
+ type => '$',
+ mount => '$',
+});
+sub PathInMount {
+ my ($path, $mount) = @_;
+ my @splitMount = split /\//, $mount;
+ my @splitPath = split /\//, $path;
+ if ($#splitPath < $#splitMount) {
+ return 0;
+ }
+ for (my $i = 0; $i <= $#splitMount; $i++) {
+ if ($splitMount[$i] ne $splitPath[$i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+# Figure out what filesystem is used for the directory with init/initrd/kernel files
+sub GetFs {
+ my ($dir) = @_;
+ my $bestFs = Fs->new(device => "", type => "", mount => "");
+ foreach my $fs (read_file("/proc/self/mountinfo")) {
+ chomp $fs;
+ my @fields = split / /, $fs;
+ my $mountPoint = $fields[4];
+ next unless -d $mountPoint;
+ my @mountOptions = split /,/, $fields[5];
+
+ # Skip the optional fields.
+ my $n = 6; $n++ while $fields[$n] ne "-"; $n++;
+ my $fsType = $fields[$n];
+ my $device = $fields[$n + 1];
+ my @superOptions = split /,/, $fields[$n + 2];
+
+ # Skip the bind-mount on /nix/store.
+ next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions);
+ # Skip mount point generated by systemd-efi-boot-generator?
+ next if $fsType eq "autofs";
+
+ # Ensure this matches the intended directory
+ next unless PathInMount($dir, $mountPoint);
+
+ # Is it better than our current match?
+ if (length($mountPoint) > length($bestFs->mount)) {
+ $bestFs = Fs->new(device => $device, type => $fsType, mount => $mountPoint);
+ }
+ }
+ return $bestFs;
+}
+struct (Grub => {
+ path => '$',
+ search => '$',
+});
+my $driveid = 1;
+sub GrubFs {
+ my ($dir) = @_;
+ my $fs = GetFs($dir);
+ my $path = substr($dir, length($fs->mount));
+ if (substr($path, 0, 1) ne "/") {
+ $path = "/$path";
+ }
+ my $search = "";
+
+ if ($grubVersion > 1) {
+ # ZFS is completely separate logic as zpools are always identified by a label
+ # or custom UUID
+ if ($fs->type eq 'zfs') {
+ my $sid = index($fs->device, '/');
+
+ if ($sid < 0) {
+ $search = '--label ' . $fs->device;
+ $path = '/@' . $path;
+ } else {
+ $search = '--label ' . substr($fs->device, 0, $sid);
+ $path = '/' . substr($fs->device, $sid) . '/@' . $path;
+ }
+ } else {
+ my %types = ('uuid' => '--fs-uuid', 'label' => '--label');
+
+ if ($fsIdentifier eq 'provided') {
+ # If the provided dev is identifying the partition using a label or uuid,
+ # we should get the label / uuid and do a proper search
+ my @matches = $fs->device =~ m/\/dev\/disk\/by-(label|uuid)\/(.*)/;
+ if ($#matches > 1) {
+ die "Too many matched devices"
+ } elsif ($#matches == 1) {
+ $search = "$types{$matches[0]} $matches[1]"
+ }
+ } else {
+ # Determine the identifying type
+ $search = $types{$fsIdentifier} . ' ';
+
+ # Based on the type pull in the identifier from the system
+ my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid -o export @{[$fs->device]}");
+ if ($status != 0) {
+ die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}";
+ }
+ my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/;
+ if ($#matches != 0) {
+ die "Couldn't find a $types{$fsIdentifier} for @{[$fs->device]}\n"
+ }
+ $search .= $matches[0];
+ }
+
+ # BTRFS is a special case in that we need to fix the referrenced path based on subvolumes
+ if ($fs->type eq 'btrfs') {
+ my ($status, @id_info) = runCommand("@btrfsprogs@/bin/btrfs subvol show @{[$fs->mount]}");
+ if ($status != 0) {
+ die "Failed to retrieve subvolume info for @{[$fs->mount]}\n";
+ }
+ my @ids = join("\n", @id_info) =~ m/^(?!\/\n).*Subvolume ID:[ \t\n]*([0-9]+)/s;
+ if ($#ids > 0) {
+ die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n"
+ } elsif ($#ids == 0) {
+ my ($status, @path_info) = runCommand("@btrfsprogs@/bin/btrfs subvol list @{[$fs->mount]}");
+ if ($status != 0) {
+ die "Failed to find @{[$fs->mount]} subvolume id from btrfs\n";
+ }
+ my @paths = join("", @path_info) =~ m/ID $ids[0] [^\n]* path ([^\n]*)/;
+ if ($#paths > 0) {
+ die "Btrfs returned multiple paths for a single subvolume id, mountpoint @{[$fs->mount]}\n";
+ } elsif ($#paths != 0) {
+ die "Btrfs did not return a path for the subvolume at @{[$fs->mount]}\n";
+ }
+ $path = "/$paths[0]$path";
+ }
+ }
+ }
+ if (not $search eq "") {
+ $search = "search --set=drive$driveid " . $search;
+ $path = "(\$drive$driveid)$path";
+ $driveid += 1;
+ }
+ }
+ return Grub->new(path => $path, search => $search);
+}
+my $grubBoot = GrubFs($bootPath);
+my $grubStore;
+if ($copyKernels == 0) {
+ $grubStore = GrubFs($storePath);
+}
+
+# Generate the header.
+my $conf .= "# Automatically generated. DO NOT EDIT THIS FILE!\n";
+
+if ($grubVersion == 1) {
+ $conf .= "
+ default $defaultEntry
+ timeout $timeout
+ ";
+ if ($splashImage) {
+ copy $splashImage, "$bootPath/background.xpm.gz" or die "cannot copy $splashImage to $bootPath: $!\n";
+ $conf .= "splashimage " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background.xpm.gz\n";
+ }
+}
+
+else {
+ my @users = ();
+ foreach my $user ($dom->findnodes('/expr/attrs/attr[@name = "users"]/attrs/attr')) {
+ my $name = $user->findvalue('@name') or die;
+ my $hashedPassword = $user->findvalue('./attrs/attr[@name = "hashedPassword"]/string/@value');
+ my $hashedPasswordFile = $user->findvalue('./attrs/attr[@name = "hashedPasswordFile"]/string/@value');
+ my $password = $user->findvalue('./attrs/attr[@name = "password"]/string/@value');
+ my $passwordFile = $user->findvalue('./attrs/attr[@name = "passwordFile"]/string/@value');
+
+ if ($hashedPasswordFile) {
+ open(my $f, '<', $hashedPasswordFile) or die "Can't read file '$hashedPasswordFile'!";
+ $hashedPassword = <$f>;
+ chomp $hashedPassword;
+ }
+ if ($passwordFile) {
+ open(my $f, '<', $passwordFile) or die "Can't read file '$passwordFile'!";
+ $password = <$f>;
+ chomp $password;
+ }
+
+ if ($hashedPassword) {
+ if (index($hashedPassword, "grub.pbkdf2.") == 0) {
+ $conf .= "\npassword_pbkdf2 $name $hashedPassword";
+ }
+ else {
+ die "Password hash for GRUB user '$name' is not valid!";
+ }
+ }
+ elsif ($password) {
+ $conf .= "\npassword $name $password";
+ }
+ else {
+ die "GRUB user '$name' has no password!";
+ }
+ push(@users, $name);
+ }
+ if (@users) {
+ $conf .= "\nset superusers=\"" . join(' ',@users) . "\"\n";
+ }
+
+ if ($copyKernels == 0) {
+ $conf .= "
+ " . $grubStore->search;
+ }
+ # FIXME: should use grub-mkconfig.
+ $conf .= "
+ " . $grubBoot->search . "
+ if [ -s \$prefix/grubenv ]; then
+ load_env
+ fi
+
+ # ‘grub-reboot’ sets a one-time saved entry, which we process here and
+ # then delete.
+ if [ \"\${next_entry}\" ]; then
+ set default=\"\${next_entry}\"
+ set next_entry=
+ save_env next_entry
+ set timeout=1
+ else
+ set default=$defaultEntry
+ set timeout=$timeout
+ fi
+
+ # Setup the graphics stack for bios and efi systems
+ if [ \"\${grub_platform}\" = \"efi\" ]; then
+ insmod efi_gop
+ insmod efi_uga
+ else
+ insmod vbe
+ fi
+ ";
+
+ if ($font) {
+ copy $font, "$bootPath/converted-font.pf2" or die "cannot copy $font to $bootPath: $!\n";
+ $conf .= "
+ insmod font
+ if loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/converted-font.pf2; then
+ insmod gfxterm
+ if [ \"\${grub_platform}\" = \"efi\" ]; then
+ set gfxmode=$gfxmodeEfi
+ set gfxpayload=$gfxpayloadEfi
+ else
+ set gfxmode=$gfxmodeBios
+ set gfxpayload=$gfxpayloadBios
+ fi
+ terminal_output gfxterm
+ fi
+ ";
+ }
+ if ($splashImage) {
+ # Keeps the image's extension.
+ my ($filename, $dirs, $suffix) = fileparse($splashImage, qr"\..[^.]*$");
+ # The module for jpg is jpeg.
+ if ($suffix eq ".jpg") {
+ $suffix = ".jpeg";
+ }
+ if ($backgroundColor) {
+ $conf .= "
+ background_color '$backgroundColor'
+ ";
+ }
+ copy $splashImage, "$bootPath/background$suffix" or die "cannot copy $splashImage to $bootPath: $!\n";
+ $conf .= "
+ insmod " . substr($suffix, 1) . "
+ if background_image --mode '$splashMode' " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background$suffix; then
+ set color_normal=white/black
+ set color_highlight=black/white
+ else
+ set menu_color_normal=cyan/blue
+ set menu_color_highlight=white/blue
+ fi
+ ";
+ }
+
+ rmtree("$bootPath/theme") or die "cannot clean up theme folder in $bootPath\n" if -e "$bootPath/theme";
+
+ if ($theme) {
+ # Copy theme
+ rcopy($theme, "$bootPath/theme") or die "cannot copy $theme to $bootPath\n";
+ $conf .= "
+ # Sets theme.
+ set theme=" . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/theme.txt
+ export theme
+ # Load theme fonts, if any
+ ";
+
+ find( { wanted => sub {
+ if ($_ =~ /\.pf2$/i) {
+ $font = File::Spec->abs2rel($File::Find::name, $theme);
+ $conf .= "
+ loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/$font
+ ";
+ }
+ }, no_chdir => 1 }, $theme );
+ }
+}
+
+$conf .= "$extraConfig\n";
+
+
+# Generate the menu entries.
+$conf .= "\n";
+
+my %copied;
+mkpath("$bootPath/kernels", 0, 0755) if $copyKernels;
+
+sub copyToKernelsDir {
+ my ($path) = @_;
+ return $grubStore->path . substr($path, length("/nix/store")) unless $copyKernels;
+ $path =~ /\/nix\/store\/(.*)/ or die;
+ my $name = $1; $name =~ s/\//-/g;
+ my $dst = "$bootPath/kernels/$name";
+ # Don't copy the file if $dst already exists. This means that we
+ # have to create $dst atomically to prevent partially copied
+ # kernels or initrd if this script is ever interrupted.
+ if (! -e $dst) {
+ my $tmp = "$dst.tmp";
+ copy $path, $tmp or die "cannot copy $path to $tmp: $!\n";
+ rename $tmp, $dst or die "cannot rename $tmp to $dst: $!\n";
+ }
+ $copied{$dst} = 1;
+ return ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/kernels/$name";
+}
+
+sub addEntry {
+ my ($name, $path, $options) = @_;
+ return unless -e "$path/kernel" && -e "$path/initrd";
+
+ my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel"));
+ my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd"));
+
+ # Include second initrd with secrets
+ if (-e -x "$path/append-initrd-secrets") {
+ my $initrdName = basename($initrd);
+ my $initrdSecretsPath = "$bootPath/kernels/$initrdName-secrets";
+
+ mkpath(dirname($initrdSecretsPath), 0, 0755);
+ my $oldUmask = umask;
+ # Make sure initrd is not world readable (won't work if /boot is FAT)
+ umask 0137;
+ my $initrdSecretsPathTemp = File::Temp::mktemp("$initrdSecretsPath.XXXXXXXX");
+ system("$path/append-initrd-secrets", $initrdSecretsPathTemp) == 0 or die "failed to create initrd secrets: $!\n";
+ # Check whether any secrets were actually added
+ if (-e $initrdSecretsPathTemp && ! -z _) {
+ rename $initrdSecretsPathTemp, $initrdSecretsPath or die "failed to move initrd secrets into place: $!\n";
+ $copied{$initrdSecretsPath} = 1;
+ $initrd .= " " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/kernels/$initrdName-secrets";
+ } else {
+ unlink $initrdSecretsPathTemp;
+ rmdir dirname($initrdSecretsPathTemp);
+ }
+ umask $oldUmask;
+ }
+
+ my $xen = -e "$path/xen.gz" ? copyToKernelsDir(Cwd::abs_path("$path/xen.gz")) : undef;
+
+ # FIXME: $confName
+
+ my $kernelParams =
+ "systemConfig=" . Cwd::abs_path($path) . " " .
+ "init=" . Cwd::abs_path("$path/init") . " " .
+ readFile("$path/kernel-params");
+ my $xenParams = $xen && -e "$path/xen-params" ? readFile("$path/xen-params") : "";
+
+ if ($grubVersion == 1) {
+ $conf .= "title $name\n";
+ $conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig;
+ $conf .= " kernel $xen $xenParams\n" if $xen;
+ $conf .= " " . ($xen ? "module" : "kernel") . " $kernel $kernelParams\n";
+ $conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n\n";
+ } else {
+ $conf .= "menuentry \"$name\" " . ($options||"") . " {\n";
+ $conf .= $grubBoot->search . "\n";
+ if ($copyKernels == 0) {
+ $conf .= $grubStore->search . "\n";
+ }
+ $conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig;
+ $conf .= " multiboot $xen $xenParams\n" if $xen;
+ $conf .= " " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n";
+ $conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n";
+ $conf .= "}\n\n";
+ }
+}
+
+
+# Add default entries.
+$conf .= "$extraEntries\n" if $extraEntriesBeforeNixOS;
+
+addEntry("NixOS - Default", $defaultConfig, "--unrestricted");
+
+$conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS;
+
+# Find all the children of the current default configuration
+# Do not search for grand children
+my @links = sort (glob "$defaultConfig/specialisation/*");
+foreach my $link (@links) {
+
+ my $entryName = "";
+
+ my $cfgName = readFile("$link/configuration-name");
+
+ my $date = strftime("%F", localtime(lstat($link)->mtime));
+ my $version =
+ -e "$link/nixos-version"
+ ? readFile("$link/nixos-version")
+ : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]);
+
+ if ($cfgName) {
+ $entryName = $cfgName;
+ } else {
+ my $linkname = basename($link);
+ $entryName = "($linkname - $date - $version)";
+ }
+ addEntry("NixOS - $entryName", $link);
+}
+
+my $grubBootPath = $grubBoot->path;
+# extraEntries could refer to @bootRoot@, which we have to substitute
+$conf =~ s/\@bootRoot\@/$grubBootPath/g;
+
+# Emit submenus for all system profiles.
+sub addProfile {
+ my ($profile, $description) = @_;
+
+ # Add entries for all generations of this profile.
+ $conf .= "submenu \"$description\" {\n" if $grubVersion == 2;
+
+ sub nrFromGen { my ($x) = @_; $x =~ /\/\w+-(\d+)-link/; return $1; }
+
+ my @links = sort
+ { nrFromGen($b) <=> nrFromGen($a) }
+ (glob "$profile-*-link");
+
+ my $curEntry = 0;
+ foreach my $link (@links) {
+ last if $curEntry++ >= $configurationLimit;
+ if (! -e "$link/nixos-version") {
+ warn "skipping corrupt system profile entry ‘$link’\n";
+ next;
+ }
+ my $date = strftime("%F", localtime(lstat($link)->mtime));
+ my $version =
+ -e "$link/nixos-version"
+ ? readFile("$link/nixos-version")
+ : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]);
+ addEntry("NixOS - Configuration " . nrFromGen($link) . " ($date - $version)", $link);
+ }
+
+ $conf .= "}\n" if $grubVersion == 2;
+}
+
+addProfile "/nix/var/nix/profiles/system", "NixOS - All configurations";
+
+if ($grubVersion == 2) {
+ for my $profile (glob "/nix/var/nix/profiles/system-profiles/*") {
+ my $name = basename($profile);
+ next unless $name =~ /^\w+$/;
+ addProfile $profile, "NixOS - Profile '$name'";
+ }
+}
+
+# extraPrepareConfig could refer to @bootPath@, which we have to substitute
+$extraPrepareConfig =~ s/\@bootPath\@/$bootPath/g;
+
+# Run extraPrepareConfig in sh
+if ($extraPrepareConfig ne "") {
+ system((get("shell"), "-c", $extraPrepareConfig));
+}
+
+# write the GRUB config.
+my $confFile = $grubVersion == 1 ? "$bootPath/grub/menu.lst" : "$bootPath/grub/grub.cfg";
+my $tmpFile = $confFile . ".tmp";
+writeFile($tmpFile, $conf);
+
+
+# check whether to install GRUB EFI or not
+sub getEfiTarget {
+ if ($grubVersion == 1) {
+ return "no"
+ } elsif (($grub ne "") && ($grubEfi ne "")) {
+ # EFI can only be installed when target is set;
+ # A target is also required then for non-EFI grub
+ if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die }
+ else { return "both" }
+ } elsif (($grub ne "") && ($grubEfi eq "")) {
+ # TODO: It would be safer to disallow non-EFI grub installation if no taget is given.
+ # If no target is given, then grub auto-detects the target which can lead to errors.
+ # E.g. it seems as if grub would auto-detect a EFI target based on the availability
+ # of a EFI partition.
+ # However, it seems as auto-detection is currently relied on for non-x86_64 and non-i386
+ # architectures in NixOS. That would have to be fixed in the nixos modules first.
+ return "no"
+ } elsif (($grub eq "") && ($grubEfi ne "")) {
+ # EFI can only be installed when target is set;
+ if ($grubTargetEfi eq "") { die }
+ else {return "only" }
+ } else {
+ # prevent an installation if neither grub nor grubEfi is given
+ return "neither"
+ }
+}
+
+my $efiTarget = getEfiTarget();
+
+# Append entries detected by os-prober
+if (get("useOSProber") eq "true") {
+ my $targetpackage = ($efiTarget eq "no") ? $grub : $grubEfi;
+ system(get("shell"), "-c", "pkgdatadir=$targetpackage/share/grub $targetpackage/etc/grub.d/30_os-prober >> $tmpFile");
+}
+
+# Atomically switch to the new config
+rename $tmpFile, $confFile or die "cannot rename $tmpFile to $confFile: $!\n";
+
+
+# Remove obsolete files from $bootPath/kernels.
+foreach my $fn (glob "$bootPath/kernels/*") {
+ next if defined $copied{$fn};
+ print STDERR "removing obsolete file $fn\n";
+ unlink $fn;
+}
+
+
+#
+# Install GRUB if the parameters changed from the last time we installed it.
+#
+
+struct(GrubState => {
+ name => '$',
+ version => '$',
+ efi => '$',
+ devices => '$',
+ efiMountPoint => '$',
+ extraGrubInstallArgs => '@',
+});
+# If you add something to the state file, only add it to the end
+# because it is read line-by-line.
+sub readGrubState {
+ my $defaultGrubState = GrubState->new(name => "", version => "", efi => "", devices => "", efiMountPoint => "", extraGrubInstallArgs => () );
+ open FILE, "<$bootPath/grub/state" or return $defaultGrubState;
+ local $/ = "\n";
+ my $name = <FILE>;
+ chomp($name);
+ my $version = <FILE>;
+ chomp($version);
+ my $efi = <FILE>;
+ chomp($efi);
+ my $devices = <FILE>;
+ chomp($devices);
+ my $efiMountPoint = <FILE>;
+ chomp($efiMountPoint);
+ # Historically, arguments in the state file were one per each line, but that
+ # gets really messy when newlines are involved, structured arguments
+ # like lists are needed (they have to have a separator encoding), or even worse,
+ # when we need to remove a setting in the future. Thus, the 6th line is a JSON
+ # object that can store structured data, with named keys, and all new state
+ # should go in there.
+ my $jsonStateLine = <FILE>;
+ # For historical reasons we do not check the values above for un-definedness
+ # (that is, when the state file has too few lines and EOF is reached),
+ # because the above come from the first version of this logic and are thus
+ # guaranteed to be present.
+ $jsonStateLine = defined $jsonStateLine ? $jsonStateLine : '{}'; # empty JSON object
+ chomp($jsonStateLine);
+ if ($jsonStateLine eq "") {
+ $jsonStateLine = '{}'; # empty JSON object
+ }
+ my %jsonState = %{decode_json($jsonStateLine)};
+ my @extraGrubInstallArgs = exists($jsonState{'extraGrubInstallArgs'}) ? @{$jsonState{'extraGrubInstallArgs'}} : ();
+ close FILE;
+ my $grubState = GrubState->new(name => $name, version => $version, efi => $efi, devices => $devices, efiMountPoint => $efiMountPoint, extraGrubInstallArgs => \@extraGrubInstallArgs );
+ return $grubState
+}
+
+my @deviceTargets = getList('devices');
+my $prevGrubState = readGrubState();
+my @prevDeviceTargets = split/,/, $prevGrubState->devices;
+my @extraGrubInstallArgs = getList('extraGrubInstallArgs');
+my @prevExtraGrubInstallArgs = @{$prevGrubState->extraGrubInstallArgs};
+
+my $devicesDiffer = scalar (List::Compare->new( '-u', '-a', \@deviceTargets, \@prevDeviceTargets)->get_symmetric_difference());
+my $extraGrubInstallArgsDiffer = scalar (List::Compare->new( '-u', '-a', \@extraGrubInstallArgs, \@prevExtraGrubInstallArgs)->get_symmetric_difference());
+my $nameDiffer = get("fullName") ne $prevGrubState->name;
+my $versionDiffer = get("fullVersion") ne $prevGrubState->version;
+my $efiDiffer = $efiTarget ne $prevGrubState->efi;
+my $efiMountPointDiffer = $efiSysMountPoint ne $prevGrubState->efiMountPoint;
+if (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1") {
+ warn "NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER";
+ $ENV{'NIXOS_INSTALL_BOOTLOADER'} = "1";
+}
+my $requireNewInstall = $devicesDiffer || $extraGrubInstallArgsDiffer || $nameDiffer || $versionDiffer || $efiDiffer || $efiMountPointDiffer || (($ENV{'NIXOS_INSTALL_BOOTLOADER'} // "") eq "1");
+
+# install a symlink so that grub can detect the boot drive
+my $tmpDir = File::Temp::tempdir(CLEANUP => 1) or die "Failed to create temporary space: $!";
+symlink "$bootPath", "$tmpDir/boot" or die "Failed to symlink $tmpDir/boot: $!";
+
+# install non-EFI GRUB
+if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) {
+ foreach my $dev (@deviceTargets) {
+ next if $dev eq "nodev";
+ print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n";
+ my @command = ("$grub/sbin/grub-install", "--recheck", "--root-directory=$tmpDir", Cwd::abs_path($dev), @extraGrubInstallArgs);
+ if ($forceInstall eq "true") {
+ push @command, "--force";
+ }
+ if ($grubTarget ne "") {
+ push @command, "--target=$grubTarget";
+ }
+ (system @command) == 0 or die "$0: installation of GRUB on $dev failed: $!\n";
+ }
+}
+
+
+# install EFI GRUB
+if (($requireNewInstall != 0) && ($efiTarget eq "only" || $efiTarget eq "both")) {
+ print STDERR "installing the GRUB $grubVersion EFI boot loader into $efiSysMountPoint...\n";
+ my @command = ("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--boot-directory=$bootPath", "--efi-directory=$efiSysMountPoint", @extraGrubInstallArgs);
+ if ($forceInstall eq "true") {
+ push @command, "--force";
+ }
+ if ($canTouchEfiVariables eq "true") {
+ push @command, "--bootloader-id=$bootloaderId";
+ } else {
+ push @command, "--no-nvram";
+ push @command, "--removable" if $efiInstallAsRemovable eq "true";
+ }
+
+ (system @command) == 0 or die "$0: installation of GRUB EFI into $efiSysMountPoint failed: $!\n";
+}
+
+
+# update GRUB state file
+if ($requireNewInstall != 0) {
+ # Temp file for atomic rename.
+ my $stateFile = "$bootPath/grub/state";
+ my $stateFileTmp = $stateFile . ".tmp";
+
+ open FILE, ">$stateFileTmp" or die "cannot create $stateFileTmp: $!\n";
+ print FILE get("fullName"), "\n" or die;
+ print FILE get("fullVersion"), "\n" or die;
+ print FILE $efiTarget, "\n" or die;
+ print FILE join( ",", @deviceTargets ), "\n" or die;
+ print FILE $efiSysMountPoint, "\n" or die;
+ my %jsonState = (
+ extraGrubInstallArgs => \@extraGrubInstallArgs
+ );
+ my $jsonStateLine = encode_json(\%jsonState);
+ print FILE $jsonStateLine, "\n" or die;
+ close FILE or die;
+
+ # Atomically switch to the new state file
+ rename $stateFileTmp, $stateFile or die "cannot rename $stateFileTmp to $stateFile: $!\n";
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/ipxe.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/ipxe.nix
new file mode 100644
index 000000000000..249c2761934d
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/ipxe.nix
@@ -0,0 +1,64 @@
+# This module adds a scripted iPXE entry to the GRUB boot menu.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ scripts = builtins.attrNames config.boot.loader.grub.ipxe;
+
+ grubEntry = name:
+ ''
+ menuentry "iPXE - ${name}" {
+ linux16 @bootRoot@/ipxe.lkrn
+ initrd16 @bootRoot@/${name}.ipxe
+ }
+
+ '';
+
+ scriptFile = name:
+ let
+ value = builtins.getAttr name config.boot.loader.grub.ipxe;
+ in
+ if builtins.typeOf value == "path" then value
+ else builtins.toFile "${name}.ipxe" value;
+in
+{
+ options =
+ { boot.loader.grub.ipxe = mkOption {
+ type = types.attrsOf (types.either types.path types.str);
+ description =
+ ''
+ Set of iPXE scripts available for
+ booting from the GRUB boot menu.
+ '';
+ default = { };
+ example = literalExample ''
+ { demo = '''
+ #!ipxe
+ dhcp
+ chain http://boot.ipxe.org/demo/boot.php
+ ''';
+ }
+ '';
+ };
+ };
+
+ config = mkIf (builtins.length scripts != 0) {
+
+ boot.loader.grub.extraEntries =
+ if config.boot.loader.grub.version == 2 then
+ toString (map grubEntry scripts)
+ else
+ throw "iPXE is not supported with GRUB 1.";
+
+ boot.loader.grub.extraFiles =
+ { "ipxe.lkrn" = "${pkgs.ipxe}/ipxe.lkrn"; }
+ //
+ builtins.listToAttrs ( map
+ (name: { name = name+".ipxe"; value = scriptFile name; })
+ scripts
+ );
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/memtest.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/memtest.nix
new file mode 100644
index 000000000000..71e50dd0577e
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/grub/memtest.nix
@@ -0,0 +1,116 @@
+# This module adds Memtest86+/Memtest86 to the GRUB boot menu.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ memtest86 = pkgs.memtest86plus;
+ efiSupport = config.boot.loader.grub.efiSupport;
+ cfg = config.boot.loader.grub.memtest86;
+in
+
+{
+ options = {
+
+ boot.loader.grub.memtest86 = {
+
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Make Memtest86+ (or MemTest86 if EFI support is enabled),
+ a memory testing program, available from the
+ GRUB boot menu. MemTest86 is an unfree program, so
+ this requires <literal>allowUnfree</literal> to be set to
+ <literal>true</literal>.
+ '';
+ };
+
+ params = mkOption {
+ default = [];
+ example = [ "console=ttyS0,115200" ];
+ type = types.listOf types.str;
+ description = ''
+ Parameters added to the Memtest86+ command line. As of memtest86+ 5.01
+ the following list of (apparently undocumented) parameters are
+ accepted:
+
+ <itemizedlist>
+
+ <listitem>
+ <para><literal>console=...</literal>, set up a serial console.
+ Examples:
+ <literal>console=ttyS0</literal>,
+ <literal>console=ttyS0,9600</literal> or
+ <literal>console=ttyS0,115200n8</literal>.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>btrace</literal>, enable boot trace.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>maxcpus=N</literal>, limit number of CPUs.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>onepass</literal>, run one pass and exit if there
+ are no errors.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>tstlist=...</literal>, list of tests to run.
+ Example: <literal>0,1,2</literal>.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>cpumask=...</literal>, set a CPU mask, to select CPUs
+ to use for testing.</para>
+ </listitem>
+
+ </itemizedlist>
+
+ This list of command line options was obtained by reading the
+ Memtest86+ source code.
+ '';
+ };
+
+ };
+ };
+
+ config = mkMerge [
+ (mkIf (cfg.enable && efiSupport) {
+ assertions = [
+ {
+ assertion = cfg.params == [];
+ message = "Parameters are not available for MemTest86";
+ }
+ ];
+
+ boot.loader.grub.extraFiles = {
+ "memtest86.efi" = "${pkgs.memtest86-efi}/BOOTX64.efi";
+ };
+
+ boot.loader.grub.extraEntries = ''
+ menuentry "Memtest86" {
+ chainloader /memtest86.efi
+ }
+ '';
+ })
+
+ (mkIf (cfg.enable && !efiSupport) {
+ boot.loader.grub.extraEntries =
+ if config.boot.loader.grub.version == 2 then
+ ''
+ menuentry "Memtest86+" {
+ linux16 @bootRoot@/memtest.bin ${toString cfg.params}
+ }
+ ''
+ else
+ throw "Memtest86+ is not supported with GRUB 1.";
+
+ boot.loader.grub.extraFiles."memtest.bin" = "${memtest86}/memtest.bin";
+ })
+ ];
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/init-script/init-script-builder.sh b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/init-script/init-script-builder.sh
new file mode 100644
index 000000000000..2a1ec479fea0
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/init-script/init-script-builder.sh
@@ -0,0 +1,93 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+if test $# -ne 1; then
+ echo "Usage: init-script-builder.sh DEFAULT-CONFIG"
+ exit 1
+fi
+
+defaultConfig="$1"
+
+
+[ "$(stat -f -c '%i' /)" = "$(stat -f -c '%i' /boot)" ] || {
+ # see grub-menu-builder.sh
+ echo "WARNING: /boot being on a different filesystem not supported by init-script-builder.sh"
+}
+
+
+
+target="/sbin/init"
+targetOther="/boot/init-other-configurations-contents.txt"
+
+tmp="$target.tmp"
+tmpOther="$targetOther.tmp"
+
+
+configurationCounter=0
+numAlienEntries=`cat <<EOF | egrep '^[[:space:]]*title' | wc -l
+@extraEntries@
+EOF`
+
+
+
+
+# Add an entry to $targetOther
+addEntry() {
+ local name="$1"
+ local path="$2"
+ local shortSuffix="$3"
+
+ configurationCounter=$((configurationCounter + 1))
+
+ local stage2=$path/init
+
+ content="$(
+ echo "#!/bin/sh"
+ echo "# $name"
+ echo "# created by init-script-builder.sh"
+ echo "export systemConfig=$(readlink -f $path)"
+ echo "exec $stage2"
+ )"
+
+ [ "$path" != "$defaultConfig" ] || {
+ echo "$content" > $tmp
+ echo "# older configurations: $targetOther" >> $tmp
+ chmod +x $tmp
+ }
+
+ echo -e "$content\n\n" >> $tmpOther
+}
+
+
+mkdir -p /boot /sbin
+
+addEntry "NixOS - Default" $defaultConfig ""
+
+# Add all generations of the system profile to the menu, in reverse
+# (most recent to least recent) order.
+for link in $((ls -d $defaultConfig/specialisation/* ) | sort -n); do
+ date=$(stat --printf="%y\n" $link | sed 's/\..*//')
+ addEntry "NixOS - variation" $link ""
+done
+
+for generation in $(
+ (cd /nix/var/nix/profiles && ls -d system-*-link) \
+ | sed 's/system-\([0-9]\+\)-link/\1/' \
+ | sort -n -r); do
+ link=/nix/var/nix/profiles/system-$generation-link
+ date=$(stat --printf="%y\n" $link | sed 's/\..*//')
+ if [ -d $link/kernel ]; then
+ kernelVersion=$(cd $(dirname $(readlink -f $link/kernel))/lib/modules && echo *)
+ suffix="($date - $kernelVersion)"
+ else
+ suffix="($date)"
+ fi
+ addEntry "NixOS - Configuration $generation $suffix" $link "$generation ($date)"
+done
+
+mv $tmpOther $targetOther
+mv $tmp $target
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/init-script/init-script.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/init-script/init-script.nix
new file mode 100644
index 000000000000..374d9524ff1e
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/init-script/init-script.nix
@@ -0,0 +1,51 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ initScriptBuilder = pkgs.substituteAll {
+ src = ./init-script-builder.sh;
+ isExecutable = true;
+ inherit (pkgs) bash;
+ path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+ };
+
+in
+
+{
+
+ ###### interface
+
+ options = {
+
+ boot.loader.initScript = {
+
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Some systems require a /sbin/init script which is started.
+ Or having it makes starting NixOS easier.
+ This applies to some kind of hosting services and user mode linux.
+
+ Additionally this script will create
+ /boot/init-other-configurations-contents.txt containing
+ contents of remaining configurations. You can copy paste them into
+ /sbin/init manually running a rescue system or such.
+ '';
+ };
+ };
+
+ };
+
+
+ ###### implementation
+
+ config = mkIf config.boot.loader.initScript.enable {
+
+ system.build.installBootLoader = initScriptBuilder;
+
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/loader.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/loader.nix
new file mode 100644
index 000000000000..01475f79b9c2
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/loader.nix
@@ -0,0 +1,20 @@
+{ lib, ... }:
+
+with lib;
+
+{
+ imports = [
+ (mkRenamedOptionModule [ "boot" "loader" "grub" "timeout" ] [ "boot" "loader" "timeout" ])
+ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "timeout" ] [ "boot" "loader" "timeout" ])
+ ];
+
+ options = {
+ boot.loader.timeout = mkOption {
+ default = 5;
+ type = types.nullOr types.int;
+ description = ''
+ Timeout (in seconds) until loader boots the default menu item. Use null if the loader menu should be displayed indefinitely.
+ '';
+ };
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.nix
new file mode 100644
index 000000000000..7eb52e3d021f
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.nix
@@ -0,0 +1,10 @@
+{ pkgs, configTxt }:
+
+pkgs.substituteAll {
+ src = ./raspberrypi-builder.sh;
+ isExecutable = true;
+ inherit (pkgs) bash;
+ path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+ firmware = pkgs.raspberrypifw;
+ inherit configTxt;
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh
new file mode 100644
index 000000000000..0541ca1ba622
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh
@@ -0,0 +1,143 @@
+#! @bash@/bin/sh
+
+# This can end up being called disregarding the shebang.
+set -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+usage() {
+ echo "usage: $0 -c <path-to-default-configuration> [-d <boot-dir>]" >&2
+ exit 1
+}
+
+default= # Default configuration
+target=/boot # Target directory
+
+while getopts "c:d:" opt; do
+ case "$opt" in
+ c) default="$OPTARG" ;;
+ d) target="$OPTARG" ;;
+ \?) usage ;;
+ esac
+done
+
+echo "updating the boot generations directory..."
+
+mkdir -p $target/old
+
+# Convert a path to a file in the Nix store such as
+# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
+cleanName() {
+ local path="$1"
+ echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g'
+}
+
+# Copy a file from the Nix store to $target/kernels.
+declare -A filesCopied
+
+copyToKernelsDir() {
+ local src="$1"
+ local dst="$target/old/$(cleanName $src)"
+ # Don't copy the file if $dst already exists. This means that we
+ # have to create $dst atomically to prevent partially copied
+ # kernels or initrd if this script is ever interrupted.
+ if ! test -e $dst; then
+ local dstTmp=$dst.tmp.$$
+ cp $src $dstTmp
+ mv $dstTmp $dst
+ fi
+ filesCopied[$dst]=1
+ result=$dst
+}
+
+copyForced() {
+ local src="$1"
+ local dst="$2"
+ cp $src $dst.tmp
+ mv $dst.tmp $dst
+}
+
+outdir=$target/old
+mkdir -p $outdir || true
+
+# Copy its kernel and initrd to $target/old.
+addEntry() {
+ local path="$1"
+ local generation="$2"
+
+ if ! test -e $path/kernel -a -e $path/initrd; then
+ return
+ fi
+
+ local kernel=$(readlink -f $path/kernel)
+ local initrd=$(readlink -f $path/initrd)
+ local dtb_path=$(readlink -f $path/dtbs)
+
+ if test -n "@copyKernels@"; then
+ copyToKernelsDir $kernel; kernel=$result
+ copyToKernelsDir $initrd; initrd=$result
+ fi
+
+ echo $(readlink -f $path) > $outdir/$generation-system
+ echo $(readlink -f $path/init) > $outdir/$generation-init
+ cp $path/kernel-params $outdir/$generation-cmdline.txt
+ echo $initrd > $outdir/$generation-initrd
+ echo $kernel > $outdir/$generation-kernel
+
+ if test "$generation" = "default"; then
+ copyForced $kernel $target/kernel.img
+ copyForced $initrd $target/initrd
+ for dtb in $dtb_path/{broadcom,}/bcm*.dtb; do
+ dst="$target/$(basename $dtb)"
+ copyForced $dtb "$dst"
+ filesCopied[$dst]=1
+ done
+ cp "$(readlink -f "$path/init")" $target/nixos-init
+ echo "`cat $path/kernel-params` init=$path/init" >$target/cmdline.txt
+ fi
+}
+
+addEntry $default default
+
+# Add all generations of the system profile to the menu, in reverse
+# (most recent to least recent) order.
+for generation in $(
+ (cd /nix/var/nix/profiles && ls -d system-*-link) \
+ | sed 's/system-\([0-9]\+\)-link/\1/' \
+ | sort -n -r); do
+ link=/nix/var/nix/profiles/system-$generation-link
+ addEntry $link $generation
+done
+
+# Add the firmware files
+fwdir=@firmware@/share/raspberrypi/boot/
+copyForced $fwdir/bootcode.bin $target/bootcode.bin
+copyForced $fwdir/fixup.dat $target/fixup.dat
+copyForced $fwdir/fixup4.dat $target/fixup4.dat
+copyForced $fwdir/fixup4cd.dat $target/fixup4cd.dat
+copyForced $fwdir/fixup4db.dat $target/fixup4db.dat
+copyForced $fwdir/fixup4x.dat $target/fixup4x.dat
+copyForced $fwdir/fixup_cd.dat $target/fixup_cd.dat
+copyForced $fwdir/fixup_db.dat $target/fixup_db.dat
+copyForced $fwdir/fixup_x.dat $target/fixup_x.dat
+copyForced $fwdir/start.elf $target/start.elf
+copyForced $fwdir/start4.elf $target/start4.elf
+copyForced $fwdir/start4cd.elf $target/start4cd.elf
+copyForced $fwdir/start4db.elf $target/start4db.elf
+copyForced $fwdir/start4x.elf $target/start4x.elf
+copyForced $fwdir/start_cd.elf $target/start_cd.elf
+copyForced $fwdir/start_db.elf $target/start_db.elf
+copyForced $fwdir/start_x.elf $target/start_x.elf
+
+# Add the config.txt
+copyForced @configTxt@ $target/config.txt
+
+# Remove obsolete files from $target and $target/old.
+for fn in $target/old/*linux* $target/old/*initrd-initrd* $target/bcm*.dtb; do
+ if ! test "${filesCopied[$fn]}" = 1; then
+ rm -vf -- "$fn"
+ fi
+done
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
new file mode 100644
index 000000000000..337afe9ef628
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
@@ -0,0 +1,108 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.boot.loader.raspberryPi;
+
+ inherit (pkgs.stdenv.hostPlatform) platform;
+
+ builderUboot = import ./uboot-builder.nix { inherit pkgs configTxt; inherit (cfg) version; };
+ builderGeneric = import ./raspberrypi-builder.nix { inherit pkgs configTxt; };
+
+ builder =
+ if cfg.uboot.enable then
+ "${builderUboot} -g ${toString cfg.uboot.configurationLimit} -t ${timeoutStr} -c"
+ else
+ "${builderGeneric} -c";
+
+ blCfg = config.boot.loader;
+ timeoutStr = if blCfg.timeout == null then "-1" else toString blCfg.timeout;
+
+ isAarch64 = pkgs.stdenv.hostPlatform.isAarch64;
+ optional = pkgs.stdenv.lib.optionalString;
+
+ configTxt =
+ pkgs.writeText "config.txt" (''
+ # U-Boot used to need this to work, regardless of whether UART is actually used or not.
+ # TODO: check when/if this can be removed.
+ enable_uart=1
+
+ # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
+ # when attempting to show low-voltage or overtemperature warnings.
+ avoid_warnings=1
+ '' + optional isAarch64 ''
+ # Boot in 64-bit mode.
+ arm_64bit=1
+ '' + (if cfg.uboot.enable then ''
+ kernel=u-boot-rpi.bin
+ '' else ''
+ kernel=kernel.img
+ initramfs initrd followkernel
+ '') + optional (cfg.firmwareConfig != null) cfg.firmwareConfig);
+
+in
+
+{
+ options = {
+
+ boot.loader.raspberryPi = {
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to create files with the system generations in
+ <literal>/boot</literal>.
+ <literal>/boot/old</literal> will hold files from old generations.
+ '';
+ };
+
+ version = mkOption {
+ default = 2;
+ type = types.enum [ 0 1 2 3 4 ];
+ description = ''
+ '';
+ };
+
+ uboot = {
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Enable using uboot as bootmanager for the raspberry pi.
+ '';
+ };
+
+ configurationLimit = mkOption {
+ default = 20;
+ example = 10;
+ type = types.int;
+ description = ''
+ Maximum number of configurations in the boot menu.
+ '';
+ };
+
+ };
+
+ firmwareConfig = mkOption {
+ default = null;
+ type = types.nullOr types.lines;
+ description = ''
+ Extra options that will be appended to <literal>/boot/config.txt</literal> file.
+ For possible values, see: https://www.raspberrypi.org/documentation/configuration/config-txt/
+ '';
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ assertions = singleton {
+ assertion = !pkgs.stdenv.hostPlatform.isAarch64 || cfg.version >= 3;
+ message = "Only Raspberry Pi >= 3 supports aarch64.";
+ };
+
+ system.build.installBootLoader = builder;
+ system.boot.loader.id = "raspberrypi";
+ system.boot.loader.kernelFile = platform.kernelTarget;
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix
new file mode 100644
index 000000000000..a4352ab9a240
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix
@@ -0,0 +1,37 @@
+{ pkgs, version, configTxt }:
+
+let
+ isAarch64 = pkgs.stdenv.hostPlatform.isAarch64;
+
+ uboot =
+ if version == 0 then
+ pkgs.ubootRaspberryPiZero
+ else if version == 1 then
+ pkgs.ubootRaspberryPi
+ else if version == 2 then
+ pkgs.ubootRaspberryPi2
+ else if version == 3 then
+ if isAarch64 then
+ pkgs.ubootRaspberryPi3_64bit
+ else
+ pkgs.ubootRaspberryPi3_32bit
+ else
+ throw "U-Boot is not yet supported on the raspberry pi 4.";
+
+ extlinuxConfBuilder =
+ import ../generic-extlinux-compatible/extlinux-conf-builder.nix {
+ pkgs = pkgs.buildPackages;
+ };
+in
+pkgs.substituteAll {
+ src = ./uboot-builder.sh;
+ isExecutable = true;
+ inherit (pkgs) bash;
+ path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+ firmware = pkgs.raspberrypifw;
+ inherit uboot;
+ inherit configTxt;
+ inherit extlinuxConfBuilder;
+ inherit version;
+}
+
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.sh b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.sh
new file mode 100644
index 000000000000..ea591427179f
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.sh
@@ -0,0 +1,38 @@
+#! @bash@/bin/sh -e
+
+target=/boot # Target directory
+
+while getopts "t:c:d:g:" opt; do
+ case "$opt" in
+ d) target="$OPTARG" ;;
+ *) ;;
+ esac
+done
+
+copyForced() {
+ local src="$1"
+ local dst="$2"
+ cp $src $dst.tmp
+ mv $dst.tmp $dst
+}
+
+# Call the extlinux builder
+"@extlinuxConfBuilder@" "$@"
+
+# Add the firmware files
+fwdir=@firmware@/share/raspberrypi/boot/
+copyForced $fwdir/bootcode.bin $target/bootcode.bin
+copyForced $fwdir/fixup.dat $target/fixup.dat
+copyForced $fwdir/fixup_cd.dat $target/fixup_cd.dat
+copyForced $fwdir/fixup_db.dat $target/fixup_db.dat
+copyForced $fwdir/fixup_x.dat $target/fixup_x.dat
+copyForced $fwdir/start.elf $target/start.elf
+copyForced $fwdir/start_cd.elf $target/start_cd.elf
+copyForced $fwdir/start_db.elf $target/start_db.elf
+copyForced $fwdir/start_x.elf $target/start_x.elf
+
+# Add the uboot file
+copyForced @uboot@/u-boot.bin $target/u-boot-rpi.bin
+
+# Add the config.txt
+copyForced @configTxt@ $target/config.txt
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
new file mode 100644
index 000000000000..97e824fe629c
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -0,0 +1,256 @@
+#! @python3@/bin/python3 -B
+import argparse
+import shutil
+import os
+import sys
+import errno
+import subprocess
+import glob
+import tempfile
+import errno
+import warnings
+import ctypes
+libc = ctypes.CDLL("libc.so.6")
+import re
+import datetime
+import glob
+import os.path
+
+def copy_if_not_exists(source, dest):
+ if not os.path.exists(dest):
+ shutil.copyfile(source, dest)
+
+def system_dir(profile, generation):
+ if profile:
+ return "/nix/var/nix/profiles/system-profiles/%s-%d-link" % (profile, generation)
+ else:
+ return "/nix/var/nix/profiles/system-%d-link" % (generation)
+
+BOOT_ENTRY = """title NixOS{profile}
+version Generation {generation} {description}
+linux {kernel}
+initrd {initrd}
+options {kernel_params}
+"""
+
+# The boot loader entry for memtest86.
+#
+# TODO: This is hard-coded to use the 64-bit EFI app, but it could probably
+# be updated to use the 32-bit EFI app on 32-bit systems. The 32-bit EFI
+# app filename is BOOTIA32.efi.
+MEMTEST_BOOT_ENTRY = """title MemTest86
+efi /efi/memtest86/BOOTX64.efi
+"""
+
+def write_loader_conf(profile, generation):
+ with open("@efiSysMountPoint@/loader/loader.conf.tmp", 'w') as f:
+ if "@timeout@" != "":
+ f.write("timeout @timeout@\n")
+ if profile:
+ f.write("default nixos-%s-generation-%d.conf\n" % (profile, generation))
+ else:
+ f.write("default nixos-generation-%d.conf\n" % (generation))
+ if not @editor@:
+ f.write("editor 0\n");
+ f.write("console-mode @consoleMode@\n");
+ os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
+
+def profile_path(profile, generation, name):
+ return os.readlink("%s/%s" % (system_dir(profile, generation), name))
+
+def copy_from_profile(profile, generation, name, dry_run=False):
+ store_file_path = profile_path(profile, generation, name)
+ suffix = os.path.basename(store_file_path)
+ store_dir = os.path.basename(os.path.dirname(store_file_path))
+ efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
+ if not dry_run:
+ copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
+ return efi_file_path
+
+def describe_generation(generation_dir):
+ try:
+ with open("%s/nixos-version" % generation_dir) as f:
+ nixos_version = f.read()
+ except IOError:
+ nixos_version = "Unknown"
+
+ kernel_dir = os.path.dirname(os.path.realpath("%s/kernel" % generation_dir))
+ module_dir = glob.glob("%s/lib/modules/*" % kernel_dir)[0]
+ kernel_version = os.path.basename(module_dir)
+
+ build_time = int(os.path.getctime(generation_dir))
+ build_date = datetime.datetime.fromtimestamp(build_time).strftime('%F')
+
+ description = "NixOS {}, Linux Kernel {}, Built on {}".format(
+ nixos_version, kernel_version, build_date
+ )
+
+ return description
+
+def write_entry(profile, generation, machine_id):
+ kernel = copy_from_profile(profile, generation, "kernel")
+ initrd = copy_from_profile(profile, generation, "initrd")
+ try:
+ append_initrd_secrets = profile_path(profile, generation, "append-initrd-secrets")
+ subprocess.check_call([append_initrd_secrets, "@efiSysMountPoint@%s" % (initrd)])
+ except FileNotFoundError:
+ pass
+ if profile:
+ entry_file = "@efiSysMountPoint@/loader/entries/nixos-%s-generation-%d.conf" % (profile, generation)
+ else:
+ entry_file = "@efiSysMountPoint@/loader/entries/nixos-generation-%d.conf" % (generation)
+ generation_dir = os.readlink(system_dir(profile, generation))
+ tmp_path = "%s.tmp" % (entry_file)
+ kernel_params = "systemConfig=%s init=%s/init " % (generation_dir, generation_dir)
+
+ with open("%s/kernel-params" % (generation_dir)) as params_file:
+ kernel_params = kernel_params + params_file.read()
+ with open(tmp_path, 'w') as f:
+ f.write(BOOT_ENTRY.format(profile=" [" + profile + "]" if profile else "",
+ generation=generation,
+ kernel=kernel,
+ initrd=initrd,
+ kernel_params=kernel_params,
+ description=describe_generation(generation_dir)))
+ if machine_id is not None:
+ f.write("machine-id %s\n" % machine_id)
+ os.rename(tmp_path, entry_file)
+
+def mkdir_p(path):
+ try:
+ os.makedirs(path)
+ except OSError as e:
+ if e.errno != errno.EEXIST or not os.path.isdir(path):
+ raise
+
+def get_generations(profile=None):
+ gen_list = subprocess.check_output([
+ "@nix@/bin/nix-env",
+ "--list-generations",
+ "-p",
+ "/nix/var/nix/profiles/%s" % ("system-profiles/" + profile if profile else "system"),
+ "--option", "build-users-group", ""],
+ universal_newlines=True)
+ gen_lines = gen_list.split('\n')
+ gen_lines.pop()
+
+ configurationLimit = @configurationLimit@
+ return [ (profile, int(line.split()[0])) for line in gen_lines ][-configurationLimit:]
+
+def remove_old_entries(gens):
+ rex_profile = re.compile("^@efiSysMountPoint@/loader/entries/nixos-(.*)-generation-.*\.conf$")
+ rex_generation = re.compile("^@efiSysMountPoint@/loader/entries/nixos.*-generation-(.*)\.conf$")
+ known_paths = []
+ for gen in gens:
+ known_paths.append(copy_from_profile(*gen, "kernel", True))
+ known_paths.append(copy_from_profile(*gen, "initrd", True))
+ for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos*-generation-[1-9]*.conf"):
+ try:
+ if rex_profile.match(path):
+ prof = rex_profile.sub(r"\1", path)
+ else:
+ prof = "system"
+ gen = int(rex_generation.sub(r"\1", path))
+ if not (prof, gen) in gens:
+ os.unlink(path)
+ except ValueError:
+ pass
+ for path in glob.iglob("@efiSysMountPoint@/efi/nixos/*"):
+ if not path in known_paths and not os.path.isdir(path):
+ os.unlink(path)
+
+def get_profiles():
+ if os.path.isdir("/nix/var/nix/profiles/system-profiles/"):
+ return [x
+ for x in os.listdir("/nix/var/nix/profiles/system-profiles/")
+ if not x.endswith("-link")]
+ else:
+ return []
+
+def main():
+ parser = argparse.ArgumentParser(description='Update NixOS-related systemd-boot files')
+ parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default NixOS config to boot')
+ args = parser.parse_args()
+
+ try:
+ with open("/etc/machine-id") as machine_file:
+ machine_id = machine_file.readlines()[0]
+ except IOError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ # Since systemd version 232 a machine ID is required and it might not
+ # 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.
+ cmd = ["@systemd@/bin/systemd-machine-id-setup", "--print"]
+ machine_id = subprocess.check_output(cmd).rstrip()
+
+ if os.getenv("NIXOS_INSTALL_GRUB") == "1":
+ warnings.warn("NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER", DeprecationWarning)
+ os.environ["NIXOS_INSTALL_BOOTLOADER"] = "1"
+
+ if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1":
+ # bootctl uses fopen() with modes "wxe" and fails if the file exists.
+ if os.path.exists("@efiSysMountPoint@/loader/loader.conf"):
+ os.unlink("@efiSysMountPoint@/loader/loader.conf")
+
+ if "@canTouchEfiVariables@" == "1":
+ subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "install"])
+ else:
+ subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "--no-variables", "install"])
+ else:
+ # Update bootloader to latest if needed
+ systemd_version = subprocess.check_output(["@systemd@/bin/bootctl", "--version"], universal_newlines=True).split()[1]
+ sdboot_status = subprocess.check_output(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "status"], universal_newlines=True)
+
+ # See status_binaries() in systemd bootctl.c for code which generates this
+ m = re.search("^\W+File:.*/EFI/(BOOT|systemd)/.*\.efi \(systemd-boot (\d+)\)$",
+ sdboot_status, re.IGNORECASE | re.MULTILINE)
+ if m is None:
+ print("could not find any previously installed systemd-boot")
+ else:
+ sdboot_version = m.group(2)
+ if systemd_version > sdboot_version:
+ print("updating systemd-boot from %s to %s" % (sdboot_version, systemd_version))
+ subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "update"])
+
+
+ mkdir_p("@efiSysMountPoint@/efi/nixos")
+ mkdir_p("@efiSysMountPoint@/loader/entries")
+
+ gens = get_generations()
+ for profile in get_profiles():
+ gens += get_generations(profile)
+ remove_old_entries(gens)
+ for gen in gens:
+ write_entry(*gen, machine_id)
+ if os.readlink(system_dir(*gen)) == args.default_config:
+ write_loader_conf(*gen)
+
+ memtest_entry_file = "@efiSysMountPoint@/loader/entries/memtest86.conf"
+ if os.path.exists(memtest_entry_file):
+ os.unlink(memtest_entry_file)
+ shutil.rmtree("@efiSysMountPoint@/efi/memtest86", ignore_errors=True)
+ if "@memtest86@" != "":
+ mkdir_p("@efiSysMountPoint@/efi/memtest86")
+ for path in glob.iglob("@memtest86@/*"):
+ if os.path.isdir(path):
+ shutil.copytree(path, os.path.join("@efiSysMountPoint@/efi/memtest86", os.path.basename(path)))
+ else:
+ shutil.copy(path, "@efiSysMountPoint@/efi/memtest86/")
+
+ memtest_entry_file = "@efiSysMountPoint@/loader/entries/memtest86.conf"
+ memtest_entry_file_tmp_path = "%s.tmp" % memtest_entry_file
+ with open(memtest_entry_file_tmp_path, 'w') as f:
+ f.write(MEMTEST_BOOT_ENTRY)
+ os.rename(memtest_entry_file_tmp_path, memtest_entry_file)
+
+ # Since fat32 provides little recovery facilities after a crash,
+ # 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("@efiSysMountPoint@", os.O_RDONLY))
+ if rc != 0:
+ print("could not sync @efiSysMountPoint@: {}".format(os.strerror(rc)), file=sys.stderr)
+
+if __name__ == '__main__':
+ main()
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
new file mode 100644
index 000000000000..f0bd76a3c1d2
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -0,0 +1,143 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.boot.loader.systemd-boot;
+
+ efi = config.boot.loader.efi;
+
+ gummibootBuilder = pkgs.substituteAll {
+ src = ./systemd-boot-builder.py;
+
+ isExecutable = true;
+
+ inherit (pkgs) python3;
+
+ systemd = config.systemd.package;
+
+ nix = config.nix.package.out;
+
+ timeout = if config.boot.loader.timeout != null then config.boot.loader.timeout else "";
+
+ editor = if cfg.editor then "True" else "False";
+
+ configurationLimit = if cfg.configurationLimit == null then 0 else cfg.configurationLimit;
+
+ inherit (cfg) consoleMode;
+
+ inherit (efi) efiSysMountPoint canTouchEfiVariables;
+
+ memtest86 = if cfg.memtest86.enable then pkgs.memtest86-efi else "";
+ };
+in {
+
+ imports =
+ [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ])
+ ];
+
+ options.boot.loader.systemd-boot = {
+ enable = mkOption {
+ default = false;
+
+ type = types.bool;
+
+ description = "Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager";
+ };
+
+ editor = mkOption {
+ default = true;
+
+ type = types.bool;
+
+ description = ''
+ Whether to allow editing the kernel command-line before
+ boot. It is recommended to set this to false, as it allows
+ gaining root access by passing init=/bin/sh as a kernel
+ parameter. However, it is enabled by default for backwards
+ compatibility.
+ '';
+ };
+
+ configurationLimit = mkOption {
+ default = null;
+ example = 120;
+ type = types.nullOr types.int;
+ description = ''
+ Maximum number of latest generations in the boot menu.
+ Useful to prevent boot partition running out of disk space.
+
+ <literal>null</literal> means no limit i.e. all generations
+ that were not garbage collected yet.
+ '';
+ };
+
+ consoleMode = mkOption {
+ default = "keep";
+
+ type = types.enum [ "0" "1" "2" "auto" "max" "keep" ];
+
+ description = ''
+ The resolution of the console. The following values are valid:
+
+ <itemizedlist>
+ <listitem><para>
+ <literal>"0"</literal>: Standard UEFI 80x25 mode
+ </para></listitem>
+ <listitem><para>
+ <literal>"1"</literal>: 80x50 mode, not supported by all devices
+ </para></listitem>
+ <listitem><para>
+ <literal>"2"</literal>: The first non-standard mode provided by the device firmware, if any
+ </para></listitem>
+ <listitem><para>
+ <literal>"auto"</literal>: Pick a suitable mode automatically using heuristics
+ </para></listitem>
+ <listitem><para>
+ <literal>"max"</literal>: Pick the highest-numbered available mode
+ </para></listitem>
+ <listitem><para>
+ <literal>"keep"</literal>: Keep the mode selected by firmware (the default)
+ </para></listitem>
+ </itemizedlist>
+ '';
+ };
+
+ memtest86 = {
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Make MemTest86 available from the systemd-boot menu. MemTest86 is a
+ program for testing memory. MemTest86 is an unfree program, so
+ this requires <literal>allowUnfree</literal> to be set to
+ <literal>true</literal>.
+ '';
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub;
+
+ message = "This kernel does not support the EFI boot stub";
+ }
+ ];
+
+ boot.loader.grub.enable = mkDefault false;
+
+ boot.loader.supportsInitrdSecrets = true;
+
+ system = {
+ build.installBootLoader = gummibootBuilder;
+
+ boot.loader.id = "systemd-boot";
+
+ requiredKernelConfig = with config.lib.kernelConfig; [
+ (isYes "EFI_STUB")
+ ];
+ };
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/luksroot.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/luksroot.nix
new file mode 100644
index 000000000000..88190e8200b1
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/luksroot.nix
@@ -0,0 +1,897 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ luks = config.boot.initrd.luks;
+ kernelPackages = config.boot.kernelPackages;
+
+ commonFunctions = ''
+ die() {
+ echo "$@" >&2
+ exit 1
+ }
+
+ dev_exist() {
+ local target="$1"
+ if [ -e $target ]; then
+ return 0
+ else
+ local uuid=$(echo -n $target | sed -e 's,UUID=\(.*\),\1,g')
+ blkid --uuid $uuid >/dev/null
+ return $?
+ fi
+ }
+
+ wait_target() {
+ local name="$1"
+ local target="$2"
+ local secs="''${3:-10}"
+ local desc="''${4:-$name $target to appear}"
+
+ if ! dev_exist $target; then
+ echo -n "Waiting $secs seconds for $desc..."
+ local success=false;
+ for try in $(seq $secs); do
+ echo -n "."
+ sleep 1
+ if dev_exist $target; then
+ success=true
+ break
+ fi
+ done
+ if [ $success == true ]; then
+ echo " - success";
+ return 0
+ else
+ echo " - failure";
+ return 1
+ fi
+ fi
+ return 0
+ }
+
+ wait_yubikey() {
+ local secs="''${1:-10}"
+
+ ykinfo -v 1>/dev/null 2>&1
+ if [ $? != 0 ]; then
+ echo -n "Waiting $secs seconds for Yubikey to appear..."
+ local success=false
+ for try in $(seq $secs); do
+ echo -n .
+ sleep 1
+ ykinfo -v 1>/dev/null 2>&1
+ if [ $? == 0 ]; then
+ success=true
+ break
+ fi
+ done
+ if [ $success == true ]; then
+ echo " - success";
+ return 0
+ else
+ echo " - failure";
+ return 1
+ fi
+ fi
+ return 0
+ }
+
+ wait_gpgcard() {
+ local secs="''${1:-10}"
+
+ gpg --card-status > /dev/null 2> /dev/null
+ if [ $? != 0 ]; then
+ echo -n "Waiting $secs seconds for GPG Card to appear"
+ local success=false
+ for try in $(seq $secs); do
+ echo -n .
+ sleep 1
+ gpg --card-status > /dev/null 2> /dev/null
+ if [ $? == 0 ]; then
+ success=true
+ break
+ fi
+ done
+ if [ $success == true ]; then
+ echo " - success";
+ return 0
+ else
+ echo " - failure";
+ return 1
+ fi
+ fi
+ return 0
+ }
+ '';
+
+ preCommands = ''
+ # A place to store crypto things
+
+ # A ramfs is used here to ensure that the file used to update
+ # the key slot with cryptsetup will never get swapped out.
+ # Warning: Do NOT replace with tmpfs!
+ mkdir -p /crypt-ramfs
+ mount -t ramfs none /crypt-ramfs
+
+ # Cryptsetup locking directory
+ mkdir -p /run/cryptsetup
+
+ # For Yubikey salt storage
+ mkdir -p /crypt-storage
+
+ ${optionalString luks.gpgSupport ''
+ export GPG_TTY=$(tty)
+ export GNUPGHOME=/crypt-ramfs/.gnupg
+
+ gpg-agent --daemon --scdaemon-program $out/bin/scdaemon > /dev/null 2> /dev/null
+ ''}
+
+ # Disable all input echo for the whole stage. We could use read -s
+ # instead but that would ocasionally leak characters between read
+ # invocations.
+ stty -echo
+ '';
+
+ postCommands = ''
+ stty echo
+ umount /crypt-storage 2>/dev/null
+ umount /crypt-ramfs 2>/dev/null
+ '';
+
+ openCommand = name': { name, device, header, keyFile, keyFileSize, keyFileOffset, allowDiscards, yubikey, gpgCard, fido2, fallbackToPassword, preOpenCommands, postOpenCommands,... }: assert name' == name;
+ let
+ csopen = "cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} ${optionalString (header != null) "--header=${header}"}";
+ cschange = "cryptsetup luksChangeKey ${device} ${optionalString (header != null) "--header=${header}"}";
+ in ''
+ # Wait for luksRoot (and optionally keyFile and/or header) to appear, e.g.
+ # if on a USB drive.
+ wait_target "device" ${device} || die "${device} is unavailable"
+
+ ${optionalString (header != null) ''
+ wait_target "header" ${header} || die "${header} is unavailable"
+ ''}
+
+ do_open_passphrase() {
+ local passphrase
+
+ while true; do
+ echo -n "Passphrase for ${device}: "
+ passphrase=
+ while true; do
+ if [ -e /crypt-ramfs/passphrase ]; then
+ echo "reused"
+ passphrase=$(cat /crypt-ramfs/passphrase)
+ break
+ else
+ # ask cryptsetup-askpass
+ echo -n "${device}" > /crypt-ramfs/device
+
+ # and try reading it from /dev/console with a timeout
+ IFS= read -t 1 -r passphrase
+ if [ -n "$passphrase" ]; then
+ ${if luks.reusePassphrases then ''
+ # remember it for the next device
+ echo -n "$passphrase" > /crypt-ramfs/passphrase
+ '' else ''
+ # Don't save it to ramfs. We are very paranoid
+ ''}
+ echo
+ break
+ fi
+ fi
+ done
+ echo -n "Verifying passphrase for ${device}..."
+ echo -n "$passphrase" | ${csopen} --key-file=-
+ if [ $? == 0 ]; then
+ echo " - success"
+ ${if luks.reusePassphrases then ''
+ # we don't rm here because we might reuse it for the next device
+ '' else ''
+ rm -f /crypt-ramfs/passphrase
+ ''}
+ break
+ else
+ echo " - failure"
+ # ask for a different one
+ rm -f /crypt-ramfs/passphrase
+ fi
+ done
+ }
+
+ # LUKS
+ open_normally() {
+ ${if (keyFile != null) then ''
+ if wait_target "key file" ${keyFile}; then
+ ${csopen} --key-file=${keyFile} \
+ ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"} \
+ ${optionalString (keyFileOffset != null) "--keyfile-offset=${toString keyFileOffset}"}
+ else
+ ${if fallbackToPassword then "echo" else "die"} "${keyFile} is unavailable"
+ echo " - failing back to interactive password prompt"
+ do_open_passphrase
+ fi
+ '' else ''
+ do_open_passphrase
+ ''}
+ }
+
+ ${optionalString (luks.yubikeySupport && (yubikey != null)) ''
+ # Yubikey
+ rbtohex() {
+ ( od -An -vtx1 | tr -d ' \n' )
+ }
+
+ hextorb() {
+ ( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf )
+ }
+
+ do_open_yubikey() {
+ # Make all of these local to this function
+ # to prevent their values being leaked
+ local salt
+ local iterations
+ local k_user
+ local challenge
+ local response
+ local k_luks
+ local opened
+ local new_salt
+ local new_iterations
+ local new_challenge
+ local new_response
+ local new_k_luks
+
+ mount -t ${yubikey.storage.fsType} ${yubikey.storage.device} /crypt-storage || \
+ die "Failed to mount Yubikey salt storage device"
+
+ salt="$(cat /crypt-storage${yubikey.storage.path} | sed -n 1p | tr -d '\n')"
+ iterations="$(cat /crypt-storage${yubikey.storage.path} | sed -n 2p | tr -d '\n')"
+ challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
+ response="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
+
+ for try in $(seq 3); do
+ ${optionalString yubikey.twoFactor ''
+ echo -n "Enter two-factor passphrase: "
+ read -r k_user
+ echo
+ ''}
+
+ if [ ! -z "$k_user" ]; then
+ k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
+ else
+ k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
+ fi
+
+ echo -n "$k_luks" | hextorb | ${csopen} --key-file=-
+
+ if [ $? == 0 ]; then
+ opened=true
+ break
+ else
+ opened=false
+ echo "Authentication failed!"
+ fi
+ done
+
+ [ "$opened" == false ] && die "Maximum authentication errors reached"
+
+ echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..."
+ for i in $(seq ${toString yubikey.saltLength}); do
+ byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)";
+ new_salt="$new_salt$byte";
+ echo -n .
+ done;
+ echo "ok"
+
+ new_iterations="$iterations"
+ ${optionalString (yubikey.iterationStep > 0) ''
+ new_iterations="$(($new_iterations + ${toString yubikey.iterationStep}))"
+ ''}
+
+ new_challenge="$(echo -n $new_salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
+
+ new_response="$(ykchalresp -${toString yubikey.slot} -x $new_challenge 2>/dev/null)"
+
+ if [ ! -z "$k_user" ]; then
+ new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
+ else
+ new_k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
+ fi
+
+ echo -n "$new_k_luks" | hextorb > /crypt-ramfs/new_key
+ echo -n "$k_luks" | hextorb | ${cschange} --key-file=- /crypt-ramfs/new_key
+
+ if [ $? == 0 ]; then
+ echo -ne "$new_salt\n$new_iterations" > /crypt-storage${yubikey.storage.path}
+ else
+ echo "Warning: Could not update LUKS key, current challenge persists!"
+ fi
+
+ rm -f /crypt-ramfs/new_key
+ umount /crypt-storage
+ }
+
+ open_with_hardware() {
+ if wait_yubikey ${toString yubikey.gracePeriod}; then
+ do_open_yubikey
+ else
+ echo "No yubikey found, falling back to non-yubikey open procedure"
+ open_normally
+ fi
+ }
+ ''}
+
+ ${optionalString (luks.gpgSupport && (gpgCard != null)) ''
+
+ do_open_gpg_card() {
+ # Make all of these local to this function
+ # to prevent their values being leaked
+ local pin
+ local opened
+
+ gpg --import /gpg-keys/${device}/pubkey.asc > /dev/null 2> /dev/null
+
+ gpg --card-status > /dev/null 2> /dev/null
+
+ for try in $(seq 3); do
+ echo -n "PIN for GPG Card associated with device ${device}: "
+ pin=
+ while true; do
+ if [ -e /crypt-ramfs/passphrase ]; then
+ echo "reused"
+ pin=$(cat /crypt-ramfs/passphrase)
+ break
+ else
+ # and try reading it from /dev/console with a timeout
+ IFS= read -t 1 -r pin
+ if [ -n "$pin" ]; then
+ ${if luks.reusePassphrases then ''
+ # remember it for the next device
+ echo -n "$pin" > /crypt-ramfs/passphrase
+ '' else ''
+ # Don't save it to ramfs. We are very paranoid
+ ''}
+ echo
+ break
+ fi
+ fi
+ done
+ echo -n "Verifying passphrase for ${device}..."
+ echo -n "$pin" | gpg -q --batch --passphrase-fd 0 --pinentry-mode loopback -d /gpg-keys/${device}/cryptkey.gpg 2> /dev/null | ${csopen} --key-file=- > /dev/null 2> /dev/null
+ if [ $? == 0 ]; then
+ echo " - success"
+ ${if luks.reusePassphrases then ''
+ # we don't rm here because we might reuse it for the next device
+ '' else ''
+ rm -f /crypt-ramfs/passphrase
+ ''}
+ break
+ else
+ echo " - failure"
+ # ask for a different one
+ rm -f /crypt-ramfs/passphrase
+ fi
+ done
+
+ [ "$opened" == false ] && die "Maximum authentication errors reached"
+ }
+
+ open_with_hardware() {
+ if wait_gpgcard ${toString gpgCard.gracePeriod}; then
+ do_open_gpg_card
+ else
+ echo "No GPG Card found, falling back to normal open procedure"
+ open_normally
+ fi
+ }
+ ''}
+
+ ${optionalString (luks.fido2Support && (fido2.credential != null)) ''
+
+ open_with_hardware() {
+ local passsphrase
+
+ ${if fido2.passwordLess then ''
+ export passphrase=""
+ '' else ''
+ read -rsp "FIDO2 salt for ${device}: " passphrase
+ echo
+ ''}
+ ${optionalString (lib.versionOlder kernelPackages.kernel.version "5.4") ''
+ echo "On systems with Linux Kernel < 5.4, it might take a while to initialize the CRNG, you might want to use linuxPackages_latest."
+ echo "Please move your mouse to create needed randomness."
+ ''}
+ echo "Waiting for your FIDO2 device..."
+ fido2luks -i open ${device} ${name} ${fido2.credential} --await-dev ${toString fido2.gracePeriod} --salt string:$passphrase
+ if [ $? -ne 0 ]; then
+ echo "No FIDO2 key found, falling back to normal open procedure"
+ open_normally
+ fi
+ }
+ ''}
+
+ # commands to run right before we mount our device
+ ${preOpenCommands}
+
+ ${if (luks.yubikeySupport && (yubikey != null)) || (luks.gpgSupport && (gpgCard != null)) || (luks.fido2Support && (fido2.credential != null)) then ''
+ open_with_hardware
+ '' else ''
+ open_normally
+ ''}
+
+ # commands to run right after we mounted our device
+ ${postOpenCommands}
+ '';
+
+ askPass = pkgs.writeScriptBin "cryptsetup-askpass" ''
+ #!/bin/sh
+
+ ${commonFunctions}
+
+ while true; do
+ wait_target "luks" /crypt-ramfs/device 10 "LUKS to request a passphrase" || die "Passphrase is not requested now"
+ device=$(cat /crypt-ramfs/device)
+
+ echo -n "Passphrase for $device: "
+ IFS= read -rs passphrase
+ echo
+
+ rm /crypt-ramfs/device
+ echo -n "$passphrase" > /crypt-ramfs/passphrase
+ done
+ '';
+
+ preLVM = filterAttrs (n: v: v.preLVM) luks.devices;
+ postLVM = filterAttrs (n: v: !v.preLVM) luks.devices;
+
+in
+{
+ imports = [
+ (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "")
+ ];
+
+ options = {
+
+ boot.initrd.luks.mitigateDMAAttacks = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Unless enabled, encryption keys can be easily recovered by an attacker with physical
+ access to any machine with PCMCIA, ExpressCard, ThunderBolt or FireWire port.
+ More information is available at <link xlink:href="http://en.wikipedia.org/wiki/DMA_attack"/>.
+
+ This option blacklists FireWire drivers, but doesn't remove them. You can manually
+ load the drivers if you need to use a FireWire device, but don't forget to unload them!
+ '';
+ };
+
+ boot.initrd.luks.cryptoModules = mkOption {
+ type = types.listOf types.str;
+ default =
+ [ "aes" "aes_generic" "blowfish" "twofish"
+ "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512"
+ "af_alg" "algif_skcipher"
+ ];
+ description = ''
+ A list of cryptographic kernel modules needed to decrypt the root device(s).
+ The default includes all common modules.
+ '';
+ };
+
+ boot.initrd.luks.forceLuksSupportInInitrd = mkOption {
+ type = types.bool;
+ default = false;
+ internal = true;
+ description = ''
+ Whether to configure luks support in the initrd, when no luks
+ devices are configured.
+ '';
+ };
+
+ boot.initrd.luks.reusePassphrases = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ When opening a new LUKS device try reusing last successful
+ passphrase.
+
+ Useful for mounting a number of devices that use the same
+ passphrase without retyping it several times.
+
+ Such setup can be useful if you use <command>cryptsetup
+ luksSuspend</command>. Different LUKS devices will still have
+ different master keys even when using the same passphrase.
+ '';
+ };
+
+ boot.initrd.luks.devices = mkOption {
+ default = { };
+ example = { luksroot.device = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08"; };
+ description = ''
+ The encrypted disk that should be opened before the root
+ filesystem is mounted. Both LVM-over-LUKS and LUKS-over-LVM
+ setups are supported. The unencrypted devices can be accessed as
+ <filename>/dev/mapper/<replaceable>name</replaceable></filename>.
+ '';
+
+ type = with types; attrsOf (submodule (
+ { name, ... }: { options = {
+
+ name = mkOption {
+ visible = false;
+ default = name;
+ example = "luksroot";
+ type = types.str;
+ description = "Name of the unencrypted device in <filename>/dev/mapper</filename>.";
+ };
+
+ device = mkOption {
+ example = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08";
+ type = types.str;
+ description = "Path of the underlying encrypted block device.";
+ };
+
+ header = mkOption {
+ default = null;
+ example = "/root/header.img";
+ type = types.nullOr types.str;
+ description = ''
+ The name of the file or block device that
+ should be used as header for the encrypted device.
+ '';
+ };
+
+ keyFile = mkOption {
+ default = null;
+ example = "/dev/sdb1";
+ type = types.nullOr types.str;
+ description = ''
+ The name of the file (can be a raw device or a partition) that
+ should be used as the decryption key for the encrypted device. If
+ not specified, you will be prompted for a passphrase instead.
+ '';
+ };
+
+ keyFileSize = mkOption {
+ default = null;
+ example = 4096;
+ type = types.nullOr types.int;
+ description = ''
+ The size of the key file. Use this if only the beginning of the
+ key file should be used as a key (often the case if a raw device
+ or partition is used as key file). If not specified, the whole
+ <literal>keyFile</literal> will be used decryption, instead of just
+ the first <literal>keyFileSize</literal> bytes.
+ '';
+ };
+
+ keyFileOffset = mkOption {
+ default = null;
+ example = 4096;
+ type = types.nullOr types.int;
+ description = ''
+ The offset of the key file. Use this in combination with
+ <literal>keyFileSize</literal> to use part of a file as key file
+ (often the case if a raw device or partition is used as a key file).
+ If not specified, the key begins at the first byte of
+ <literal>keyFile</literal>.
+ '';
+ };
+
+ # FIXME: get rid of this option.
+ preLVM = mkOption {
+ default = true;
+ type = types.bool;
+ description = "Whether the luksOpen will be attempted before LVM scan or after it.";
+ };
+
+ allowDiscards = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to allow TRIM requests to the underlying device. This option
+ has security implications; please read the LUKS documentation before
+ activating it.
+ '';
+ };
+
+ fallbackToPassword = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to fallback to interactive passphrase prompt if the keyfile
+ cannot be found. This will prevent unattended boot should the keyfile
+ go missing.
+ '';
+ };
+
+ gpgCard = mkOption {
+ default = null;
+ description = ''
+ The option to use this LUKS device with a GPG encrypted luks password by the GPG Smartcard.
+ If null (the default), GPG-Smartcard will be disabled for this device.
+ '';
+
+ type = with types; nullOr (submodule {
+ options = {
+ gracePeriod = mkOption {
+ default = 10;
+ type = types.int;
+ description = "Time in seconds to wait for the GPG Smartcard.";
+ };
+
+ encryptedPass = mkOption {
+ default = "";
+ type = types.path;
+ description = "Path to the GPG encrypted passphrase.";
+ };
+
+ publicKey = mkOption {
+ default = "";
+ type = types.path;
+ description = "Path to the Public Key.";
+ };
+ };
+ });
+ };
+
+ fido2 = {
+ credential = mkOption {
+ default = null;
+ example = "f1d00200d8dc783f7fb1e10ace8da27f8312d72692abfca2f7e4960a73f48e82e1f7571f6ebfcee9fb434f9886ccc8fcc52a6614d8d2";
+ type = types.nullOr types.str;
+ description = "The FIDO2 credential ID.";
+ };
+
+ gracePeriod = mkOption {
+ default = 10;
+ type = types.int;
+ description = "Time in seconds to wait for the FIDO2 key.";
+ };
+
+ passwordLess = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Defines whatever to use an empty string as a default salt.
+
+ Enable only when your device is PIN protected, such as <link xlink:href="https://trezor.io/">Trezor</link>.
+ '';
+ };
+ };
+
+ yubikey = mkOption {
+ default = null;
+ description = ''
+ The options to use for this LUKS device in Yubikey-PBA.
+ If null (the default), Yubikey-PBA will be disabled for this device.
+ '';
+
+ type = with types; nullOr (submodule {
+ options = {
+ twoFactor = mkOption {
+ default = true;
+ type = types.bool;
+ description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false).";
+ };
+
+ slot = mkOption {
+ default = 2;
+ type = types.int;
+ description = "Which slot on the Yubikey to challenge.";
+ };
+
+ saltLength = mkOption {
+ default = 16;
+ type = types.int;
+ description = "Length of the new salt in byte (64 is the effective maximum).";
+ };
+
+ keyLength = mkOption {
+ default = 64;
+ type = types.int;
+ description = "Length of the LUKS slot key derived with PBKDF2 in byte.";
+ };
+
+ iterationStep = mkOption {
+ default = 0;
+ type = types.int;
+ description = "How much the iteration count for PBKDF2 is increased at each successful authentication.";
+ };
+
+ gracePeriod = mkOption {
+ default = 10;
+ type = types.int;
+ description = "Time in seconds to wait for the Yubikey.";
+ };
+
+ /* TODO: Add to the documentation of the current module:
+
+ Options related to the storing the salt.
+ */
+ storage = {
+ device = mkOption {
+ default = "/dev/sda1";
+ type = types.path;
+ description = ''
+ An unencrypted device that will temporarily be mounted in stage-1.
+ Must contain the current salt to create the challenge for this LUKS device.
+ '';
+ };
+
+ fsType = mkOption {
+ default = "vfat";
+ type = types.str;
+ description = "The filesystem of the unencrypted device.";
+ };
+
+ path = mkOption {
+ default = "/crypt-storage/default";
+ type = types.str;
+ description = ''
+ Absolute path of the salt on the unencrypted device with
+ that device's root directory as "/".
+ '';
+ };
+ };
+ };
+ });
+ };
+
+ preOpenCommands = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ mkdir -p /tmp/persistent
+ mount -t zfs rpool/safe/persistent /tmp/persistent
+ '';
+ description = ''
+ Commands that should be run right before we try to mount our LUKS device.
+ This can be useful, if the keys needed to open the drive is on another partion.
+ '';
+ };
+
+ postOpenCommands = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ umount /tmp/persistent
+ '';
+ description = ''
+ Commands that should be run right after we have mounted our LUKS device.
+ '';
+ };
+ };
+ }));
+ };
+
+ boot.initrd.luks.gpgSupport = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Enables support for authenticating with a GPG encrypted password.
+ '';
+ };
+
+ boot.initrd.luks.yubikeySupport = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Enables support for authenticating with a Yubikey on LUKS devices.
+ See the NixOS wiki for information on how to properly setup a LUKS device
+ and a Yubikey to work with this feature.
+ '';
+ };
+
+ boot.initrd.luks.fido2Support = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Enables support for authenticating with FIDO2 devices.
+ '';
+ };
+
+ };
+
+ config = mkIf (luks.devices != {} || luks.forceLuksSupportInInitrd) {
+
+ assertions =
+ [ { assertion = !(luks.gpgSupport && luks.yubikeySupport);
+ message = "Yubikey and GPG Card may not be used at the same time.";
+ }
+
+ { assertion = !(luks.gpgSupport && luks.fido2Support);
+ message = "FIDO2 and GPG Card may not be used at the same time.";
+ }
+
+ { assertion = !(luks.fido2Support && luks.yubikeySupport);
+ message = "FIDO2 and Yubikey may not be used at the same time.";
+ }
+ ];
+
+ # actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested
+ boot.blacklistedKernelModules = optionals luks.mitigateDMAAttacks
+ ["firewire_ohci" "firewire_core" "firewire_sbp2"];
+
+ # Some modules that may be needed for mounting anything ciphered
+ boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" "input_leds" ]
+ ++ luks.cryptoModules
+ # workaround until https://marc.info/?l=linux-crypto-vger&m=148783562211457&w=4 is merged
+ # remove once 'modprobe --show-depends xts' shows ecb as a dependency
+ ++ (if builtins.elem "xts" luks.cryptoModules then ["ecb"] else []);
+
+ # copy the cryptsetup binary and it's dependencies
+ boot.initrd.extraUtilsCommands = ''
+ copy_bin_and_libs ${pkgs.cryptsetup}/bin/cryptsetup
+ copy_bin_and_libs ${askPass}/bin/cryptsetup-askpass
+ sed -i s,/bin/sh,$out/bin/sh, $out/bin/cryptsetup-askpass
+
+ ${optionalString luks.yubikeySupport ''
+ copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykchalresp
+ copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykinfo
+ copy_bin_and_libs ${pkgs.openssl.bin}/bin/openssl
+
+ cc -O3 -I${pkgs.openssl.dev}/include -L${pkgs.openssl.out}/lib ${./pbkdf2-sha512.c} -o pbkdf2-sha512 -lcrypto
+ strip -s pbkdf2-sha512
+ copy_bin_and_libs pbkdf2-sha512
+
+ mkdir -p $out/etc/ssl
+ cp -pdv ${pkgs.openssl.out}/etc/ssl/openssl.cnf $out/etc/ssl
+
+ cat > $out/bin/openssl-wrap <<EOF
+ #!$out/bin/sh
+ export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
+ $out/bin/openssl "\$@"
+ EOF
+ chmod +x $out/bin/openssl-wrap
+ ''}
+
+ ${optionalString luks.fido2Support ''
+ copy_bin_and_libs ${pkgs.fido2luks}/bin/fido2luks
+ ''}
+
+
+ ${optionalString luks.gpgSupport ''
+ copy_bin_and_libs ${pkgs.gnupg}/bin/gpg
+ copy_bin_and_libs ${pkgs.gnupg}/bin/gpg-agent
+ copy_bin_and_libs ${pkgs.gnupg}/libexec/scdaemon
+
+ ${concatMapStringsSep "\n" (x:
+ if x.gpgCard != null then
+ ''
+ mkdir -p $out/secrets/gpg-keys/${x.device}
+ cp -a ${x.gpgCard.encryptedPass} $out/secrets/gpg-keys/${x.device}/cryptkey.gpg
+ cp -a ${x.gpgCard.publicKey} $out/secrets/gpg-keys/${x.device}/pubkey.asc
+ ''
+ else ""
+ ) (attrValues luks.devices)
+ }
+ ''}
+ '';
+
+ boot.initrd.extraUtilsCommandsTest = ''
+ $out/bin/cryptsetup --version
+ ${optionalString luks.yubikeySupport ''
+ $out/bin/ykchalresp -V
+ $out/bin/ykinfo -V
+ $out/bin/openssl-wrap version
+ ''}
+ ${optionalString luks.gpgSupport ''
+ $out/bin/gpg --version
+ $out/bin/gpg-agent --version
+ $out/bin/scdaemon --version
+ ''}
+ ${optionalString luks.fido2Support ''
+ $out/bin/fido2luks --version
+ ''}
+ '';
+
+ boot.initrd.preFailCommands = postCommands;
+ boot.initrd.preLVMCommands = commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand preLVM) + postCommands;
+ boot.initrd.postDeviceCommands = commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand postLVM) + postCommands;
+
+ environment.systemPackages = [ pkgs.cryptsetup ];
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/modprobe.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/modprobe.nix
new file mode 100644
index 000000000000..c75f32c4d99a
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/modprobe.nix
@@ -0,0 +1,68 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+ ###### interface
+
+ options = {
+
+ boot.blacklistedKernelModules = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ example = [ "cirrusfb" "i2c_piix4" ];
+ description = ''
+ List of names of kernel modules that should not be loaded
+ automatically by the hardware probing code.
+ '';
+ };
+
+ boot.extraModprobeConfig = mkOption {
+ default = "";
+ example =
+ ''
+ options parport_pc io=0x378 irq=7 dma=1
+ '';
+ description = ''
+ Any additional configuration to be appended to the generated
+ <filename>modprobe.conf</filename>. This is typically used to
+ specify module options. See
+ <citerefentry><refentrytitle>modprobe.d</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ type = types.lines;
+ };
+
+ };
+
+
+ ###### implementation
+
+ config = mkIf (!config.boot.isContainer) {
+
+ environment.etc."modprobe.d/ubuntu.conf".source = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
+
+ environment.etc."modprobe.d/nixos.conf".text =
+ ''
+ ${flip concatMapStrings config.boot.blacklistedKernelModules (name: ''
+ blacklist ${name}
+ '')}
+ ${config.boot.extraModprobeConfig}
+ '';
+ environment.etc."modprobe.d/debian.conf".source = pkgs.kmod-debian-aliases;
+
+ environment.systemPackages = [ pkgs.kmod ];
+
+ system.activationScripts.modprobe = stringAfter ["specialfs"]
+ ''
+ # Allow the kernel to find our wrapped modprobe (which searches
+ # in the right location in the Nix store for kernel modules).
+ # We need this when the kernel (or some module) auto-loads a
+ # module.
+ echo ${pkgs.kmod}/bin/modprobe > /proc/sys/kernel/modprobe
+ '';
+
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/networkd.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/networkd.nix
new file mode 100644
index 000000000000..47689b2a4700
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/networkd.nix
@@ -0,0 +1,1578 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+with import ./systemd-unit-options.nix { inherit config lib; };
+with import ./systemd-lib.nix { inherit config lib pkgs; };
+
+let
+
+ cfg = config.systemd.network;
+
+ check = {
+
+ link = {
+
+ sectionLink = checkUnitConfig "Link" [
+ (assertOnlyFields [
+ "Description"
+ "Alias"
+ "MACAddressPolicy"
+ "MACAddress"
+ "NamePolicy"
+ "Name"
+ "AlternativeNamesPolicy"
+ "AlternativeName"
+ "MTUBytes"
+ "BitsPerSecond"
+ "Duplex"
+ "AutoNegotiation"
+ "WakeOnLan"
+ "Port"
+ "Advertise"
+ "ReceiveChecksumOffload"
+ "TransmitChecksumOffload"
+ "TCPSegmentationOffload"
+ "TCP6SegmentationOffload"
+ "GenericSegmentationOffload"
+ "GenericReceiveOffload"
+ "LargeReceiveOffload"
+ "RxChannels"
+ "TxChannels"
+ "OtherChannels"
+ "CombinedChannels"
+ "RxBufferSize"
+ "TxBufferSize"
+ ])
+ (assertValueOneOf "MACAddressPolicy" ["persistent" "random" "none"])
+ (assertMacAddress "MACAddress")
+ (assertByteFormat "MTUBytes")
+ (assertByteFormat "BitsPerSecond")
+ (assertValueOneOf "Duplex" ["half" "full"])
+ (assertValueOneOf "AutoNegotiation" boolValues)
+ (assertValueOneOf "WakeOnLan" ["phy" "unicast" "multicast" "broadcast" "arp" "magic" "secureon" "off"])
+ (assertValueOneOf "Port" ["tp" "aui" "bnc" "mii" "fibre"])
+ (assertValueOneOf "ReceiveChecksumOffload" boolValues)
+ (assertValueOneOf "TransmitChecksumOffload" boolValues)
+ (assertValueOneOf "TCPSegmentationOffload" boolValues)
+ (assertValueOneOf "TCP6SegmentationOffload" boolValues)
+ (assertValueOneOf "GenericSegmentationOffload" boolValues)
+ (assertValueOneOf "GenericReceiveOffload" boolValues)
+ (assertValueOneOf "LargeReceiveOffload" boolValues)
+ (assertInt "RxChannels")
+ (assertRange "RxChannels" 1 4294967295)
+ (assertInt "TxChannels")
+ (assertRange "TxChannels" 1 4294967295)
+ (assertInt "OtherChannels")
+ (assertRange "OtherChannels" 1 4294967295)
+ (assertInt "CombinedChannels")
+ (assertRange "CombinedChannels" 1 4294967295)
+ (assertInt "RxBufferSize")
+ (assertInt "TxBufferSize")
+ ];
+ };
+
+ netdev = let
+
+ tunChecks = [
+ (assertOnlyFields [
+ "MultiQueue"
+ "PacketInfo"
+ "VNetHeader"
+ "User"
+ "Group"
+ ])
+ (assertValueOneOf "MultiQueue" boolValues)
+ (assertValueOneOf "PacketInfo" boolValues)
+ (assertValueOneOf "VNetHeader" boolValues)
+ ];
+ in {
+
+ sectionNetdev = checkUnitConfig "Netdev" [
+ (assertOnlyFields [
+ "Description"
+ "Name"
+ "Kind"
+ "MTUBytes"
+ "MACAddress"
+ ])
+ (assertHasField "Name")
+ (assertHasField "Kind")
+ (assertValueOneOf "Kind" [
+ "bond"
+ "bridge"
+ "dummy"
+ "gre"
+ "gretap"
+ "erspan"
+ "ip6gre"
+ "ip6tnl"
+ "ip6gretap"
+ "ipip"
+ "ipvlan"
+ "macvlan"
+ "macvtap"
+ "sit"
+ "tap"
+ "tun"
+ "veth"
+ "vlan"
+ "vti"
+ "vti6"
+ "vxlan"
+ "geneve"
+ "l2tp"
+ "macsec"
+ "vrf"
+ "vcan"
+ "vxcan"
+ "wireguard"
+ "netdevsim"
+ "nlmon"
+ "fou"
+ "xfrm"
+ "ifb"
+ ])
+ (assertByteFormat "MTUBytes")
+ (assertMacAddress "MACAddress")
+ ];
+
+ sectionVLAN = checkUnitConfig "VLAN" [
+ (assertOnlyFields [
+ "Id"
+ "GVRP"
+ "MVRP"
+ "LooseBinding"
+ "ReorderHeader"
+ ])
+ (assertInt "Id")
+ (assertRange "Id" 0 4094)
+ (assertValueOneOf "GVRP" boolValues)
+ (assertValueOneOf "MVRP" boolValues)
+ (assertValueOneOf "LooseBinding" boolValues)
+ (assertValueOneOf "ReorderHeader" boolValues)
+ ];
+
+ sectionMACVLAN = checkUnitConfig "MACVLAN" [
+ (assertOnlyFields [
+ "Mode"
+ ])
+ (assertValueOneOf "Mode" ["private" "vepa" "bridge" "passthru"])
+ ];
+
+ sectionVXLAN = checkUnitConfig "VXLAN" [
+ (assertOnlyFields [
+ "VNI"
+ "Remote"
+ "Local"
+ "Group"
+ "TOS"
+ "TTL"
+ "MacLearning"
+ "FDBAgeingSec"
+ "MaximumFDBEntries"
+ "ReduceARPProxy"
+ "L2MissNotification"
+ "L3MissNotification"
+ "RouteShortCircuit"
+ "UDPChecksum"
+ "UDP6ZeroChecksumTx"
+ "UDP6ZeroChecksumRx"
+ "RemoteChecksumTx"
+ "RemoteChecksumRx"
+ "GroupPolicyExtension"
+ "GenericProtocolExtension"
+ "DestinationPort"
+ "PortRange"
+ "FlowLabel"
+ "IPDoNotFragment"
+ ])
+ (assertInt "VNI")
+ (assertRange "VNI" 1 16777215)
+ (assertValueOneOf "MacLearning" boolValues)
+ (assertInt "MaximumFDBEntries")
+ (assertValueOneOf "ReduceARPProxy" boolValues)
+ (assertValueOneOf "L2MissNotification" boolValues)
+ (assertValueOneOf "L3MissNotification" boolValues)
+ (assertValueOneOf "RouteShortCircuit" boolValues)
+ (assertValueOneOf "UDPChecksum" boolValues)
+ (assertValueOneOf "UDP6ZeroChecksumTx" boolValues)
+ (assertValueOneOf "UDP6ZeroChecksumRx" boolValues)
+ (assertValueOneOf "RemoteChecksumTx" boolValues)
+ (assertValueOneOf "RemoteChecksumRx" boolValues)
+ (assertValueOneOf "GroupPolicyExtension" boolValues)
+ (assertValueOneOf "GenericProtocolExtension" boolValues)
+ (assertInt "FlowLabel")
+ (assertRange "FlowLabel" 0 1048575)
+ (assertValueOneOf "IPDoNotFragment" (boolValues + ["inherit"]))
+ ];
+
+ sectionTunnel = checkUnitConfig "Tunnel" [
+ (assertOnlyFields [
+ "Local"
+ "Remote"
+ "TOS"
+ "TTL"
+ "DiscoverPathMTU"
+ "IPv6FlowLabel"
+ "CopyDSCP"
+ "EncapsulationLimit"
+ "Key"
+ "InputKey"
+ "OutputKey"
+ "Mode"
+ "Independent"
+ "AssignToLoopback"
+ "AllowLocalRemote"
+ "FooOverUDP"
+ "FOUDestinationPort"
+ "FOUSourcePort"
+ "Encapsulation"
+ "IPv6RapidDeploymentPrefix"
+ "ISATAP"
+ "SerializeTunneledPackets"
+ "ERSPANIndex"
+ ])
+ (assertInt "TTL")
+ (assertRange "TTL" 0 255)
+ (assertValueOneOf "DiscoverPathMTU" boolValues)
+ (assertValueOneOf "CopyDSCP" boolValues)
+ (assertValueOneOf "Mode" ["ip6ip6" "ipip6" "any"])
+ (assertValueOneOf "Independent" boolValues)
+ (assertValueOneOf "AssignToLoopback" boolValues)
+ (assertValueOneOf "AllowLocalRemote" boolValues)
+ (assertValueOneOf "FooOverUDP" boolValues)
+ (assertPort "FOUDestinationPort")
+ (assertPort "FOUSourcePort")
+ (assertValueOneOf "Encapsulation" ["FooOverUDP" "GenericUDPEncapsulation"])
+ (assertValueOneOf "ISATAP" boolValues)
+ (assertValueOneOf "SerializeTunneledPackets" boolValues)
+ (assertInt "ERSPANIndex")
+ (assertRange "ERSPANIndex" 1 1048575)
+ ];
+
+ sectionPeer = checkUnitConfig "Peer" [
+ (assertOnlyFields [
+ "Name"
+ "MACAddress"
+ ])
+ (assertMacAddress "MACAddress")
+ ];
+
+ sectionTun = checkUnitConfig "Tun" tunChecks;
+
+ sectionTap = checkUnitConfig "Tap" tunChecks;
+
+ # NOTE The PrivateKey directive is missing on purpose here, please
+ # do not add it to this list. The nix store is world-readable let's
+ # refrain ourselves from providing a footgun.
+ sectionWireGuard = checkUnitConfig "WireGuard" [
+ (assertOnlyFields [
+ "PrivateKeyFile"
+ "ListenPort"
+ "FirewallMark"
+ ])
+ (assertInt "FirewallMark")
+ (assertRange "FirewallMark" 1 4294967295)
+ ];
+
+ # NOTE The PresharedKey directive is missing on purpose here, please
+ # do not add it to this list. The nix store is world-readable,let's
+ # refrain ourselves from providing a footgun.
+ sectionWireGuardPeer = checkUnitConfig "WireGuardPeer" [
+ (assertOnlyFields [
+ "PublicKey"
+ "PresharedKeyFile"
+ "AllowedIPs"
+ "Endpoint"
+ "PersistentKeepalive"
+ ])
+ (assertInt "PersistentKeepalive")
+ (assertRange "PersistentKeepalive" 0 65535)
+ ];
+
+ sectionBond = checkUnitConfig "Bond" [
+ (assertOnlyFields [
+ "Mode"
+ "TransmitHashPolicy"
+ "LACPTransmitRate"
+ "MIIMonitorSec"
+ "UpDelaySec"
+ "DownDelaySec"
+ "LearnPacketIntervalSec"
+ "AdSelect"
+ "AdActorSystemPriority"
+ "AdUserPortKey"
+ "AdActorSystem"
+ "FailOverMACPolicy"
+ "ARPValidate"
+ "ARPIntervalSec"
+ "ARPIPTargets"
+ "ARPAllTargets"
+ "PrimaryReselectPolicy"
+ "ResendIGMP"
+ "PacketsPerSlave"
+ "GratuitousARP"
+ "AllSlavesActive"
+ "DynamicTransmitLoadBalancing"
+ "MinLinks"
+ ])
+ (assertValueOneOf "Mode" [
+ "balance-rr"
+ "active-backup"
+ "balance-xor"
+ "broadcast"
+ "802.3ad"
+ "balance-tlb"
+ "balance-alb"
+ ])
+ (assertValueOneOf "TransmitHashPolicy" [
+ "layer2"
+ "layer3+4"
+ "layer2+3"
+ "encap2+3"
+ "encap3+4"
+ ])
+ (assertValueOneOf "LACPTransmitRate" ["slow" "fast"])
+ (assertValueOneOf "AdSelect" ["stable" "bandwidth" "count"])
+ (assertInt "AdActorSystemPriority")
+ (assertRange "AdActorSystemPriority" 1 65535)
+ (assertInt "AdUserPortKey")
+ (assertRange "AdUserPortKey" 0 1023)
+ (assertValueOneOf "FailOverMACPolicy" ["none" "active" "follow"])
+ (assertValueOneOf "ARPValidate" ["none" "active" "backup" "all"])
+ (assertValueOneOf "ARPAllTargets" ["any" "all"])
+ (assertValueOneOf "PrimaryReselectPolicy" ["always" "better" "failure"])
+ (assertInt "ResendIGMP")
+ (assertRange "ResendIGMP" 0 255)
+ (assertInt "PacketsPerSlave")
+ (assertRange "PacketsPerSlave" 0 65535)
+ (assertInt "GratuitousARP")
+ (assertRange "GratuitousARP" 0 255)
+ (assertValueOneOf "AllSlavesActive" boolValues)
+ (assertValueOneOf "DynamicTransmitLoadBalancing" boolValues)
+ (assertInt "MinLinks")
+ (assertMinimum "MinLinks" 0)
+ ];
+
+ sectionXfrm = checkUnitConfig "Xfrm" [
+ (assertOnlyFields [
+ "InterfaceId"
+ "Independent"
+ ])
+ (assertInt "InterfaceId")
+ (assertRange "InterfaceId" 1 4294967295)
+ (assertValueOneOf "Independent" boolValues)
+ ];
+
+ sectionVRF = checkUnitConfig "VRF" [
+ (assertOnlyFields [
+ "Table"
+ ])
+ (assertInt "Table")
+ (assertMinimum "Table" 0)
+ ];
+ };
+
+ network = {
+
+ sectionLink = checkUnitConfig "Link" [
+ (assertOnlyFields [
+ "MACAddress"
+ "MTUBytes"
+ "ARP"
+ "Multicast"
+ "AllMulticast"
+ "Unmanaged"
+ "RequiredForOnline"
+ ])
+ (assertMacAddress "MACAddress")
+ (assertByteFormat "MTUBytes")
+ (assertValueOneOf "ARP" boolValues)
+ (assertValueOneOf "Multicast" boolValues)
+ (assertValueOneOf "AllMulticast" boolValues)
+ (assertValueOneOf "Unmanaged" boolValues)
+ (assertValueOneOf "RequiredForOnline" (boolValues ++ [
+ "missing"
+ "off"
+ "no-carrier"
+ "dormant"
+ "degraded-carrier"
+ "carrier"
+ "degraded"
+ "enslaved"
+ "routable"
+ ]))
+ ];
+
+ sectionNetwork = checkUnitConfig "Network" [
+ (assertOnlyFields [
+ "Description"
+ "DHCP"
+ "DHCPServer"
+ "LinkLocalAddressing"
+ "IPv4LLRoute"
+ "DefaultRouteOnDevice"
+ "IPv6Token"
+ "LLMNR"
+ "MulticastDNS"
+ "DNSOverTLS"
+ "DNSSEC"
+ "DNSSECNegativeTrustAnchors"
+ "LLDP"
+ "EmitLLDP"
+ "BindCarrier"
+ "Address"
+ "Gateway"
+ "DNS"
+ "Domains"
+ "DNSDefaultRoute"
+ "NTP"
+ "IPForward"
+ "IPMasquerade"
+ "IPv6PrivacyExtensions"
+ "IPv6AcceptRA"
+ "IPv6DuplicateAddressDetection"
+ "IPv6HopLimit"
+ "IPv4ProxyARP"
+ "IPv6ProxyNDP"
+ "IPv6ProxyNDPAddress"
+ "IPv6PrefixDelegation"
+ "IPv6MTUBytes"
+ "Bridge"
+ "Bond"
+ "VRF"
+ "VLAN"
+ "IPVLAN"
+ "MACVLAN"
+ "VXLAN"
+ "Tunnel"
+ "MACsec"
+ "ActiveSlave"
+ "PrimarySlave"
+ "ConfigureWithoutCarrier"
+ "IgnoreCarrierLoss"
+ "Xfrm"
+ "KeepConfiguration"
+ ])
+ # Note: For DHCP the values both, none, v4, v6 are deprecated
+ (assertValueOneOf "DHCP" ["yes" "no" "ipv4" "ipv6"])
+ (assertValueOneOf "DHCPServer" boolValues)
+ (assertValueOneOf "LinkLocalAddressing" ["yes" "no" "ipv4" "ipv6" "fallback" "ipv4-fallback"])
+ (assertValueOneOf "IPv4LLRoute" boolValues)
+ (assertValueOneOf "DefaultRouteOnDevice" boolValues)
+ (assertValueOneOf "LLMNR" (boolValues ++ ["resolve"]))
+ (assertValueOneOf "MulticastDNS" (boolValues ++ ["resolve"]))
+ (assertValueOneOf "DNSOverTLS" (boolValues ++ ["opportunistic"]))
+ (assertValueOneOf "DNSSEC" (boolValues ++ ["allow-downgrade"]))
+ (assertValueOneOf "LLDP" (boolValues ++ ["routers-only"]))
+ (assertValueOneOf "EmitLLDP" (boolValues ++ ["nearest-bridge" "non-tpmr-bridge" "customer-bridge"]))
+ (assertValueOneOf "DNSDefaultRoute" boolValues)
+ (assertValueOneOf "IPForward" (boolValues ++ ["ipv4" "ipv6"]))
+ (assertValueOneOf "IPMasquerade" boolValues)
+ (assertValueOneOf "IPv6PrivacyExtensions" (boolValues ++ ["prefer-public" "kernel"]))
+ (assertValueOneOf "IPv6AcceptRA" boolValues)
+ (assertInt "IPv6DuplicateAddressDetection")
+ (assertMinimum "IPv6DuplicateAddressDetection" 0)
+ (assertInt "IPv6HopLimit")
+ (assertMinimum "IPv6HopLimit" 0)
+ (assertValueOneOf "IPv4ProxyARP" boolValues)
+ (assertValueOneOf "IPv6ProxyNDP" boolValues)
+ (assertValueOneOf "IPv6PrefixDelegation" ["static" "dhcpv6" "yes" "false"])
+ (assertByteFormat "IPv6MTUBytes")
+ (assertValueOneOf "ActiveSlave" boolValues)
+ (assertValueOneOf "PrimarySlave" boolValues)
+ (assertValueOneOf "ConfigureWithoutCarrier" boolValues)
+ (assertValueOneOf "IgnoreCarrierLoss" boolValues)
+ (assertValueOneOf "KeepConfiguration" (boolValues ++ ["static" "dhcp-on-stop" "dhcp"]))
+ ];
+
+ sectionAddress = checkUnitConfig "Address" [
+ (assertOnlyFields [
+ "Address"
+ "Peer"
+ "Broadcast"
+ "Label"
+ "PreferredLifetime"
+ "Scope"
+ "HomeAddress"
+ "DuplicateAddressDetection"
+ "ManageTemporaryAddress"
+ "AddPrefixRoute"
+ "AutoJoin"
+ ])
+ (assertHasField "Address")
+ (assertValueOneOf "PreferredLifetime" ["forever" "infinity" "0" 0])
+ (assertValueOneOf "HomeAddress" boolValues)
+ (assertValueOneOf "DuplicateAddressDetection" ["ipv4" "ipv6" "both" "none"])
+ (assertValueOneOf "ManageTemporaryAddress" boolValues)
+ (assertValueOneOf "AddPrefixRoute" boolValues)
+ (assertValueOneOf "AutoJoin" boolValues)
+ ];
+
+ sectionRoutingPolicyRule = checkUnitConfig "RoutingPolicyRule" [
+ (assertOnlyFields [
+ "TypeOfService"
+ "From"
+ "To"
+ "FirewallMark"
+ "Table"
+ "Priority"
+ "IncomingInterface"
+ "OutgoingInterface"
+ "SourcePort"
+ "DestinationPort"
+ "IPProtocol"
+ "InvertRule"
+ "Family"
+ "User"
+ "SuppressPrefixLength"
+ ])
+ (assertInt "TypeOfService")
+ (assertRange "TypeOfService" 0 255)
+ (assertInt "FirewallMark")
+ (assertRange "FirewallMark" 1 4294967295)
+ (assertInt "Priority")
+ (assertPort "SourcePort")
+ (assertPort "DestinationPort")
+ (assertValueOneOf "InvertRule" boolValues)
+ (assertValueOneOf "Family" ["ipv4" "ipv6" "both"])
+ (assertInt "SuppressPrefixLength")
+ (assertRange "SuppressPrefixLength" 0 128)
+ ];
+
+ sectionRoute = checkUnitConfig "Route" [
+ (assertOnlyFields [
+ "Gateway"
+ "GatewayOnLink"
+ "Destination"
+ "Source"
+ "Metric"
+ "IPv6Preference"
+ "Scope"
+ "PreferredSource"
+ "Table"
+ "Protocol"
+ "Type"
+ "InitialCongestionWindow"
+ "InitialAdvertisedReceiveWindow"
+ "QuickAck"
+ "FastOpenNoCookie"
+ "TTLPropagate"
+ "MTUBytes"
+ "IPServiceType"
+ "MultiPathRoute"
+ ])
+ (assertValueOneOf "GatewayOnLink" boolValues)
+ (assertInt "Metric")
+ (assertValueOneOf "IPv6Preference" ["low" "medium" "high"])
+ (assertValueOneOf "Scope" ["global" "site" "link" "host" "nowhere"])
+ (assertValueOneOf "Type" [
+ "unicast"
+ "local"
+ "broadcast"
+ "anycast"
+ "multicast"
+ "blackhole"
+ "unreachable"
+ "prohibit"
+ "throw"
+ "nat"
+ "xresolve"
+ ])
+ (assertValueOneOf "QuickAck" boolValues)
+ (assertValueOneOf "FastOpenNoCookie" boolValues)
+ (assertValueOneOf "TTLPropagate" boolValues)
+ (assertByteFormat "MTUBytes")
+ (assertValueOneOf "IPServiceType" ["CS6" "CS4"])
+ ];
+
+ sectionDHCPv4 = checkUnitConfig "DHCPv4" [
+ (assertOnlyFields [
+ "UseDNS"
+ "RoutesToDNS"
+ "UseNTP"
+ "UseSIP"
+ "UseMTU"
+ "Anonymize"
+ "SendHostname"
+ "UseHostname"
+ "Hostname"
+ "UseDomains"
+ "UseRoutes"
+ "UseTimezone"
+ "ClientIdentifier"
+ "VendorClassIdentifier"
+ "UserClass"
+ "MaxAttempts"
+ "DUIDType"
+ "DUIDRawData"
+ "IAID"
+ "RequestBroadcast"
+ "RouteMetric"
+ "RouteTable"
+ "RouteMTUBytes"
+ "ListenPort"
+ "SendRelease"
+ "SendDecline"
+ "BlackList"
+ "RequestOptions"
+ "SendOption"
+ ])
+ (assertValueOneOf "UseDNS" boolValues)
+ (assertValueOneOf "RoutesToDNS" boolValues)
+ (assertValueOneOf "UseNTP" boolValues)
+ (assertValueOneOf "UseSIP" boolValues)
+ (assertValueOneOf "UseMTU" boolValues)
+ (assertValueOneOf "Anonymize" boolValues)
+ (assertValueOneOf "SendHostname" boolValues)
+ (assertValueOneOf "UseHostname" boolValues)
+ (assertValueOneOf "UseDomains" (boolValues ++ ["route"]))
+ (assertValueOneOf "UseRoutes" boolValues)
+ (assertValueOneOf "UseTimezone" boolValues)
+ (assertValueOneOf "ClientIdentifier" ["mac" "duid" "duid-only"])
+ (assertInt "IAID")
+ (assertValueOneOf "RequestBroadcast" boolValues)
+ (assertInt "RouteMetric")
+ (assertInt "RouteTable")
+ (assertRange "RouteTable" 0 4294967295)
+ (assertByteFormat "RouteMTUBytes")
+ (assertPort "ListenPort")
+ (assertValueOneOf "SendRelease" boolValues)
+ (assertValueOneOf "SendDecline" boolValues)
+ ];
+
+ sectionDHCPv6 = checkUnitConfig "DHCPv6" [
+ (assertOnlyFields [
+ "UseDNS"
+ "UseNTP"
+ "RapidCommit"
+ "ForceDHCPv6PDOtherInformation"
+ "PrefixDelegationHint"
+ ])
+ (assertValueOneOf "UseDNS" boolValues)
+ (assertValueOneOf "UseNTP" boolValues)
+ (assertValueOneOf "RapidCommit" boolValues)
+ (assertValueOneOf "ForceDHCPv6PDOtherInformation" boolValues)
+ ];
+
+ sectionDHCPServer = checkUnitConfig "DHCPServer" [
+ (assertOnlyFields [
+ "PoolOffset"
+ "PoolSize"
+ "DefaultLeaseTimeSec"
+ "MaxLeaseTimeSec"
+ "EmitDNS"
+ "DNS"
+ "EmitNTP"
+ "NTP"
+ "EmitSIP"
+ "SIP"
+ "EmitRouter"
+ "EmitTimezone"
+ "Timezone"
+ "SendOption"
+ ])
+ (assertInt "PoolOffset")
+ (assertMinimum "PoolOffset" 0)
+ (assertInt "PoolSize")
+ (assertMinimum "PoolSize" 0)
+ (assertValueOneOf "EmitDNS" boolValues)
+ (assertValueOneOf "EmitNTP" boolValues)
+ (assertValueOneOf "EmitSIP" boolValues)
+ (assertValueOneOf "EmitRouter" boolValues)
+ (assertValueOneOf "EmitTimezone" boolValues)
+ ];
+
+ sectionIPv6PrefixDelegation = checkUnitConfig "IPv6PrefixDelegation" [
+ (assertOnlyFields [
+ "Managed"
+ "OtherInformation"
+ "RouterLifetimeSec"
+ "RouterPreference"
+ "EmitDNS"
+ "DNS"
+ "EmitDomains"
+ "Domains"
+ "DNSLifetimeSec"
+ ])
+ (assertValueOneOf "Managed" boolValues)
+ (assertValueOneOf "OtherInformation" boolValues)
+ (assertValueOneOf "RouterPreference" ["high" "medium" "low" "normal" "default"])
+ (assertValueOneOf "EmitDNS" boolValues)
+ (assertValueOneOf "EmitDomains" boolValues)
+ ];
+
+ sectionIPv6Prefix = checkUnitConfig "IPv6Prefix" [
+ (assertOnlyFields [
+ "AddressAutoconfiguration"
+ "OnLink"
+ "Prefix"
+ "PreferredLifetimeSec"
+ "ValidLifetimeSec"
+ ])
+ (assertValueOneOf "AddressAutoconfiguration" boolValues)
+ (assertValueOneOf "OnLink" boolValues)
+ ];
+
+ };
+ };
+
+ commonNetworkOptions = {
+
+ enable = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to manage network configuration using <command>systemd-network</command>.
+ '';
+ };
+
+ matchConfig = mkOption {
+ default = {};
+ example = { Name = "eth0"; };
+ type = types.attrsOf unitOption;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Match]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details.
+ '';
+ };
+
+ extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ description = "Extra configuration append to unit";
+ };
+ };
+
+ linkOptions = commonNetworkOptions // {
+ # overwrite enable option from above
+ enable = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to enable this .link unit. It's handled by udev no matter if <command>systemd-networkd</command> is enabled or not
+ '';
+ };
+
+ linkConfig = mkOption {
+ default = {};
+ example = { MACAddress = "00:ff:ee:aa:cc:dd"; };
+ type = types.addCheck (types.attrsOf unitOption) check.link.sectionLink;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Link]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.link</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ };
+
+ wireguardPeerOptions = {
+ options = {
+ wireguardPeerConfig = mkOption {
+ default = {};
+ example = { };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionWireGuardPeer;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[WireGuardPeer]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+ };
+ };
+
+ netdevOptions = commonNetworkOptions // {
+
+ netdevConfig = mkOption {
+ default = {};
+ example = { Name = "mybridge"; Kind = "bridge"; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionNetdev;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Netdev]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ vlanConfig = mkOption {
+ default = {};
+ example = { Id = 4; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionVLAN;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[VLAN]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ macvlanConfig = mkOption {
+ default = {};
+ example = { Mode = "private"; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionMACVLAN;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[MACVLAN]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ vxlanConfig = mkOption {
+ default = {};
+ example = { Id = "4"; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionVXLAN;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[VXLAN]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ tunnelConfig = mkOption {
+ default = {};
+ example = { Remote = "192.168.1.1"; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionTunnel;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Tunnel]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ peerConfig = mkOption {
+ default = {};
+ example = { Name = "veth2"; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionPeer;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Peer]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ tunConfig = mkOption {
+ default = {};
+ example = { User = "openvpn"; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionTun;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Tun]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ tapConfig = mkOption {
+ default = {};
+ example = { User = "openvpn"; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionTap;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Tap]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ wireguardConfig = mkOption {
+ default = {};
+ example = {
+ PrivateKeyFile = "/etc/wireguard/secret.key";
+ ListenPort = 51820;
+ FwMark = 42;
+ };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionWireGuard;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[WireGuard]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ Use <literal>PrivateKeyFile</literal> instead of
+ <literal>PrivateKey</literal>: the nix store is
+ world-readable.
+ '';
+ };
+
+ wireguardPeers = mkOption {
+ default = [];
+ example = [ { wireguardPeerConfig={
+ Endpoint = "192.168.1.1:51820";
+ PublicKey = "27s0OvaBBdHoJYkH9osZpjpgSOVNw+RaKfboT/Sfq0g=";
+ PresharedKeyFile = "/etc/wireguard/psk.key";
+ AllowedIPs = [ "10.0.0.1/32" ];
+ PersistentKeepalive = 15;
+ };}];
+ type = with types; listOf (submodule wireguardPeerOptions);
+ description = ''
+ Each item in this array specifies an option in the
+ <literal>[WireGuardPeer]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ Use <literal>PresharedKeyFile</literal> instead of
+ <literal>PresharedKey</literal>: the nix store is
+ world-readable.
+ '';
+ };
+
+ bondConfig = mkOption {
+ default = {};
+ example = { Mode = "802.3ad"; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionBond;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Bond]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ xfrmConfig = mkOption {
+ default = {};
+ example = { InterfaceId = 1; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionXfrm;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Xfrm]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ vrfConfig = mkOption {
+ default = {};
+ example = { Table = 2342; };
+ type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionVRF;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[VRF]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ A detailed explanation about how VRFs work can be found in the
+ <link xlink:href="https://www.kernel.org/doc/Documentation/networking/vrf.txt">kernel
+ docs</link>.
+ '';
+ };
+
+ };
+
+ addressOptions = {
+ options = {
+ addressConfig = mkOption {
+ default = {};
+ example = { Address = "192.168.0.100/24"; };
+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionAddress;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Address]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+ };
+ };
+
+ routingPolicyRulesOptions = {
+ options = {
+ routingPolicyRuleConfig = mkOption {
+ default = { };
+ example = { routingPolicyRuleConfig = { Table = 10; IncomingInterface = "eth1"; Family = "both"; } ;};
+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionRoutingPolicyRule;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[RoutingPolicyRule]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+ };
+ };
+
+ routeOptions = {
+ options = {
+ routeConfig = mkOption {
+ default = {};
+ example = { Gateway = "192.168.0.1"; };
+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionRoute;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Route]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+ };
+ };
+
+ ipv6PrefixOptions = {
+ options = {
+ ipv6PrefixConfig = mkOption {
+ default = {};
+ example = { Prefix = "fd00::/64"; };
+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionIPv6Prefix;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[IPv6Prefix]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+ };
+ };
+
+ networkOptions = commonNetworkOptions // {
+
+ linkConfig = mkOption {
+ default = {};
+ example = { Unmanaged = true; };
+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionLink;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Link]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ networkConfig = mkOption {
+ default = {};
+ example = { Description = "My Network"; };
+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionNetwork;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Network]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ # systemd.network.networks.*.dhcpConfig has been deprecated in favor of ….dhcpV4Config
+ # Produce a nice warning message so users know it is gone.
+ dhcpConfig = mkOption {
+ visible = false;
+ apply = _: throw "The option `systemd.network.networks.*.dhcpConfig` can no longer be used since it's been removed. Please use `systemd.network.networks.*.dhcpV4Config` instead.";
+ };
+
+ dhcpV4Config = mkOption {
+ default = {};
+ example = { UseDNS = true; UseRoutes = true; };
+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionDHCPv4;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[DHCPv4]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ dhcpV6Config = mkOption {
+ default = {};
+ example = { UseDNS = true; UseRoutes = true; };
+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionDHCPv6;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[DHCPv6]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ dhcpServerConfig = mkOption {
+ default = {};
+ example = { PoolOffset = 50; EmitDNS = false; };
+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionDHCPServer;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[DHCPServer]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ ipv6PrefixDelegationConfig = mkOption {
+ default = {};
+ example = { EmitDNS = true; Managed = true; OtherInformation = true; };
+ type = types.addCheck (types.attrsOf unitOption) check.network.sectionIPv6PrefixDelegation;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[IPv6PrefixDelegation]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ ipv6Prefixes = mkOption {
+ default = [];
+ example = { AddressAutoconfiguration = true; OnLink = true; };
+ type = with types; listOf (submodule ipv6PrefixOptions);
+ description = ''
+ A list of ipv6Prefix sections to be added to the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ name = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ The name of the network interface to match against.
+ '';
+ };
+
+ DHCP = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ Whether to enable DHCP on the interfaces matched.
+ '';
+ };
+
+ domains = mkOption {
+ type = types.nullOr (types.listOf types.str);
+ default = null;
+ description = ''
+ A list of domains to pass to the network config.
+ '';
+ };
+
+ address = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of addresses to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ gateway = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of gateways to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ dns = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of dns servers to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ ntp = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of ntp servers to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ bridge = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of bridge interfaces to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ bond = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of bond interfaces to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ vrf = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of vrf interfaces to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ vlan = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of vlan interfaces to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ macvlan = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of macvlan interfaces to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ vxlan = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of vxlan interfaces to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ tunnel = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of tunnel interfaces to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ xfrm = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ A list of xfrm interfaces to be added to the network section of the
+ unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ addresses = mkOption {
+ default = [ ];
+ type = with types; listOf (submodule addressOptions);
+ description = ''
+ A list of address sections to be added to the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ routingPolicyRules = mkOption {
+ default = [ ];
+ type = with types; listOf (submodule routingPolicyRulesOptions);
+ description = ''
+ A list of routing policy rules sections to be added to the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ routes = mkOption {
+ default = [ ];
+ type = with types; listOf (submodule routeOptions);
+ description = ''
+ A list of route sections to be added to the unit. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ };
+
+ networkConfig = { config, ... }: {
+ config = {
+ matchConfig = optionalAttrs (config.name != null) {
+ Name = config.name;
+ };
+ networkConfig = optionalAttrs (config.DHCP != null) {
+ DHCP = config.DHCP;
+ } // optionalAttrs (config.domains != null) {
+ Domains = concatStringsSep " " config.domains;
+ };
+ };
+ };
+
+ commonMatchText = def: optionalString (def.matchConfig != { }) ''
+ [Match]
+ ${attrsToSection def.matchConfig}
+ '';
+
+ linkToUnit = name: def:
+ { inherit (def) enable;
+ text = commonMatchText def
+ + ''
+ [Link]
+ ${attrsToSection def.linkConfig}
+ ''
+ + def.extraConfig;
+ };
+
+ netdevToUnit = name: def:
+ { inherit (def) enable;
+ text = commonMatchText def
+ + ''
+ [NetDev]
+ ${attrsToSection def.netdevConfig}
+ ''
+ + optionalString (def.vlanConfig != { }) ''
+ [VLAN]
+ ${attrsToSection def.vlanConfig}
+ ''
+ + optionalString (def.macvlanConfig != { }) ''
+ [MACVLAN]
+ ${attrsToSection def.macvlanConfig}
+ ''
+ + optionalString (def.vxlanConfig != { }) ''
+ [VXLAN]
+ ${attrsToSection def.vxlanConfig}
+ ''
+ + optionalString (def.tunnelConfig != { }) ''
+ [Tunnel]
+ ${attrsToSection def.tunnelConfig}
+ ''
+ + optionalString (def.peerConfig != { }) ''
+ [Peer]
+ ${attrsToSection def.peerConfig}
+ ''
+ + optionalString (def.tunConfig != { }) ''
+ [Tun]
+ ${attrsToSection def.tunConfig}
+ ''
+ + optionalString (def.tapConfig != { }) ''
+ [Tap]
+ ${attrsToSection def.tapConfig}
+ ''
+ + optionalString (def.wireguardConfig != { }) ''
+ [WireGuard]
+ ${attrsToSection def.wireguardConfig}
+ ''
+ + flip concatMapStrings def.wireguardPeers (x: ''
+ [WireGuardPeer]
+ ${attrsToSection x.wireguardPeerConfig}
+ '')
+ + optionalString (def.bondConfig != { }) ''
+ [Bond]
+ ${attrsToSection def.bondConfig}
+ ''
+ + optionalString (def.xfrmConfig != { }) ''
+ [Xfrm]
+ ${attrsToSection def.xfrmConfig}
+ ''
+ + optionalString (def.vrfConfig != { }) ''
+ [VRF]
+ ${attrsToSection def.vrfConfig}
+ ''
+ + def.extraConfig;
+ };
+
+ networkToUnit = name: def:
+ { inherit (def) enable;
+ text = commonMatchText def
+ + optionalString (def.linkConfig != { }) ''
+ [Link]
+ ${attrsToSection def.linkConfig}
+ ''
+ + ''
+ [Network]
+ ''
+ + attrsToSection def.networkConfig
+ + optionalString (def.address != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "Address=${s}") def.address)}
+ ''
+ + optionalString (def.gateway != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "Gateway=${s}") def.gateway)}
+ ''
+ + optionalString (def.dns != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "DNS=${s}") def.dns)}
+ ''
+ + optionalString (def.ntp != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "NTP=${s}") def.ntp)}
+ ''
+ + optionalString (def.bridge != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "Bridge=${s}") def.bridge)}
+ ''
+ + optionalString (def.bond != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "Bond=${s}") def.bond)}
+ ''
+ + optionalString (def.vrf != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "VRF=${s}") def.vrf)}
+ ''
+ + optionalString (def.vlan != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "VLAN=${s}") def.vlan)}
+ ''
+ + optionalString (def.macvlan != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "MACVLAN=${s}") def.macvlan)}
+ ''
+ + optionalString (def.vxlan != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "VXLAN=${s}") def.vxlan)}
+ ''
+ + optionalString (def.tunnel != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "Tunnel=${s}") def.tunnel)}
+ ''
+ + optionalString (def.xfrm != [ ]) ''
+ ${concatStringsSep "\n" (map (s: "Xfrm=${s}") def.xfrm)}
+ ''
+ + ''
+
+ ''
+ + flip concatMapStrings def.addresses (x: ''
+ [Address]
+ ${attrsToSection x.addressConfig}
+ '')
+ + flip concatMapStrings def.routingPolicyRules (x: ''
+ [RoutingPolicyRule]
+ ${attrsToSection x.routingPolicyRuleConfig}
+ '')
+ + flip concatMapStrings def.routes (x: ''
+ [Route]
+ ${attrsToSection x.routeConfig}
+ '')
+ + optionalString (def.dhcpV4Config != { }) ''
+ [DHCPv4]
+ ${attrsToSection def.dhcpV4Config}
+ ''
+ + optionalString (def.dhcpV6Config != { }) ''
+ [DHCPv6]
+ ${attrsToSection def.dhcpV6Config}
+ ''
+ + optionalString (def.dhcpServerConfig != { }) ''
+ [DHCPServer]
+ ${attrsToSection def.dhcpServerConfig}
+ ''
+ + optionalString (def.ipv6PrefixDelegationConfig != { }) ''
+ [IPv6PrefixDelegation]
+ ${attrsToSection def.ipv6PrefixDelegationConfig}
+ ''
+ + flip concatMapStrings def.ipv6Prefixes (x: ''
+ [IPv6Prefix]
+ ${attrsToSection x.ipv6PrefixConfig}
+ '')
+ + def.extraConfig;
+ };
+
+ unitFiles = listToAttrs (map (name: {
+ name = "systemd/network/${name}";
+ value.source = "${cfg.units.${name}.unit}/${name}";
+ }) (attrNames cfg.units));
+in
+
+{
+
+ options = {
+
+ systemd.network.enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to enable networkd or not.
+ '';
+ };
+
+ systemd.network.links = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = linkOptions; } ]);
+ description = "Definition of systemd network links.";
+ };
+
+ systemd.network.netdevs = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = netdevOptions; } ]);
+ description = "Definition of systemd network devices.";
+ };
+
+ systemd.network.networks = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = networkOptions; } networkConfig ]);
+ description = "Definition of systemd networks.";
+ };
+
+ systemd.network.units = mkOption {
+ description = "Definition of networkd units.";
+ default = {};
+ internal = true;
+ type = with types; attrsOf (submodule (
+ { name, config, ... }:
+ { options = mapAttrs (_: x: x // { internal = true; }) concreteUnitOptions;
+ config = {
+ unit = mkDefault (makeUnit name config);
+ };
+ }));
+ };
+
+ };
+
+ config = mkMerge [
+
+ # .link units are honored by udev, no matter if systemd-networkd is enabled or not.
+ {
+ systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links;
+ environment.etc = unitFiles;
+ }
+
+ (mkIf config.systemd.network.enable {
+
+ users.users.systemd-network.group = "systemd-network";
+
+ systemd.additionalUpstreamSystemUnits = [
+ "systemd-networkd-wait-online.service"
+ "systemd-networkd.service"
+ "systemd-networkd.socket"
+ ];
+
+ systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs
+ // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks;
+
+ # systemd-networkd is socket-activated by kernel netlink route change
+ # messages. It is important to have systemd buffer those on behalf of
+ # networkd.
+ systemd.sockets.systemd-networkd.wantedBy = [ "sockets.target" ];
+
+ systemd.services.systemd-networkd = {
+ wantedBy = [ "multi-user.target" ];
+ aliases = [ "dbus-org.freedesktop.network1.service" ];
+ restartTriggers = map (x: x.source) (attrValues unitFiles);
+ # prevent race condition with interface renaming (#39069)
+ requires = [ "systemd-udev-settle.service" ];
+ after = [ "systemd-udev-settle.service" ];
+ };
+
+ systemd.services.systemd-networkd-wait-online = {
+ wantedBy = [ "network-online.target" ];
+ };
+
+ systemd.services."systemd-network-wait-online@" = {
+ description = "Wait for Network Interface %I to be Configured";
+ conflicts = [ "shutdown.target" ];
+ requisite = [ "systemd-networkd.service" ];
+ after = [ "systemd-networkd.service" ];
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ ExecStart = "${config.systemd.package}/lib/systemd/systemd-networkd-wait-online -i %I";
+ };
+ };
+
+ services.resolved.enable = mkDefault true;
+ })
+ ];
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/pbkdf2-sha512.c b/infra/libkookie/nixpkgs/nixos/modules/system/boot/pbkdf2-sha512.c
new file mode 100644
index 000000000000..b40c383ac023
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/pbkdf2-sha512.c
@@ -0,0 +1,38 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <openssl/evp.h>
+
+void hextorb(uint8_t* hex, uint8_t* rb)
+{
+ while(sscanf(hex, "%2x", rb) == 1)
+ {
+ hex += 2;
+ rb += 1;
+ }
+ *rb = '\0';
+}
+
+int main(int argc, char** argv)
+{
+ uint8_t k_user[2048];
+ uint8_t salt[2048];
+ uint8_t key[4096];
+
+ uint32_t key_length = atoi(argv[1]);
+ uint32_t iteration_count = atoi(argv[2]);
+
+ hextorb(argv[3], salt);
+ uint32_t salt_length = strlen(argv[3]) / 2;
+
+ fgets(k_user, 2048, stdin);
+ uint32_t k_user_length = strlen(k_user);
+ if(k_user[k_user_length - 1] == '\n') {
+ k_user[k_user_length - 1] = '\0';
+ }
+
+ PKCS5_PBKDF2_HMAC(k_user, k_user_length, salt, salt_length, iteration_count, EVP_sha512(), key_length, key);
+ fwrite(key, 1, key_length, stdout);
+
+ return 0;
+} \ No newline at end of file
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/plymouth.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/plymouth.nix
new file mode 100644
index 000000000000..55e5b07ed615
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/plymouth.nix
@@ -0,0 +1,173 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ inherit (pkgs) plymouth;
+ inherit (pkgs) nixos-icons;
+
+ cfg = config.boot.plymouth;
+
+ nixosBreezePlymouth = pkgs.breeze-plymouth.override {
+ logoFile = cfg.logo;
+ logoName = "nixos";
+ osName = "NixOS";
+ osVersion = config.system.nixos.release;
+ };
+
+ themesEnv = pkgs.buildEnv {
+ name = "plymouth-themes";
+ paths = [ plymouth ] ++ cfg.themePackages;
+ };
+
+ configFile = pkgs.writeText "plymouthd.conf" ''
+ [Daemon]
+ ShowDelay=0
+ Theme=${cfg.theme}
+ ${cfg.extraConfig}
+ '';
+
+in
+
+{
+
+ options = {
+
+ boot.plymouth = {
+
+ enable = mkEnableOption "Plymouth boot splash screen";
+
+ themePackages = mkOption {
+ default = [ nixosBreezePlymouth ];
+ type = types.listOf types.package;
+ description = ''
+ Extra theme packages for plymouth.
+ '';
+ };
+
+ theme = mkOption {
+ default = "breeze";
+ type = types.str;
+ description = ''
+ Splash screen theme.
+ '';
+ };
+
+ logo = mkOption {
+ type = types.path;
+ default = "${nixos-icons}/share/icons/hicolor/128x128/apps/nix-snowflake.png";
+ defaultText = ''pkgs.fetchurl {
+ url = "https://nixos.org/logo/nixos-hires.png";
+ sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
+ }'';
+ description = ''
+ Logo which is displayed on the splash screen.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Literal string to append to <literal>configFile</literal>
+ and the config file generated by the plymouth module.
+ '';
+ };
+
+ };
+
+ };
+
+ config = mkIf cfg.enable {
+
+ boot.kernelParams = [ "splash" ];
+
+ # To be discoverable by systemd.
+ environment.systemPackages = [ plymouth ];
+
+ environment.etc."plymouth/plymouthd.conf".source = configFile;
+ environment.etc."plymouth/plymouthd.defaults".source = "${plymouth}/share/plymouth/plymouthd.defaults";
+ environment.etc."plymouth/logo.png".source = cfg.logo;
+ environment.etc."plymouth/themes".source = "${themesEnv}/share/plymouth/themes";
+ # XXX: Needed because we supply a different set of plugins in initrd.
+ environment.etc."plymouth/plugins".source = "${plymouth}/lib/plymouth";
+
+ systemd.packages = [ plymouth ];
+
+ systemd.services.plymouth-kexec.wantedBy = [ "kexec.target" ];
+ systemd.services.plymouth-halt.wantedBy = [ "halt.target" ];
+ systemd.services.plymouth-quit-wait.wantedBy = [ "multi-user.target" ];
+ systemd.services.plymouth-quit.wantedBy = [ "multi-user.target" ];
+ systemd.services.plymouth-poweroff.wantedBy = [ "poweroff.target" ];
+ systemd.services.plymouth-reboot.wantedBy = [ "reboot.target" ];
+ systemd.services.plymouth-read-write.wantedBy = [ "sysinit.target" ];
+ systemd.services.systemd-ask-password-plymouth.wantedBy = ["multi-user.target"];
+ systemd.paths.systemd-ask-password-plymouth.wantedBy = ["multi-user.target"];
+
+ boot.initrd.extraUtilsCommands = ''
+ copy_bin_and_libs ${pkgs.plymouth}/bin/plymouthd
+ copy_bin_and_libs ${pkgs.plymouth}/bin/plymouth
+
+ moduleName="$(sed -n 's,ModuleName *= *,,p' ${themesEnv}/share/plymouth/themes/${cfg.theme}/${cfg.theme}.plymouth)"
+
+ mkdir -p $out/lib/plymouth/renderers
+ # module might come from a theme
+ cp ${themesEnv}/lib/plymouth/{text,details,$moduleName}.so $out/lib/plymouth
+ cp ${plymouth}/lib/plymouth/renderers/{drm,frame-buffer}.so $out/lib/plymouth/renderers
+
+ mkdir -p $out/share/plymouth/themes
+ cp ${plymouth}/share/plymouth/plymouthd.defaults $out/share/plymouth
+
+ # copy themes into working directory for patching
+ mkdir themes
+ # use -L to copy the directories proper, not the symlinks to them
+ cp -r -L ${themesEnv}/share/plymouth/themes/{text,details,${cfg.theme}} themes
+
+ # patch out any attempted references to the theme or plymouth's themes directory
+ chmod -R +w themes
+ find themes -type f | while read file
+ do
+ sed -i "s,/nix/.*/share/plymouth/themes,$out/share/plymouth/themes,g" $file
+ done
+
+ cp -r themes/* $out/share/plymouth/themes
+ cp ${cfg.logo} $out/share/plymouth/logo.png
+ '';
+
+ boot.initrd.extraUtilsCommandsTest = ''
+ $out/bin/plymouthd --help >/dev/null
+ $out/bin/plymouth --help >/dev/null
+ '';
+
+ boot.initrd.extraUdevRulesCommands = ''
+ cp ${config.systemd.package}/lib/udev/rules.d/{70-uaccess,71-seat}.rules $out
+ sed -i '/loginctl/d' $out/71-seat.rules
+ '';
+
+ # We use `mkAfter` to ensure that LUKS password prompt would be shown earlier than the splash screen.
+ boot.initrd.preLVMCommands = mkAfter ''
+ mkdir -p /etc/plymouth
+ mkdir -p /run/plymouth
+ ln -s ${configFile} /etc/plymouth/plymouthd.conf
+ ln -s $extraUtils/share/plymouth/plymouthd.defaults /etc/plymouth/plymouthd.defaults
+ ln -s $extraUtils/share/plymouth/logo.png /etc/plymouth/logo.png
+ ln -s $extraUtils/share/plymouth/themes /etc/plymouth/themes
+ ln -s $extraUtils/lib/plymouth /etc/plymouth/plugins
+
+ plymouthd --mode=boot --pid-file=/run/plymouth/pid --attach-to-session
+ plymouth show-splash
+ '';
+
+ boot.initrd.postMountCommands = ''
+ plymouth update-root-fs --new-root-dir="$targetRoot"
+ '';
+
+ # `mkBefore` to ensure that any custom prompts would be visible.
+ boot.initrd.preFailCommands = mkBefore ''
+ plymouth quit --wait
+ '';
+
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/resolved.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/resolved.nix
new file mode 100644
index 000000000000..b024f9cf5ee9
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/resolved.nix
@@ -0,0 +1,181 @@
+{ config, lib, ... }:
+
+with lib;
+let
+ cfg = config.services.resolved;
+
+ dnsmasqResolve = config.services.dnsmasq.enable &&
+ config.services.dnsmasq.resolveLocalQueries;
+
+in
+{
+
+ options = {
+
+ services.resolved.enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to enable the systemd DNS resolver daemon.
+ '';
+ };
+
+ services.resolved.fallbackDns = mkOption {
+ default = [ ];
+ example = [ "8.8.8.8" "2001:4860:4860::8844" ];
+ type = types.listOf types.str;
+ description = ''
+ A list of IPv4 and IPv6 addresses to use as the fallback DNS servers.
+ If this option is empty, a compiled-in list of DNS servers is used instead.
+ '';
+ };
+
+ services.resolved.domains = mkOption {
+ default = config.networking.search;
+ example = [ "example.com" ];
+ type = types.listOf types.str;
+ description = ''
+ A list of domains. These domains are used as search suffixes
+ when resolving single-label host names (domain names which
+ contain no dot), in order to qualify them into fully-qualified
+ domain names (FQDNs).
+
+ For compatibility reasons, if this setting is not specified,
+ the search domains listed in
+ <filename>/etc/resolv.conf</filename> are used instead, if
+ that file exists and any domains are configured in it.
+ '';
+ };
+
+ services.resolved.llmnr = mkOption {
+ default = "true";
+ example = "false";
+ type = types.enum [ "true" "resolve" "false" ];
+ description = ''
+ Controls Link-Local Multicast Name Resolution support
+ (RFC 4795) on the local host.
+
+ If set to
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>"true"</literal></term>
+ <listitem><para>
+ Enables full LLMNR responder and resolver support.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>"false"</literal></term>
+ <listitem><para>
+ Disables both.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>"resolve"</literal></term>
+ <listitem><para>
+ Only resolution support is enabled, but responding is disabled.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+ '';
+ };
+
+ services.resolved.dnssec = mkOption {
+ default = "allow-downgrade";
+ example = "true";
+ type = types.enum [ "true" "allow-downgrade" "false" ];
+ description = ''
+ If set to
+ <variablelist>
+ <varlistentry>
+ <term><literal>"true"</literal></term>
+ <listitem><para>
+ all DNS lookups are DNSSEC-validated locally (excluding
+ LLMNR and Multicast DNS). Note that this mode requires a
+ DNS server that supports DNSSEC. If the DNS server does
+ not properly support DNSSEC all validations will fail.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>"allow-downgrade"</literal></term>
+ <listitem><para>
+ DNSSEC validation is attempted, but if the server does not
+ support DNSSEC properly, DNSSEC mode is automatically
+ disabled. Note that this mode makes DNSSEC validation
+ vulnerable to "downgrade" attacks, where an attacker might
+ be able to trigger a downgrade to non-DNSSEC mode by
+ synthesizing a DNS response that suggests DNSSEC was not
+ supported.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>"false"</literal></term>
+ <listitem><para>
+ DNS lookups are not DNSSEC validated.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+ '';
+ };
+
+ services.resolved.extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Extra config to append to resolved.conf.
+ '';
+ };
+
+ };
+
+ config = mkIf cfg.enable {
+
+ assertions = [
+ { assertion = !config.networking.useHostResolvConf;
+ message = "Using host resolv.conf is not supported with systemd-resolved";
+ }
+ ];
+
+ users.users.resolved.group = "systemd-resolve";
+
+ # add resolve to nss hosts database if enabled and nscd enabled
+ # system.nssModules is configured in nixos/modules/system/boot/systemd.nix
+ system.nssDatabases.hosts = optional config.services.nscd.enable "resolve [!UNAVAIL=return]";
+
+ systemd.additionalUpstreamSystemUnits = [
+ "systemd-resolved.service"
+ ];
+
+ systemd.services.systemd-resolved = {
+ wantedBy = [ "multi-user.target" ];
+ aliases = [ "dbus-org.freedesktop.resolve1.service" ];
+ restartTriggers = [ config.environment.etc."systemd/resolved.conf".source ];
+ };
+
+ environment.etc = {
+ "systemd/resolved.conf".text = ''
+ [Resolve]
+ ${optionalString (config.networking.nameservers != [])
+ "DNS=${concatStringsSep " " config.networking.nameservers}"}
+ ${optionalString (cfg.fallbackDns != [])
+ "FallbackDNS=${concatStringsSep " " cfg.fallbackDns}"}
+ ${optionalString (cfg.domains != [])
+ "Domains=${concatStringsSep " " cfg.domains}"}
+ LLMNR=${cfg.llmnr}
+ DNSSEC=${cfg.dnssec}
+ ${config.services.resolved.extraConfig}
+ '';
+
+ # symlink the dynamic stub resolver of resolv.conf as recommended by upstream:
+ # https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf
+ "resolv.conf".source = "/run/systemd/resolve/stub-resolv.conf";
+ } // optionalAttrs dnsmasqResolve {
+ "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
+ };
+
+ # If networkmanager is enabled, ask it to interface with resolved.
+ networking.networkmanager.dns = "systemd-resolved";
+
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/shutdown.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/shutdown.nix
new file mode 100644
index 000000000000..11041066e07c
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/shutdown.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+ # This unit saves the value of the system clock to the hardware
+ # clock on shutdown.
+ systemd.services.save-hwclock =
+ { description = "Save Hardware Clock";
+
+ wantedBy = [ "shutdown.target" ];
+
+ unitConfig = {
+ DefaultDependencies = false;
+ ConditionPathExists = "/dev/rtc";
+ };
+
+ serviceConfig = {
+ Type = "oneshot";
+ ExecStart = "${pkgs.utillinux}/sbin/hwclock --systohc ${if config.time.hardwareClockInLocalTime then "--localtime" else "--utc"}";
+ };
+ };
+
+ boot.kernel.sysctl."kernel.poweroff_cmd" = "${config.systemd.package}/sbin/poweroff";
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-1-init.sh b/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
new file mode 100644
index 000000000000..f7c2940049e5
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
@@ -0,0 +1,623 @@
+#! @shell@
+
+targetRoot=/mnt-root
+console=tty1
+
+extraUtils="@extraUtils@"
+export LD_LIBRARY_PATH=@extraUtils@/lib
+export PATH=@extraUtils@/bin
+ln -s @extraUtils@/bin /bin
+
+# Copy the secrets to their needed location
+if [ -d "@extraUtils@/secrets" ]; then
+ for secret in $(cd "@extraUtils@/secrets"; find . -type f); do
+ mkdir -p $(dirname "/$secret")
+ ln -s "@extraUtils@/secrets/$secret" "$secret"
+ done
+fi
+
+# Stop LVM complaining about fd3
+export LVM_SUPPRESS_FD_WARNINGS=true
+
+fail() {
+ if [ -n "$panicOnFail" ]; then exit 1; fi
+
+ @preFailCommands@
+
+ # If starting stage 2 failed, allow the user to repair the problem
+ # in an interactive shell.
+ cat <<EOF
+
+An error occurred in stage 1 of the boot process, which must mount the
+root filesystem on \`$targetRoot' and then start stage 2. Press one
+of the following keys:
+
+EOF
+ if [ -n "$allowShell" ]; then cat <<EOF
+ i) to launch an interactive shell
+ f) to start an interactive shell having pid 1 (needed if you want to
+ start stage 2's init manually)
+EOF
+ fi
+ cat <<EOF
+ r) to reboot immediately
+ *) to ignore the error and continue
+EOF
+
+ read -n 1 reply
+
+ if [ -n "$allowShell" -a "$reply" = f ]; then
+ exec setsid @shell@ -c "exec @shell@ < /dev/$console >/dev/$console 2>/dev/$console"
+ elif [ -n "$allowShell" -a "$reply" = i ]; then
+ echo "Starting interactive shell..."
+ setsid @shell@ -c "exec @shell@ < /dev/$console >/dev/$console 2>/dev/$console" || fail
+ elif [ "$reply" = r ]; then
+ echo "Rebooting..."
+ reboot -f
+ else
+ echo "Continuing..."
+ fi
+}
+
+trap 'fail' 0
+
+
+# Print a greeting.
+echo
+echo "<<< NixOS Stage 1 >>>"
+echo
+
+# Make several required directories.
+mkdir -p /etc/udev
+touch /etc/fstab # to shut up mount
+ln -s /proc/mounts /etc/mtab # to shut up mke2fs
+touch /etc/udev/hwdb.bin # to shut up udev
+touch /etc/initrd-release
+
+# Function for waiting a device to appear.
+waitDevice() {
+ local device="$1"
+
+ # USB storage devices tend to appear with some delay. It would be
+ # great if we had a way to synchronously wait for them, but
+ # alas... So just wait for a few seconds for the device to
+ # appear.
+ if test ! -e $device; then
+ echo -n "waiting for device $device to appear..."
+ try=20
+ while [ $try -gt 0 ]; do
+ sleep 1
+ # also re-try lvm activation now that new block devices might have appeared
+ lvm vgchange -ay
+ # and tell udev to create nodes for the new LVs
+ udevadm trigger --action=add
+ if test -e $device; then break; fi
+ echo -n "."
+ try=$((try - 1))
+ done
+ echo
+ [ $try -ne 0 ]
+ fi
+}
+
+# Mount special file systems.
+specialMount() {
+ local device="$1"
+ local mountPoint="$2"
+ local options="$3"
+ local fsType="$4"
+
+ mkdir -m 0755 -p "$mountPoint"
+ mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
+}
+source @earlyMountScript@
+
+# Log the script output to /dev/kmsg or /run/log/stage-1-init.log.
+mkdir -p /tmp
+mkfifo /tmp/stage-1-init.log.fifo
+logOutFd=8 && logErrFd=9
+eval "exec $logOutFd>&1 $logErrFd>&2"
+if test -w /dev/kmsg; then
+ tee -i < /tmp/stage-1-init.log.fifo /proc/self/fd/"$logOutFd" | while read -r line; do
+ if test -n "$line"; then
+ echo "<7>stage-1-init: $line" > /dev/kmsg
+ fi
+ done &
+else
+ mkdir -p /run/log
+ tee -i < /tmp/stage-1-init.log.fifo /run/log/stage-1-init.log &
+fi
+exec > /tmp/stage-1-init.log.fifo 2>&1
+
+
+# Process the kernel command line.
+export stage2Init=/init
+for o in $(cat /proc/cmdline); do
+ case $o in
+ console=*)
+ set -- $(IFS==; echo $o)
+ params=$2
+ set -- $(IFS=,; echo $params)
+ console=$1
+ ;;
+ init=*)
+ set -- $(IFS==; echo $o)
+ stage2Init=$2
+ ;;
+ boot.persistence=*)
+ set -- $(IFS==; echo $o)
+ persistence=$2
+ ;;
+ boot.persistence.opt=*)
+ set -- $(IFS==; echo $o)
+ persistence_opt=$2
+ ;;
+ boot.trace|debugtrace)
+ # Show each command.
+ set -x
+ ;;
+ boot.shell_on_fail)
+ allowShell=1
+ ;;
+ boot.debug1|debug1) # stop right away
+ allowShell=1
+ fail
+ ;;
+ boot.debug1devices) # stop after loading modules and creating device nodes
+ allowShell=1
+ debug1devices=1
+ ;;
+ boot.debug1mounts) # stop after mounting file systems
+ allowShell=1
+ debug1mounts=1
+ ;;
+ boot.panic_on_fail|stage1panic=1)
+ panicOnFail=1
+ ;;
+ root=*)
+ # If a root device is specified on the kernel command
+ # line, make it available through the symlink /dev/root.
+ # Recognise LABEL= and UUID= to support UNetbootin.
+ set -- $(IFS==; echo $o)
+ if [ $2 = "LABEL" ]; then
+ root="/dev/disk/by-label/$3"
+ elif [ $2 = "UUID" ]; then
+ root="/dev/disk/by-uuid/$3"
+ else
+ root=$2
+ fi
+ ln -s "$root" /dev/root
+ ;;
+ copytoram)
+ copytoram=1
+ ;;
+ findiso=*)
+ # if an iso name is supplied, try to find the device where
+ # the iso resides on
+ set -- $(IFS==; echo $o)
+ isoPath=$2
+ ;;
+ esac
+done
+
+# Set hostid before modules are loaded.
+# This is needed by the spl/zfs modules.
+@setHostId@
+
+# Load the required kernel modules.
+mkdir -p /lib
+ln -s @modulesClosure@/lib/modules /lib/modules
+ln -s @modulesClosure@/lib/firmware /lib/firmware
+echo @extraUtils@/bin/modprobe > /proc/sys/kernel/modprobe
+for i in @kernelModules@; do
+ echo "loading module $(basename $i)..."
+ modprobe $i
+done
+
+
+# Create device nodes in /dev.
+@preDeviceCommands@
+echo "running udev..."
+ln -sfn /proc/self/fd /dev/fd
+mkdir -p /etc/systemd
+ln -sfn @linkUnits@ /etc/systemd/network
+mkdir -p /etc/udev
+ln -sfn @udevRules@ /etc/udev/rules.d
+mkdir -p /dev/.mdadm
+systemd-udevd --daemon
+udevadm trigger --action=add
+udevadm settle
+
+
+# XXX: Use case usb->lvm will still fail, usb->luks->lvm is covered
+@preLVMCommands@
+
+
+echo "starting device mapper and LVM..."
+lvm vgchange -ay
+
+if test -n "$debug1devices"; then fail; fi
+
+
+@postDeviceCommands@
+
+
+# Return true if the machine is on AC power, or if we can't determine
+# whether it's on AC power.
+onACPower() {
+ ! test -d "/proc/acpi/battery" ||
+ ! ls /proc/acpi/battery/BAT[0-9]* > /dev/null 2>&1 ||
+ ! cat /proc/acpi/battery/BAT*/state | grep "^charging state" | grep -q "discharg"
+}
+
+
+# Check the specified file system, if appropriate.
+checkFS() {
+ local device="$1"
+ local fsType="$2"
+
+ # Only check block devices.
+ if [ ! -b "$device" ]; then return 0; fi
+
+ # Don't check ROM filesystems.
+ if [ "$fsType" = iso9660 -o "$fsType" = udf ]; then return 0; fi
+
+ # Don't check resilient COWs as they validate the fs structures at mount time
+ if [ "$fsType" = btrfs -o "$fsType" = zfs -o "$fsType" = bcachefs ]; then return 0; fi
+
+ # Skip fsck for nilfs2 - not needed by design and no fsck tool for this filesystem.
+ if [ "$fsType" = nilfs2 ]; then return 0; fi
+
+ # Skip fsck for inherently readonly filesystems.
+ if [ "$fsType" = squashfs ]; then return 0; fi
+
+ # If we couldn't figure out the FS type, then skip fsck.
+ if [ "$fsType" = auto ]; then
+ echo 'cannot check filesystem with type "auto"!'
+ return 0
+ fi
+
+ # Device might be already mounted manually
+ # e.g. NBD-device or the host filesystem of the file which contains encrypted root fs
+ if mount | grep -q "^$device on "; then
+ echo "skip checking already mounted $device"
+ return 0
+ fi
+
+ # Optionally, skip fsck on journaling filesystems. This option is
+ # a hack - it's mostly because e2fsck on ext3 takes much longer to
+ # recover the journal than the ext3 implementation in the kernel
+ # does (minutes versus seconds).
+ if test -z "@checkJournalingFS@" -a \
+ \( "$fsType" = ext3 -o "$fsType" = ext4 -o "$fsType" = reiserfs \
+ -o "$fsType" = xfs -o "$fsType" = jfs -o "$fsType" = f2fs \)
+ then
+ return 0
+ fi
+
+ # Don't run `fsck' if the machine is on battery power. !!! Is
+ # this a good idea?
+ if ! onACPower; then
+ echo "on battery power, so no \`fsck' will be performed on \`$device'"
+ return 0
+ fi
+
+ echo "checking $device..."
+
+ fsckFlags=
+ if test "$fsType" != "btrfs"; then
+ fsckFlags="-V -a"
+ fi
+ fsck $fsckFlags "$device"
+ fsckResult=$?
+
+ if test $(($fsckResult | 2)) = $fsckResult; then
+ echo "fsck finished, rebooting..."
+ sleep 3
+ reboot -f
+ fi
+
+ if test $(($fsckResult | 4)) = $fsckResult; then
+ echo "$device has unrepaired errors, please fix them manually."
+ fail
+ fi
+
+ if test $fsckResult -ge 8; then
+ echo "fsck on $device failed."
+ fail
+ fi
+
+ return 0
+}
+
+
+# Function for mounting a file system.
+mountFS() {
+ local device="$1"
+ local mountPoint="$2"
+ local options="$3"
+ local fsType="$4"
+
+ if [ "$fsType" = auto ]; then
+ fsType=$(blkid -o value -s TYPE "$device")
+ if [ -z "$fsType" ]; then fsType=auto; fi
+ fi
+
+ # Filter out x- options, which busybox doesn't do yet.
+ local optionsFiltered="$(IFS=,; for i in $options; do if [ "${i:0:2}" != "x-" ]; then echo -n $i,; fi; done)"
+ # Prefix (lower|upper|work)dir with /mnt-root (overlayfs)
+ local optionsPrefixed="$( echo "$optionsFiltered" | sed -E 's#\<(lowerdir|upperdir|workdir)=#\1=/mnt-root#g' )"
+
+ echo "$device /mnt-root$mountPoint $fsType $optionsPrefixed" >> /etc/fstab
+
+ checkFS "$device" "$fsType"
+
+ # Optionally resize the filesystem.
+ case $options in
+ *x-nixos.autoresize*)
+ if [ "$fsType" = ext2 -o "$fsType" = ext3 -o "$fsType" = ext4 ]; then
+ echo "resizing $device..."
+ e2fsck -fp "$device"
+ resize2fs "$device"
+ elif [ "$fsType" = f2fs ]; then
+ echo "resizing $device..."
+ fsck.f2fs -fp "$device"
+ resize.f2fs "$device"
+ fi
+ ;;
+ esac
+
+ # Create backing directories for overlayfs
+ if [ "$fsType" = overlay ]; then
+ for i in upper work; do
+ dir="$( echo "$optionsPrefixed" | grep -o "${i}dir=[^,]*" )"
+ mkdir -m 0700 -p "${dir##*=}"
+ done
+ fi
+
+ echo "mounting $device on $mountPoint..."
+
+ mkdir -p "/mnt-root$mountPoint"
+
+ # For ZFS and CIFS mounts, retry a few times before giving up.
+ # We do this for ZFS as a workaround for issue NixOS/nixpkgs#25383.
+ local n=0
+ while true; do
+ mount "/mnt-root$mountPoint" && break
+ if [ \( "$fsType" != cifs -a "$fsType" != zfs \) -o "$n" -ge 10 ]; then fail; break; fi
+ echo "retrying..."
+ sleep 1
+ n=$((n + 1))
+ done
+
+ [ "$mountPoint" == "/" ] &&
+ [ -f "/mnt-root/etc/NIXOS_LUSTRATE" ] &&
+ lustrateRoot "/mnt-root"
+
+ true
+}
+
+lustrateRoot () {
+ local root="$1"
+
+ echo
+ echo -e "\e[1;33m<<< NixOS is now lustrating the root filesystem (cruft goes to /old-root) >>>\e[0m"
+ echo
+
+ mkdir -m 0755 -p "$root/old-root.tmp"
+
+ echo
+ echo "Moving impurities out of the way:"
+ for d in "$root"/*
+ do
+ [ "$d" == "$root/nix" ] && continue
+ [ "$d" == "$root/boot" ] && continue # Don't render the system unbootable
+ [ "$d" == "$root/old-root.tmp" ] && continue
+
+ mv -v "$d" "$root/old-root.tmp"
+ done
+
+ # Use .tmp to make sure subsequent invokations don't clash
+ mv -v "$root/old-root.tmp" "$root/old-root"
+
+ mkdir -m 0755 -p "$root/etc"
+ touch "$root/etc/NIXOS"
+
+ exec 4< "$root/old-root/etc/NIXOS_LUSTRATE"
+
+ echo
+ echo "Restoring selected impurities:"
+ while read -u 4 keeper; do
+ dirname="$(dirname "$keeper")"
+ mkdir -m 0755 -p "$root/$dirname"
+ cp -av "$root/old-root/$keeper" "$root/$keeper"
+ done
+
+ exec 4>&-
+}
+
+
+
+if test -e /sys/power/resume -a -e /sys/power/disk; then
+ if test -n "@resumeDevice@" && waitDevice "@resumeDevice@"; then
+ resumeDev="@resumeDevice@"
+ resumeInfo="$(udevadm info -q property "$resumeDev" )"
+ else
+ for sd in @resumeDevices@; do
+ # Try to detect resume device. According to Ubuntu bug:
+ # https://bugs.launchpad.net/ubuntu/+source/pm-utils/+bug/923326/comments/1
+ # when there are multiple swap devices, we can't know where the hibernate
+ # image will reside. We can check all of them for swsuspend blkid.
+ if waitDevice "$sd"; then
+ resumeInfo="$(udevadm info -q property "$sd")"
+ if [ "$(echo "$resumeInfo" | sed -n 's/^ID_FS_TYPE=//p')" = "swsuspend" ]; then
+ resumeDev="$sd"
+ break
+ fi
+ fi
+ done
+ fi
+ if test -n "$resumeDev"; then
+ resumeMajor="$(echo "$resumeInfo" | sed -n 's/^MAJOR=//p')"
+ resumeMinor="$(echo "$resumeInfo" | sed -n 's/^MINOR=//p')"
+ echo "$resumeMajor:$resumeMinor" > /sys/power/resume 2> /dev/null || echo "failed to resume..."
+ fi
+fi
+
+# If we have a path to an iso file, find the iso and link it to /dev/root
+if [ -n "$isoPath" ]; then
+ mkdir -p /findiso
+
+ for delay in 5 10; do
+ blkid | while read -r line; do
+ device=$(echo "$line" | sed 's/:.*//')
+ type=$(echo "$line" | sed 's/.*TYPE="\([^"]*\)".*/\1/')
+
+ mount -t "$type" "$device" /findiso
+ if [ -e "/findiso$isoPath" ]; then
+ ln -sf "/findiso$isoPath" /dev/root
+ break 2
+ else
+ umount /findiso
+ fi
+ done
+
+ sleep "$delay"
+ done
+fi
+
+# Try to find and mount the root device.
+mkdir -p $targetRoot
+
+exec 3< @fsInfo@
+
+while read -u 3 mountPoint; do
+ read -u 3 device
+ read -u 3 fsType
+ read -u 3 options
+
+ # !!! Really quick hack to support bind mounts, i.e., where the
+ # "device" should be taken relative to /mnt-root, not /. Assume
+ # that every device that starts with / but doesn't start with /dev
+ # is a bind mount.
+ pseudoDevice=
+ case $device in
+ /dev/*)
+ ;;
+ //*)
+ # Don't touch SMB/CIFS paths.
+ pseudoDevice=1
+ ;;
+ /*)
+ device=/mnt-root$device
+ ;;
+ *)
+ # Not an absolute path; assume that it's a pseudo-device
+ # like an NFS path (e.g. "server:/path").
+ pseudoDevice=1
+ ;;
+ esac
+
+ if test -z "$pseudoDevice" && ! waitDevice "$device"; then
+ # If it doesn't appear, try to mount it anyway (and
+ # probably fail). This is a fallback for non-device "devices"
+ # that we don't properly recognise.
+ echo "Timed out waiting for device $device, trying to mount anyway."
+ fi
+
+ # Wait once more for the udev queue to empty, just in case it's
+ # doing something with $device right now.
+ udevadm settle
+
+ # If copytoram is enabled: skip mounting the ISO and copy its content to a tmpfs.
+ if [ -n "$copytoram" ] && [ "$device" = /dev/root ] && [ "$mountPoint" = /iso ]; then
+ fsType=$(blkid -o value -s TYPE "$device")
+ fsSize=$(blockdev --getsize64 "$device")
+
+ mkdir -p /tmp-iso
+ mount -t "$fsType" /dev/root /tmp-iso
+ mountFS tmpfs /iso size="$fsSize" tmpfs
+
+ cp -r /tmp-iso/* /mnt-root/iso/
+
+ umount /tmp-iso
+ rmdir /tmp-iso
+ continue
+ fi
+
+ if [ "$mountPoint" = / ] && [ "$device" = tmpfs ] && [ ! -z "$persistence" ]; then
+ echo persistence...
+ waitDevice "$persistence"
+ echo enabling persistence...
+ mountFS "$persistence" "$mountPoint" "$persistence_opt" "auto"
+ continue
+ fi
+
+ mountFS "$device" "$mountPoint" "$options" "$fsType"
+done
+
+exec 3>&-
+
+
+@postMountCommands@
+
+
+# Emit a udev rule for /dev/root to prevent systemd from complaining.
+if [ -e /mnt-root/iso ]; then
+ eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=/mnt-root/iso)
+else
+ eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=$targetRoot)
+fi
+if [ "$ROOT_MAJOR" -a "$ROOT_MINOR" -a "$ROOT_MAJOR" != 0 ]; then
+ mkdir -p /run/udev/rules.d
+ echo 'ACTION=="add|change", SUBSYSTEM=="block", ENV{MAJOR}=="'$ROOT_MAJOR'", ENV{MINOR}=="'$ROOT_MINOR'", SYMLINK+="root"' > /run/udev/rules.d/61-dev-root-link.rules
+fi
+
+
+# Stop udevd.
+udevadm control --exit
+
+# Reset the logging file descriptors.
+# Do this just before pkill, which will kill the tee process.
+exec 1>&$logOutFd 2>&$logErrFd
+eval "exec $logOutFd>&- $logErrFd>&-"
+
+# Kill any remaining processes, just to be sure we're not taking any
+# with us into stage 2. But keep storage daemons like unionfs-fuse.
+#
+# Storage daemons are distinguished by an @ in front of their command line:
+# https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/
+for pid in $(pgrep -v -f '^@'); do
+ # Make sure we don't kill kernel processes, see #15226 and:
+ # http://stackoverflow.com/questions/12213445/identifying-kernel-threads
+ readlink "/proc/$pid/exe" &> /dev/null || continue
+ # Try to avoid killing ourselves.
+ [ $pid -eq $$ ] && continue
+ kill -9 "$pid"
+done
+
+if test -n "$debug1mounts"; then fail; fi
+
+
+# Restore /proc/sys/kernel/modprobe to its original value.
+echo /sbin/modprobe > /proc/sys/kernel/modprobe
+
+
+# Start stage 2. `switch_root' deletes all files in the ramfs on the
+# current root. Note that $stage2Init might be an absolute symlink,
+# in which case "-e" won't work because we're not in the chroot yet.
+if [ ! -e "$targetRoot/$stage2Init" ] && [ ! -L "$targetRoot/$stage2Init" ] ; then
+ echo "stage 2 init script ($targetRoot/$stage2Init) not found"
+ fail
+fi
+
+mkdir -m 0755 -p $targetRoot/proc $targetRoot/sys $targetRoot/dev $targetRoot/run
+
+mount --move /proc $targetRoot/proc
+mount --move /sys $targetRoot/sys
+mount --move /dev $targetRoot/dev
+mount --move /run $targetRoot/run
+
+exec env -i $(type -P switch_root) "$targetRoot" "$stage2Init"
+
+fail # should never be reached
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-1.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-1.nix
new file mode 100644
index 000000000000..6823e12847c2
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-1.nix
@@ -0,0 +1,619 @@
+# This module builds the initial ramdisk, which contains an init
+# script that performs the first stage of booting the system: it loads
+# the modules necessary to mount the root file system, then calls the
+# init in the root file system to start the second boot stage.
+
+{ config, lib, utils, pkgs, ... }:
+
+with lib;
+
+let
+
+ udev = config.systemd.package;
+
+ kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
+
+ modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; };
+ firmware = config.hardware.firmware;
+
+
+ # Determine the set of modules that we need to mount the root FS.
+ modulesClosure = pkgs.makeModulesClosure {
+ rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
+ kernel = modulesTree;
+ firmware = firmware;
+ allowMissing = true;
+ };
+
+
+ # The initrd only has to mount `/` or any FS marked as necessary for
+ # booting (such as the FS containing `/nix/store`, or an FS needed for
+ # mounting `/`, like `/` on a loopback).
+ fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
+
+ # A utility for enumerating the shared-library dependencies of a program
+ findLibs = pkgs.buildPackages.writeShellScriptBin "find-libs" ''
+ set -euo pipefail
+
+ declare -A seen
+ left=()
+
+ patchelf="${pkgs.buildPackages.patchelf}/bin/patchelf"
+
+ function add_needed {
+ rpath="$($patchelf --print-rpath $1)"
+ dir="$(dirname $1)"
+ for lib in $($patchelf --print-needed $1); do
+ left+=("$lib" "$rpath" "$dir")
+ done
+ }
+
+ add_needed "$1"
+
+ while [ ''${#left[@]} -ne 0 ]; do
+ next=''${left[0]}
+ rpath=''${left[1]}
+ ORIGIN=''${left[2]}
+ left=("''${left[@]:3}")
+ if [ -z ''${seen[$next]+x} ]; then
+ seen[$next]=1
+
+ # Ignore the dynamic linker which for some reason appears as a DT_NEEDED of glibc but isn't in glibc's RPATH.
+ case "$next" in
+ ld*.so.?) continue;;
+ esac
+
+ IFS=: read -ra paths <<< $rpath
+ res=
+ for path in "''${paths[@]}"; do
+ path=$(eval "echo $path")
+ if [ -f "$path/$next" ]; then
+ res="$path/$next"
+ echo "$res"
+ add_needed "$res"
+ break
+ fi
+ done
+ if [ -z "$res" ]; then
+ echo "Couldn't satisfy dependency $next" >&2
+ exit 1
+ fi
+ fi
+ done
+ '';
+
+ # Some additional utilities needed in stage 1, like mount, lvm, fsck
+ # etc. We don't want to bring in all of those packages, so we just
+ # copy what we need. Instead of using statically linked binaries,
+ # we just copy what we need from Glibc and use patchelf to make it
+ # work.
+ extraUtils = pkgs.runCommandCC "extra-utils"
+ { nativeBuildInputs = [pkgs.buildPackages.nukeReferences];
+ allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
+ }
+ ''
+ set +o pipefail
+
+ mkdir -p $out/bin $out/lib
+ ln -s $out/bin $out/sbin
+
+ copy_bin_and_libs () {
+ [ -f "$out/bin/$(basename $1)" ] && rm "$out/bin/$(basename $1)"
+ cp -pdv $1 $out/bin
+ }
+
+ # Copy BusyBox.
+ for BIN in ${pkgs.busybox}/{s,}bin/*; do
+ copy_bin_and_libs $BIN
+ done
+
+ # Copy some utillinux stuff.
+ copy_bin_and_libs ${pkgs.utillinux}/sbin/blkid
+
+ # Copy dmsetup and lvm.
+ copy_bin_and_libs ${getBin pkgs.lvm2}/bin/dmsetup
+ copy_bin_and_libs ${getBin pkgs.lvm2}/bin/lvm
+
+ # Add RAID mdadm tool.
+ copy_bin_and_libs ${pkgs.mdadm}/sbin/mdadm
+ copy_bin_and_libs ${pkgs.mdadm}/sbin/mdmon
+
+ # Copy udev.
+ copy_bin_and_libs ${udev}/bin/udevadm
+ copy_bin_and_libs ${udev}/lib/systemd/systemd-sysctl
+ for BIN in ${udev}/lib/udev/*_id; do
+ copy_bin_and_libs $BIN
+ done
+ # systemd-udevd is only a symlink to udevadm these days
+ ln -sf udevadm $out/bin/systemd-udevd
+
+ # Copy modprobe.
+ copy_bin_and_libs ${pkgs.kmod}/bin/kmod
+ ln -sf kmod $out/bin/modprobe
+
+ # Copy resize2fs if any ext* filesystems are to be resized
+ ${optionalString (any (fs: fs.autoResize && (lib.hasPrefix "ext" fs.fsType)) fileSystems) ''
+ # We need mke2fs in the initrd.
+ copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs
+ ''}
+
+ # Copy secrets if needed.
+ #
+ # TODO: move out to a separate script; see #85000.
+ ${optionalString (!config.boot.loader.supportsInitrdSecrets)
+ (concatStringsSep "\n" (mapAttrsToList (dest: source:
+ let source' = if source == null then dest else source; in
+ ''
+ mkdir -p $(dirname "$out/secrets/${dest}")
+ # Some programs (e.g. ssh) doesn't like secrets to be
+ # symlinks, so we use `cp -L` here to match the
+ # behaviour when secrets are natively supported.
+ cp -Lr ${source'} "$out/secrets/${dest}"
+ ''
+ ) config.boot.initrd.secrets))
+ }
+
+ ${config.boot.initrd.extraUtilsCommands}
+
+ # Copy ld manually since it isn't detected correctly
+ cp -pv ${pkgs.stdenv.cc.libc.out}/lib/ld*.so.? $out/lib
+
+ # Copy all of the needed libraries
+ find $out/bin $out/lib -type f | while read BIN; do
+ echo "Copying libs for executable $BIN"
+ for LIB in $(${findLibs}/bin/find-libs $BIN); do
+ TGT="$out/lib/$(basename $LIB)"
+ if [ ! -f "$TGT" ]; then
+ SRC="$(readlink -e $LIB)"
+ cp -pdv "$SRC" "$TGT"
+ fi
+ done
+ done
+
+ # Strip binaries further than normal.
+ chmod -R u+w $out
+ stripDirs "$STRIP" "lib bin" "-s"
+
+ # Run patchelf to make the programs refer to the copied libraries.
+ find $out/bin $out/lib -type f | while read i; do
+ if ! test -L $i; then
+ nuke-refs -e $out $i
+ fi
+ done
+
+ find $out/bin -type f | while read i; do
+ if ! test -L $i; then
+ echo "patching $i..."
+ patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true
+ fi
+ done
+
+ if [ -z "${toString (pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform)}" ]; then
+ # Make sure that the patchelf'ed binaries still work.
+ echo "testing patched programs..."
+ $out/bin/ash -c 'echo hello world' | grep "hello world"
+ export LD_LIBRARY_PATH=$out/lib
+ $out/bin/mount --help 2>&1 | grep -q "BusyBox"
+ $out/bin/blkid -V 2>&1 | grep -q 'libblkid'
+ $out/bin/udevadm --version
+ $out/bin/dmsetup --version 2>&1 | tee -a log | grep -q "version:"
+ LVM_SYSTEM_DIR=$out $out/bin/lvm version 2>&1 | tee -a log | grep -q "LVM"
+ $out/bin/mdadm --version
+
+ ${config.boot.initrd.extraUtilsCommandsTest}
+ fi
+ ''; # */
+
+
+ linkUnits = pkgs.runCommand "link-units" {
+ allowedReferences = [ extraUtils ];
+ preferLocalBuild = true;
+ } ''
+ mkdir -p $out
+ cp -v ${udev}/lib/systemd/network/*.link $out/
+ '';
+
+ udevRules = pkgs.runCommand "udev-rules" {
+ allowedReferences = [ extraUtils ];
+ preferLocalBuild = true;
+ } ''
+ mkdir -p $out
+
+ echo 'ENV{LD_LIBRARY_PATH}="${extraUtils}/lib"' > $out/00-env.rules
+
+ cp -v ${udev}/lib/udev/rules.d/60-cdrom_id.rules $out/
+ cp -v ${udev}/lib/udev/rules.d/60-persistent-storage.rules $out/
+ cp -v ${udev}/lib/udev/rules.d/75-net-description.rules $out/
+ cp -v ${udev}/lib/udev/rules.d/80-drivers.rules $out/
+ cp -v ${udev}/lib/udev/rules.d/80-net-setup-link.rules $out/
+ cp -v ${pkgs.lvm2}/lib/udev/rules.d/*.rules $out/
+ ${config.boot.initrd.extraUdevRulesCommands}
+
+ for i in $out/*.rules; do
+ substituteInPlace $i \
+ --replace ata_id ${extraUtils}/bin/ata_id \
+ --replace scsi_id ${extraUtils}/bin/scsi_id \
+ --replace cdrom_id ${extraUtils}/bin/cdrom_id \
+ --replace ${pkgs.coreutils}/bin/basename ${extraUtils}/bin/basename \
+ --replace ${pkgs.utillinux}/bin/blkid ${extraUtils}/bin/blkid \
+ --replace ${getBin pkgs.lvm2}/bin ${extraUtils}/bin \
+ --replace ${pkgs.mdadm}/sbin ${extraUtils}/sbin \
+ --replace ${pkgs.bash}/bin/sh ${extraUtils}/bin/sh \
+ --replace ${udev} ${extraUtils}
+ done
+
+ # Work around a bug in QEMU, which doesn't implement the "READ
+ # DISC INFORMATION" SCSI command:
+ # https://bugzilla.redhat.com/show_bug.cgi?id=609049
+ # As a result, `cdrom_id' doesn't print
+ # ID_CDROM_MEDIA_TRACK_COUNT_DATA, which in turn prevents the
+ # /dev/disk/by-label symlinks from being created. We need these
+ # in the NixOS installation CD, so use ID_CDROM_MEDIA in the
+ # corresponding udev rules for now. This was the behaviour in
+ # udev <= 154. See also
+ # http://www.spinics.net/lists/hotplug/msg03935.html
+ substituteInPlace $out/60-persistent-storage.rules \
+ --replace ID_CDROM_MEDIA_TRACK_COUNT_DATA ID_CDROM_MEDIA
+ ''; # */
+
+
+ # The init script of boot stage 1 (loading kernel modules for
+ # mounting the root FS).
+ bootStage1 = pkgs.substituteAll {
+ src = ./stage-1-init.sh;
+
+ shell = "${extraUtils}/bin/ash";
+
+ isExecutable = true;
+
+ postInstall = ''
+ echo checking syntax
+ # check both with bash
+ ${pkgs.buildPackages.bash}/bin/sh -n $target
+ # and with ash shell, just in case
+ ${pkgs.buildPackages.busybox}/bin/ash -n $target
+ '';
+
+ inherit linkUnits udevRules extraUtils modulesClosure;
+
+ inherit (config.boot) resumeDevice;
+
+ inherit (config.system.build) earlyMountScript;
+
+ inherit (config.boot.initrd) checkJournalingFS
+ preLVMCommands preDeviceCommands postDeviceCommands postMountCommands preFailCommands kernelModules;
+
+ resumeDevices = map (sd: if sd ? device then sd.device else "/dev/disk/by-label/${sd.label}")
+ (filter (sd: hasPrefix "/dev/" sd.device && !sd.randomEncryption.enable
+ # Don't include zram devices
+ && !(hasPrefix "/dev/zram" sd.device)
+ ) config.swapDevices);
+
+ fsInfo =
+ let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType (builtins.concatStringsSep "," fs.options) ];
+ in pkgs.writeText "initrd-fsinfo" (concatStringsSep "\n" (concatMap f fileSystems));
+
+ setHostId = optionalString (config.networking.hostId != null) ''
+ hi="${config.networking.hostId}"
+ ${if pkgs.stdenv.isBigEndian then ''
+ echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > /etc/hostid
+ '' else ''
+ echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > /etc/hostid
+ ''}
+ '';
+ };
+
+
+ # The closure of the init script of boot stage 1 is what we put in
+ # the initial RAM disk.
+ initialRamdisk = pkgs.makeInitrd {
+ name = "initrd-${kernel-name}";
+ inherit (config.boot.initrd) compressor prepend;
+
+ contents =
+ [ { object = bootStage1;
+ symlink = "/init";
+ }
+ { object = pkgs.writeText "mdadm.conf" config.boot.initrd.mdadmConf;
+ symlink = "/etc/mdadm.conf";
+ }
+ { object = pkgs.runCommand "initrd-kmod-blacklist-ubuntu" {
+ src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
+ preferLocalBuild = true;
+ } ''
+ target=$out
+ ${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
+ '';
+ symlink = "/etc/modprobe.d/ubuntu.conf";
+ }
+ { object = pkgs.kmod-debian-aliases;
+ symlink = "/etc/modprobe.d/debian.conf";
+ }
+ ];
+ };
+
+ # Script to add secret files to the initrd at bootloader update time
+ initialRamdiskSecretAppender =
+ pkgs.writeScriptBin "append-initrd-secrets"
+ ''
+ #!${pkgs.bash}/bin/bash -e
+ function usage {
+ echo "USAGE: $0 INITRD_FILE" >&2
+ echo "Appends this configuration's secrets to INITRD_FILE" >&2
+ }
+
+ if [ $# -ne 1 ]; then
+ usage
+ exit 1
+ fi
+
+ if [ "$1"x = "--helpx" ]; then
+ usage
+ exit 0
+ fi
+
+ ${lib.optionalString (config.boot.initrd.secrets == {})
+ "exit 0"}
+
+ export PATH=${pkgs.coreutils}/bin:${pkgs.cpio}/bin:${pkgs.gzip}/bin:${pkgs.findutils}/bin
+
+ function cleanup {
+ if [ -n "$tmp" -a -d "$tmp" ]; then
+ rm -fR "$tmp"
+ fi
+ }
+ trap cleanup EXIT
+
+ tmp=$(mktemp -d initrd-secrets.XXXXXXXXXX)
+
+ ${lib.concatStringsSep "\n" (mapAttrsToList (dest: source:
+ let source' = if source == null then dest else toString source; in
+ ''
+ mkdir -p $(dirname "$tmp/${dest}")
+ cp -a ${source'} "$tmp/${dest}"
+ ''
+ ) config.boot.initrd.secrets)
+ }
+
+ (cd "$tmp" && find . -print0 | sort -z | cpio -o -H newc -R +0:+0 --reproducible --null) | \
+ ${config.boot.initrd.compressor} >> "$1"
+ '';
+
+in
+
+{
+ options = {
+
+ boot.resumeDevice = mkOption {
+ type = types.str;
+ default = "";
+ example = "/dev/sda3";
+ description = ''
+ Device for manual resume attempt during boot. This should be used primarily
+ if you want to resume from file. If left empty, the swap partitions are used.
+ Specify here the device where the file resides.
+ You should also use <varname>boot.kernelParams</varname> to specify
+ <literal><replaceable>resume_offset</replaceable></literal>.
+ '';
+ };
+
+ boot.initrd.enable = mkOption {
+ type = types.bool;
+ default = !config.boot.isContainer;
+ defaultText = "!config.boot.isContainer";
+ description = ''
+ Whether to enable the NixOS initial RAM disk (initrd). This may be
+ needed to perform some initialisation tasks (like mounting
+ network/encrypted file systems) before continuing the boot process.
+ '';
+ };
+
+ boot.initrd.prepend = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ description = ''
+ Other initrd files to prepend to the final initrd we are building.
+ '';
+ };
+
+ boot.initrd.checkJournalingFS = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to run <command>fsck</command> on journaling filesystems such as ext3.
+ '';
+ };
+
+ boot.initrd.mdadmConf = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Contents of <filename>/etc/mdadm.conf</filename> in stage 1.
+ '';
+ };
+
+ boot.initrd.preLVMCommands = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Shell commands to be executed immediately before LVM discovery.
+ '';
+ };
+
+ boot.initrd.preDeviceCommands = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Shell commands to be executed before udev is started to create
+ device nodes.
+ '';
+ };
+
+ boot.initrd.postDeviceCommands = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Shell commands to be executed immediately after stage 1 of the
+ boot has loaded kernel modules and created device nodes in
+ <filename>/dev</filename>.
+ '';
+ };
+
+ boot.initrd.postMountCommands = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Shell commands to be executed immediately after the stage 1
+ filesystems have been mounted.
+ '';
+ };
+
+ boot.initrd.preFailCommands = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Shell commands to be executed before the failure prompt is shown.
+ '';
+ };
+
+ boot.initrd.extraUtilsCommands = mkOption {
+ internal = true;
+ default = "";
+ type = types.lines;
+ description = ''
+ Shell commands to be executed in the builder of the
+ extra-utils derivation. This can be used to provide
+ additional utilities in the initial ramdisk.
+ '';
+ };
+
+ boot.initrd.extraUtilsCommandsTest = mkOption {
+ internal = true;
+ default = "";
+ type = types.lines;
+ description = ''
+ Shell commands to be executed in the builder of the
+ extra-utils derivation after patchelf has done its
+ job. This can be used to test additional utilities
+ copied in extraUtilsCommands.
+ '';
+ };
+
+ boot.initrd.extraUdevRulesCommands = mkOption {
+ internal = true;
+ default = "";
+ type = types.lines;
+ description = ''
+ Shell commands to be executed in the builder of the
+ udev-rules derivation. This can be used to add
+ additional udev rules in the initial ramdisk.
+ '';
+ };
+
+ boot.initrd.compressor = mkOption {
+ internal = true;
+ default = "gzip -9n";
+ type = types.str;
+ description = "The compressor to use on the initrd image.";
+ example = "xz";
+ };
+
+ boot.initrd.secrets = mkOption
+ { default = {};
+ type = types.attrsOf (types.nullOr types.path);
+ description =
+ ''
+ Secrets to append to the initrd. The attribute name is the
+ path the secret should have inside the initrd, the value
+ is the path it should be copied from (or null for the same
+ path inside and out).
+ '';
+ example = literalExample
+ ''
+ { "/etc/dropbear/dropbear_rsa_host_key" =
+ ./secret-dropbear-key;
+ }
+ '';
+ };
+
+ boot.initrd.supportedFilesystems = mkOption {
+ default = [ ];
+ example = [ "btrfs" ];
+ type = types.listOf types.str;
+ description = "Names of supported filesystem types in the initial ramdisk.";
+ };
+
+ boot.loader.supportsInitrdSecrets = mkOption
+ { internal = true;
+ default = false;
+ type = types.bool;
+ description =
+ ''
+ Whether the bootloader setup runs append-initrd-secrets.
+ If not, any needed secrets must be copied into the initrd
+ and thus added to the store.
+ '';
+ };
+
+ fileSystems = mkOption {
+ type = with lib.types; attrsOf (submodule {
+ options.neededForBoot = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ If set, this file system will be mounted in the initial ramdisk.
+ Note that the file system will always be mounted in the initial
+ ramdisk if its mount point is one of the following:
+ ${concatStringsSep ", " (
+ forEach utils.pathsNeededForBoot (i: "<filename>${i}</filename>")
+ )}.
+ '';
+ };
+ });
+ };
+
+ };
+
+ config = mkIf config.boot.initrd.enable {
+ assertions = [
+ { assertion = any (fs: fs.mountPoint == "/") fileSystems;
+ message = "The ‘fileSystems’ option does not specify your root file system.";
+ }
+ { assertion = let inherit (config.boot) resumeDevice; in
+ resumeDevice == "" || builtins.substring 0 1 resumeDevice == "/";
+ message = "boot.resumeDevice has to be an absolute path."
+ + " Old \"x:y\" style is no longer supported.";
+ }
+ # TODO: remove when #85000 is fixed
+ { assertion = !config.boot.loader.supportsInitrdSecrets ->
+ all (source:
+ builtins.isPath source ||
+ (builtins.isString source && hasPrefix builtins.storeDir source))
+ (attrValues config.boot.initrd.secrets);
+ message = ''
+ boot.loader.initrd.secrets values must be unquoted paths when
+ using a bootloader that doesn't natively support initrd
+ secrets, e.g.:
+
+ boot.initrd.secrets = {
+ "/etc/secret" = /path/to/secret;
+ };
+
+ Note that this will result in all secrets being stored
+ world-readable in the Nix store!
+ '';
+ }
+ ];
+
+ system.build =
+ { inherit bootStage1 initialRamdisk initialRamdiskSecretAppender extraUtils; };
+
+ system.requiredKernelConfig = with config.lib.kernelConfig; [
+ (isYes "TMPFS")
+ (isYes "BLK_DEV_INITRD")
+ ];
+
+ boot.initrd.supportedFilesystems = map (fs: fs.fsType) fileSystems;
+
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-2-init.sh b/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-2-init.sh
new file mode 100644
index 000000000000..936077b9df1e
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-2-init.sh
@@ -0,0 +1,172 @@
+#! @shell@
+
+systemConfig=@systemConfig@
+
+export HOME=/root PATH="@path@"
+
+
+# Process the kernel command line.
+for o in $(</proc/cmdline); do
+ case $o in
+ boot.debugtrace)
+ # Show each command.
+ set -x
+ ;;
+ resume=*)
+ set -- $(IFS==; echo $o)
+ resumeDevice=$2
+ ;;
+ esac
+done
+
+
+# Print a greeting.
+echo
+echo -e "\e[1;32m<<< NixOS Stage 2 >>>\e[0m"
+echo
+
+
+# Normally, stage 1 mounts the root filesystem read/writable.
+# However, in some environments, stage 2 is executed directly, and the
+# root is read-only. So make it writable here.
+if [ -z "$container" ]; then
+ mount -n -o remount,rw none /
+fi
+
+
+# Likewise, stage 1 mounts /proc, /dev and /sys, so if we don't have a
+# stage 1, we need to do that here.
+if [ ! -e /proc/1 ]; then
+ specialMount() {
+ local device="$1"
+ local mountPoint="$2"
+ local options="$3"
+ local fsType="$4"
+
+ install -m 0755 -d "$mountPoint"
+ mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
+ }
+ source @earlyMountScript@
+fi
+
+
+echo "booting system configuration $systemConfig" > /dev/kmsg
+
+
+# Make /nix/store a read-only bind mount to enforce immutability of
+# the Nix store. Note that we can't use "chown root:nixbld" here
+# because users/groups might not exist yet.
+# Silence chown/chmod to fail gracefully on a readonly filesystem
+# like squashfs.
+chown -f 0:30000 /nix/store
+chmod -f 1775 /nix/store
+if [ -n "@readOnlyStore@" ]; then
+ if ! [[ "$(findmnt --noheadings --output OPTIONS /nix/store)" =~ ro(,|$) ]]; then
+ # FIXME when linux < 4.5 is EOL, switch to atomic bind mounts
+ #mount /nix/store /nix/store -o bind,remount,ro
+ mount --bind /nix/store /nix/store
+ mount -o remount,ro,bind /nix/store
+ fi
+fi
+
+
+# Provide a /etc/mtab.
+install -m 0755 -d /etc
+test -e /etc/fstab || touch /etc/fstab # to shut up mount
+rm -f /etc/mtab* # not that we care about stale locks
+ln -s /proc/mounts /etc/mtab
+
+
+# More special file systems, initialise required directories.
+[ -e /proc/bus/usb ] && mount -t usbfs usbfs /proc/bus/usb # UML doesn't have USB by default
+install -m 01777 -d /tmp
+install -m 0755 -d /var/{log,lib,db} /nix/var /etc/nixos/ \
+ /run/lock /home /bin # for the /bin/sh symlink
+
+
+# Miscellaneous boot time cleanup.
+rm -rf /var/run /var/lock
+rm -f /etc/{group,passwd,shadow}.lock
+
+
+# Also get rid of temporary GC roots.
+rm -rf /nix/var/nix/gcroots/tmp /nix/var/nix/temproots
+
+
+# For backwards compatibility, symlink /var/run to /run, and /var/lock
+# to /run/lock.
+ln -s /run /var/run
+ln -s /run/lock /var/lock
+
+
+# Clear the resume device.
+if test -n "$resumeDevice"; then
+ mkswap "$resumeDevice" || echo 'Failed to clear saved image.'
+fi
+
+
+# Use /etc/resolv.conf supplied by systemd-nspawn, if applicable.
+if [ -n "@useHostResolvConf@" ] && [ -e /etc/resolv.conf ]; then
+ resolvconf -m 1000 -a host </etc/resolv.conf
+fi
+
+# Log the script output to /dev/kmsg or /run/log/stage-2-init.log.
+# Only at this point are all the necessary prerequisites ready for these commands.
+exec {logOutFd}>&1 {logErrFd}>&2
+if test -w /dev/kmsg; then
+ exec > >(tee -i /proc/self/fd/"$logOutFd" | while read -r line; do
+ if test -n "$line"; then
+ echo "<7>stage-2-init: $line" > /dev/kmsg
+ fi
+ done) 2>&1
+else
+ mkdir -p /run/log
+ exec > >(tee -i /run/log/stage-2-init.log) 2>&1
+fi
+
+
+# Run the script that performs all configuration activation that does
+# not have to be done at boot time.
+echo "running activation script..."
+$systemConfig/activate
+
+
+# Restore the system time from the hardware clock. We do this after
+# running the activation script to be sure that /etc/localtime points
+# at the current time zone.
+if [ -e /dev/rtc ]; then
+ hwclock --hctosys
+fi
+
+
+# Record the boot configuration.
+ln -sfn "$systemConfig" /run/booted-system
+
+# Prevent the booted system from being garbage-collected. If it weren't
+# a gcroot, if we were running a different kernel, switched system,
+# and garbage collected all, we could not load kernel modules anymore.
+ln -sfn /run/booted-system /nix/var/nix/gcroots/booted-system
+
+
+# Run any user-specified commands.
+@shell@ @postBootCommands@
+
+
+# Ensure systemd doesn't try to populate /etc, by forcing its first-boot
+# heuristic off. It doesn't matter what's in /etc/machine-id for this purpose,
+# and systemd will immediately fill in the file when it starts, so just
+# creating it is enough. This `: >>` pattern avoids forking and avoids changing
+# the mtime if the file already exists.
+: >> /etc/machine-id
+
+
+# Reset the logging file descriptors.
+exec 1>&$logOutFd 2>&$logErrFd
+exec {logOutFd}>&- {logErrFd}>&-
+
+
+# Start systemd.
+echo "starting systemd..."
+PATH=/run/current-system/systemd/lib/systemd:@fsPackagesPath@ \
+ LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive \
+ exec @systemdExecutable@
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-2.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-2.nix
new file mode 100644
index 000000000000..dd6d83ee0094
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/stage-2.nix
@@ -0,0 +1,95 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ useHostResolvConf = config.networking.resolvconf.enable && config.networking.useHostResolvConf;
+
+ bootStage2 = pkgs.substituteAll {
+ src = ./stage-2-init.sh;
+ shellDebug = "${pkgs.bashInteractive}/bin/bash";
+ shell = "${pkgs.bash}/bin/bash";
+ inherit (config.boot) systemdExecutable;
+ isExecutable = true;
+ inherit (config.nix) readOnlyStore;
+ inherit useHostResolvConf;
+ inherit (config.system.build) earlyMountScript;
+ path = lib.makeBinPath ([
+ pkgs.coreutils
+ pkgs.utillinux
+ ] ++ lib.optional useHostResolvConf pkgs.openresolv);
+ fsPackagesPath = lib.makeBinPath config.system.fsPackages;
+ postBootCommands = pkgs.writeText "local-cmds"
+ ''
+ ${config.boot.postBootCommands}
+ ${config.powerManagement.powerUpCommands}
+ '';
+ };
+
+in
+
+{
+ options = {
+
+ boot = {
+
+ postBootCommands = mkOption {
+ default = "";
+ example = "rm -f /var/log/messages";
+ type = types.lines;
+ description = ''
+ Shell commands to be executed just before systemd is started.
+ '';
+ };
+
+ devSize = mkOption {
+ default = "5%";
+ example = "32m";
+ type = types.str;
+ description = ''
+ Size limit for the /dev tmpfs. Look at mount(8), tmpfs size option,
+ for the accepted syntax.
+ '';
+ };
+
+ devShmSize = mkOption {
+ default = "50%";
+ example = "256m";
+ type = types.str;
+ description = ''
+ Size limit for the /dev/shm tmpfs. Look at mount(8), tmpfs size option,
+ for the accepted syntax.
+ '';
+ };
+
+ runSize = mkOption {
+ default = "25%";
+ example = "256m";
+ type = types.str;
+ description = ''
+ Size limit for the /run tmpfs. Look at mount(8), tmpfs size option,
+ for the accepted syntax.
+ '';
+ };
+
+ systemdExecutable = mkOption {
+ default = "systemd";
+ type = types.str;
+ description = ''
+ The program to execute to start systemd. Typically
+ <literal>systemd</literal>, which will find systemd in the
+ PATH.
+ '';
+ };
+ };
+
+ };
+
+
+ config = {
+
+ system.build.bootStage2 = bootStage2;
+
+ };
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-lib.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-lib.nix
new file mode 100644
index 000000000000..fa109394fedb
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-lib.nix
@@ -0,0 +1,235 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+ cfg = config.systemd;
+ lndir = "${pkgs.xorg.lndir}/bin/lndir";
+in rec {
+
+ shellEscape = s: (replaceChars [ "\\" ] [ "\\\\" ] s);
+
+ mkPathSafeName = lib.replaceChars ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""];
+
+ makeUnit = name: unit:
+ if unit.enable then
+ pkgs.runCommand "unit-${mkPathSafeName name}"
+ { preferLocalBuild = true;
+ allowSubstitutes = false;
+ inherit (unit) text;
+ }
+ ''
+ mkdir -p $out
+ echo -n "$text" > $out/${shellEscape name}
+ ''
+ else
+ pkgs.runCommand "unit-${mkPathSafeName name}-disabled"
+ { preferLocalBuild = true;
+ allowSubstitutes = false;
+ }
+ ''
+ mkdir -p $out
+ ln -s /dev/null $out/${shellEscape name}
+ '';
+
+ boolValues = [true false "yes" "no"];
+
+ digits = map toString (range 0 9);
+
+ isByteFormat = s:
+ let
+ l = reverseList (stringToCharacters s);
+ suffix = head l;
+ nums = tail l;
+ in elem suffix (["K" "M" "G" "T"] ++ digits)
+ && all (num: elem num digits) nums;
+
+ assertByteFormat = name: group: attr:
+ optional (attr ? ${name} && ! isByteFormat attr.${name})
+ "Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT].";
+
+ hexChars = stringToCharacters "0123456789abcdefABCDEF";
+
+ isMacAddress = s: stringLength s == 17
+ && flip all (splitString ":" s) (bytes:
+ all (byte: elem byte hexChars) (stringToCharacters bytes)
+ );
+
+ assertMacAddress = name: group: attr:
+ optional (attr ? ${name} && ! isMacAddress attr.${name})
+ "Systemd ${group} field `${name}' must be a valid mac address.";
+
+ isPort = i: i >= 0 && i <= 65535;
+
+ assertPort = name: group: attr:
+ optional (attr ? ${name} && ! isPort attr.${name})
+ "Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number.";
+
+ assertValueOneOf = name: values: group: attr:
+ optional (attr ? ${name} && !elem attr.${name} values)
+ "Systemd ${group} field `${name}' cannot have value `${toString attr.${name}}'.";
+
+ assertHasField = name: group: attr:
+ optional (!(attr ? ${name}))
+ "Systemd ${group} field `${name}' must exist.";
+
+ assertRange = name: min: max: group: attr:
+ optional (attr ? ${name} && !(min <= attr.${name} && max >= attr.${name}))
+ "Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]";
+
+ assertMinimum = name: min: group: attr:
+ optional (attr ? ${name} && attr.${name} < min)
+ "Systemd ${group} field `${name}' must be greater than or equal to ${toString min}";
+
+ assertOnlyFields = fields: group: attr:
+ let badFields = filter (name: ! elem name fields) (attrNames attr); in
+ optional (badFields != [ ])
+ "Systemd ${group} has extra fields [${concatStringsSep " " badFields}].";
+
+ assertInt = name: group: attr:
+ optional (attr ? ${name} && !isInt attr.${name})
+ "Systemd ${group} field `${name}' is not an integer";
+
+ checkUnitConfig = group: checks: attrs: let
+ # We're applied at the top-level type (attrsOf unitOption), so the actual
+ # unit options might contain attributes from mkOverride that we need to
+ # convert into single values before checking them.
+ defs = mapAttrs (const (v:
+ if v._type or "" == "override" then v.content else v
+ )) attrs;
+ errors = concatMap (c: c group defs) checks;
+ in if errors == [] then true
+ else builtins.trace (concatStringsSep "\n" errors) false;
+
+ toOption = x:
+ if x == true then "true"
+ else if x == false then "false"
+ else toString x;
+
+ attrsToSection = as:
+ concatStrings (concatLists (mapAttrsToList (name: value:
+ map (x: ''
+ ${name}=${toOption x}
+ '')
+ (if isList value then value else [value]))
+ as));
+
+ generateUnits = generateUnits' true;
+
+ generateUnits' = allowCollisions: type: units: upstreamUnits: upstreamWants:
+ pkgs.runCommand "${type}-units"
+ { preferLocalBuild = true;
+ allowSubstitutes = false;
+ } ''
+ mkdir -p $out
+
+ # Copy the upstream systemd units we're interested in.
+ for i in ${toString upstreamUnits}; do
+ fn=${cfg.package}/example/systemd/${type}/$i
+ if ! [ -e $fn ]; then echo "missing $fn"; false; fi
+ if [ -L $fn ]; then
+ target="$(readlink "$fn")"
+ if [ ''${target:0:3} = ../ ]; then
+ ln -s "$(readlink -f "$fn")" $out/
+ else
+ cp -pd $fn $out/
+ fi
+ else
+ ln -s $fn $out/
+ fi
+ done
+
+ # Copy .wants links, but only those that point to units that
+ # we're interested in.
+ for i in ${toString upstreamWants}; do
+ fn=${cfg.package}/example/systemd/${type}/$i
+ if ! [ -e $fn ]; then echo "missing $fn"; false; fi
+ x=$out/$(basename $fn)
+ mkdir $x
+ for i in $fn/*; do
+ y=$x/$(basename $i)
+ cp -pd $i $y
+ if ! [ -e $y ]; then rm $y; fi
+ done
+ done
+
+ # Symlink all units provided listed in systemd.packages.
+ packages="${toString cfg.packages}"
+
+ # Filter duplicate directories
+ declare -A unique_packages
+ for k in $packages ; do unique_packages[$k]=1 ; done
+
+ for i in ''${!unique_packages[@]}; do
+ for fn in $i/etc/systemd/${type}/* $i/lib/systemd/${type}/*; do
+ if ! [[ "$fn" =~ .wants$ ]]; then
+ if [[ -d "$fn" ]]; then
+ targetDir="$out/$(basename "$fn")"
+ mkdir -p "$targetDir"
+ ${lndir} "$fn" "$targetDir"
+ else
+ ln -s $fn $out/
+ fi
+ fi
+ done
+ done
+
+ # Symlink all units defined by systemd.units. If these are also
+ # provided by systemd or systemd.packages, then add them as
+ # <unit-name>.d/overrides.conf, which makes them extend the
+ # upstream unit.
+ for i in ${toString (mapAttrsToList (n: v: v.unit) units)}; do
+ fn=$(basename $i/*)
+ if [ -e $out/$fn ]; then
+ if [ "$(readlink -f $i/$fn)" = /dev/null ]; then
+ ln -sfn /dev/null $out/$fn
+ else
+ ${if allowCollisions then ''
+ mkdir -p $out/$fn.d
+ ln -s $i/$fn $out/$fn.d/overrides.conf
+ '' else ''
+ echo "Found multiple derivations configuring $fn!"
+ exit 1
+ ''}
+ fi
+ else
+ ln -fs $i/$fn $out/
+ fi
+ done
+
+ # Create service aliases from aliases option.
+ ${concatStrings (mapAttrsToList (name: unit:
+ concatMapStrings (name2: ''
+ ln -sfn '${name}' $out/'${name2}'
+ '') unit.aliases) units)}
+
+ # Create .wants and .requires symlinks from the wantedBy and
+ # requiredBy options.
+ ${concatStrings (mapAttrsToList (name: unit:
+ concatMapStrings (name2: ''
+ mkdir -p $out/'${name2}.wants'
+ ln -sfn '../${name}' $out/'${name2}.wants'/
+ '') unit.wantedBy) units)}
+
+ ${concatStrings (mapAttrsToList (name: unit:
+ concatMapStrings (name2: ''
+ mkdir -p $out/'${name2}.requires'
+ ln -sfn '../${name}' $out/'${name2}.requires'/
+ '') unit.requiredBy) units)}
+
+ ${optionalString (type == "system") ''
+ # Stupid misc. symlinks.
+ ln -s ${cfg.defaultUnit} $out/default.target
+ ln -s ${cfg.ctrlAltDelUnit} $out/ctrl-alt-del.target
+ ln -s rescue.target $out/kbrequest.target
+
+ mkdir -p $out/getty.target.wants/
+ ln -s ../autovt@tty1.service $out/getty.target.wants/
+
+ ln -s ../local-fs.target ../remote-fs.target \
+ ../nss-lookup.target ../nss-user-lookup.target ../swap.target \
+ $out/multi-user.target.wants/
+ ''}
+ ''; # */
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-nspawn.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-nspawn.nix
new file mode 100644
index 000000000000..b450d77429b2
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-nspawn.nix
@@ -0,0 +1,133 @@
+{ config, lib , pkgs, ...}:
+
+with lib;
+with import ./systemd-unit-options.nix { inherit config lib; };
+with import ./systemd-lib.nix { inherit config lib pkgs; };
+
+let
+ cfg = config.systemd.nspawn;
+
+ checkExec = checkUnitConfig "Exec" [
+ (assertOnlyFields [
+ "Boot" "ProcessTwo" "Parameters" "Environment" "User" "WorkingDirectory"
+ "PivotRoot" "Capability" "DropCapability" "NoNewPrivileges" "KillSignal"
+ "Personality" "MachineId" "PrivateUsers" "NotifyReady" "SystemCallFilter"
+ "LimitCPU" "LimitFSIZE" "LimitDATA" "LimitSTACK" "LimitCORE" "LimitRSS"
+ "LimitNOFILE" "LimitAS" "LimitNPROC" "LimitMEMLOCK" "LimitLOCKS"
+ "LimitSIGPENDING" "LimitMSGQUEUE" "LimitNICE" "LimitRTPRIO" "LimitRTTIME"
+ "OOMScoreAdjust" "CPUAffinity" "Hostname" "ResolvConf" "Timezone"
+ "LinkJournal"
+ ])
+ (assertValueOneOf "Boot" boolValues)
+ (assertValueOneOf "ProcessTwo" boolValues)
+ (assertValueOneOf "NotifyReady" boolValues)
+ ];
+
+ checkFiles = checkUnitConfig "Files" [
+ (assertOnlyFields [
+ "ReadOnly" "Volatile" "Bind" "BindReadOnly" "TemporaryFileSystem"
+ "Overlay" "OverlayReadOnly" "PrivateUsersChown"
+ ])
+ (assertValueOneOf "ReadOnly" boolValues)
+ (assertValueOneOf "Volatile" (boolValues ++ [ "state" ]))
+ (assertValueOneOf "PrivateUsersChown" boolValues)
+ ];
+
+ checkNetwork = checkUnitConfig "Network" [
+ (assertOnlyFields [
+ "Private" "VirtualEthernet" "VirtualEthernetExtra" "Interface" "MACVLAN"
+ "IPVLAN" "Bridge" "Zone" "Port"
+ ])
+ (assertValueOneOf "Private" boolValues)
+ (assertValueOneOf "VirtualEthernet" boolValues)
+ ];
+
+ instanceOptions = {
+ options = sharedOptions // {
+ execConfig = mkOption {
+ default = {};
+ example = { Parameters = "/bin/sh"; };
+ type = types.addCheck (types.attrsOf unitOption) checkExec;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Exec]</literal> section of this unit. See
+ <citerefentry><refentrytitle>systemd.nspawn</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ filesConfig = mkOption {
+ default = {};
+ example = { Bind = [ "/home/alice" ]; };
+ type = types.addCheck (types.attrsOf unitOption) checkFiles;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Files]</literal> section of this unit. See
+ <citerefentry><refentrytitle>systemd.nspawn</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ networkConfig = mkOption {
+ default = {};
+ example = { Private = false; };
+ type = types.addCheck (types.attrsOf unitOption) checkNetwork;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Network]</literal> section of this unit. See
+ <citerefentry><refentrytitle>systemd.nspawn</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+ };
+
+ };
+
+ instanceToUnit = name: def:
+ let base = {
+ text = ''
+ [Exec]
+ ${attrsToSection def.execConfig}
+
+ [Files]
+ ${attrsToSection def.filesConfig}
+
+ [Network]
+ ${attrsToSection def.networkConfig}
+ '';
+ } // def;
+ in base // { unit = makeUnit name base; };
+
+in {
+
+ options = {
+
+ systemd.nspawn = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule instanceOptions);
+ description = "Definition of systemd-nspawn configurations.";
+ };
+
+ };
+
+ config =
+ let
+ units = mapAttrs' (n: v: let nspawnFile = "${n}.nspawn"; in nameValuePair nspawnFile (instanceToUnit nspawnFile v)) cfg;
+ in
+ mkMerge [
+ (mkIf (cfg != {}) {
+ environment.etc."systemd/nspawn".source = mkIf (cfg != {}) (generateUnits' false "nspawn" units [] []);
+ })
+ {
+ systemd.targets.multi-user.wants = [ "machines.target" ];
+
+ # Workaround for https://github.com/NixOS/nixpkgs/pull/67232#issuecomment-531315437 and https://github.com/systemd/systemd/issues/13622
+ # Once systemd fixes this upstream, we can re-enable -U
+ systemd.services."systemd-nspawn@".serviceConfig.ExecStart = [
+ "" # deliberately empty. signals systemd to override the ExecStart
+ # Only difference between upstream is that we do not pass the -U flag
+ "${config.systemd.package}/bin/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth --settings=override --machine=%i"
+ ];
+ }
+ ];
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-unit-options.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-unit-options.nix
new file mode 100644
index 000000000000..5addc6f9ca44
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd-unit-options.nix
@@ -0,0 +1,528 @@
+{ config, lib }:
+
+with lib;
+with import ./systemd-lib.nix { inherit config lib pkgs; };
+
+let
+ checkService = checkUnitConfig "Service" [
+ (assertValueOneOf "Type" [
+ "exec" "simple" "forking" "oneshot" "dbus" "notify" "idle"
+ ])
+ (assertValueOneOf "Restart" [
+ "no" "on-success" "on-failure" "on-abnormal" "on-abort" "always"
+ ])
+ ];
+
+in rec {
+
+ unitOption = mkOptionType {
+ name = "systemd option";
+ merge = loc: defs:
+ let
+ defs' = filterOverrides defs;
+ defs'' = getValues defs';
+ in
+ if isList (head defs'')
+ then concatLists defs''
+ else mergeEqualOption loc defs';
+ };
+
+ sharedOptions = {
+
+ enable = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ If set to false, this unit will be a symlink to
+ /dev/null. This is primarily useful to prevent specific
+ template instances
+ (e.g. <literal>serial-getty@ttyS0</literal>) from being
+ started. Note that <literal>enable=true</literal> does not
+ make a unit start by default at boot; if you want that, see
+ <literal>wantedBy</literal>.
+ '';
+ };
+
+ requiredBy = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ Units that require (i.e. depend on and need to go down with)
+ this unit. The discussion under <literal>wantedBy</literal>
+ applies here as well: inverse <literal>.requires</literal>
+ symlinks are established.
+ '';
+ };
+
+ wantedBy = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ Units that want (i.e. depend on) this unit. The standard way
+ to make a unit start by default at boot is to set this option
+ to <literal>[ "multi-user.target" ]</literal>. That's despite
+ the fact that the systemd.unit(5) manpage says this option
+ goes in the <literal>[Install]</literal> section that controls
+ the behaviour of <literal>systemctl enable</literal>. Since
+ such a process is stateful and thus contrary to the design of
+ NixOS, setting this option instead causes the equivalent
+ inverse <literal>.wants</literal> symlink to be present,
+ establishing the same desired relationship in a stateless way.
+ '';
+ };
+
+ aliases = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = "Aliases of that unit.";
+ };
+
+ };
+
+ concreteUnitOptions = sharedOptions // {
+
+ text = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "Text of this systemd unit.";
+ };
+
+ unit = mkOption {
+ internal = true;
+ description = "The generated unit.";
+ };
+
+ };
+
+ commonUnitOptions = sharedOptions // {
+
+ description = mkOption {
+ default = "";
+ type = types.str;
+ description = "Description of this unit used in systemd messages and progress indicators.";
+ };
+
+ documentation = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = "A list of URIs referencing documentation for this unit or its configuration.";
+ };
+
+ requires = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ Start the specified units when this unit is started, and stop
+ this unit when the specified units are stopped or fail.
+ '';
+ };
+
+ wants = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ Start the specified units when this unit is started.
+ '';
+ };
+
+ after = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ If the specified units are started at the same time as
+ this unit, delay this unit until they have started.
+ '';
+ };
+
+ before = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ If the specified units are started at the same time as
+ this unit, delay them until this unit has started.
+ '';
+ };
+
+ bindsTo = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ Like ‘requires’, but in addition, if the specified units
+ unexpectedly disappear, this unit will be stopped as well.
+ '';
+ };
+
+ partOf = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ If the specified units are stopped or restarted, then this
+ unit is stopped or restarted as well.
+ '';
+ };
+
+ conflicts = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ If the specified units are started, then this unit is stopped
+ and vice versa.
+ '';
+ };
+
+ requisite = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ Similar to requires. However if the units listed are not started,
+ they will not be started and the transaction will fail.
+ '';
+ };
+
+ unitConfig = mkOption {
+ default = {};
+ example = { RequiresMountsFor = "/data"; };
+ type = types.attrsOf unitOption;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Unit]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ restartTriggers = mkOption {
+ default = [];
+ type = types.listOf types.unspecified;
+ description = ''
+ An arbitrary list of items such as derivations. If any item
+ in the list changes between reconfigurations, the service will
+ be restarted.
+ '';
+ };
+
+ onFailure = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ description = ''
+ A list of one or more units that are activated when
+ this unit enters the "failed" state.
+ '';
+ };
+
+ startLimitIntervalSec = mkOption {
+ type = types.int;
+ description = ''
+ Configure unit start rate limiting. Units which are started
+ more than burst times within an interval time interval are
+ not permitted to start any more.
+ '';
+ };
+
+ };
+
+
+ serviceOptions = commonUnitOptions // {
+
+ environment = mkOption {
+ default = {};
+ type = with types; attrsOf (nullOr (oneOf [ str path package ]));
+ example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
+ description = "Environment variables passed to the service's processes.";
+ };
+
+ path = mkOption {
+ default = [];
+ type = with types; listOf (oneOf [ package str ]);
+ description = ''
+ Packages added to the service's <envar>PATH</envar>
+ environment variable. Both the <filename>bin</filename>
+ and <filename>sbin</filename> subdirectories of each
+ package are added.
+ '';
+ };
+
+ serviceConfig = mkOption {
+ default = {};
+ example =
+ { StartLimitInterval = 10;
+ RestartSec = 5;
+ };
+ type = types.addCheck (types.attrsOf unitOption) checkService;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Service]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.service</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ script = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Shell commands executed as the service's main process.";
+ };
+
+ scriptArgs = mkOption {
+ type = types.str;
+ default = "";
+ description = "Arguments passed to the main process script.";
+ };
+
+ preStart = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Shell commands executed before the service's main process
+ is started.
+ '';
+ };
+
+ postStart = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Shell commands executed after the service's main process
+ is started.
+ '';
+ };
+
+ reload = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Shell commands executed when the service's main process
+ is reloaded.
+ '';
+ };
+
+ preStop = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Shell commands executed to stop the service.
+ '';
+ };
+
+ postStop = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Shell commands executed after the service's main process
+ has exited.
+ '';
+ };
+
+ restartIfChanged = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether the service should be restarted during a NixOS
+ configuration switch if its definition has changed.
+ '';
+ };
+
+ reloadIfChanged = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether the service should be reloaded during a NixOS
+ configuration switch if its definition has changed. If
+ enabled, the value of <option>restartIfChanged</option> is
+ ignored.
+ '';
+ };
+
+ stopIfChanged = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ If set, a changed unit is restarted by calling
+ <command>systemctl stop</command> in the old configuration,
+ then <command>systemctl start</command> in the new one.
+ Otherwise, it is restarted in a single step using
+ <command>systemctl restart</command> in the new configuration.
+ The latter is less correct because it runs the
+ <literal>ExecStop</literal> commands from the new
+ configuration.
+ '';
+ };
+
+ startAt = mkOption {
+ type = with types; either str (listOf str);
+ default = [];
+ example = "Sun 14:00:00";
+ description = ''
+ Automatically start this unit at the given date/time, which
+ must be in the format described in
+ <citerefentry><refentrytitle>systemd.time</refentrytitle>
+ <manvolnum>7</manvolnum></citerefentry>. This is equivalent
+ to adding a corresponding timer unit with
+ <option>OnCalendar</option> set to the value given here.
+ '';
+ apply = v: if isList v then v else [ v ];
+ };
+
+ };
+
+
+ socketOptions = commonUnitOptions // {
+
+ listenStreams = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ example = [ "0.0.0.0:993" "/run/my-socket" ];
+ description = ''
+ For each item in this list, a <literal>ListenStream</literal>
+ option in the <literal>[Socket]</literal> section will be created.
+ '';
+ };
+
+ listenDatagrams = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ example = [ "0.0.0.0:993" "/run/my-socket" ];
+ description = ''
+ For each item in this list, a <literal>ListenDatagram</literal>
+ option in the <literal>[Socket]</literal> section will be created.
+ '';
+ };
+
+ socketConfig = mkOption {
+ default = {};
+ example = { ListenStream = "/run/my-socket"; };
+ type = types.attrsOf unitOption;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Socket]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.socket</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ };
+
+
+ timerOptions = commonUnitOptions // {
+
+ timerConfig = mkOption {
+ default = {};
+ example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; };
+ type = types.attrsOf unitOption;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Timer]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.timer</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> and
+ <citerefentry><refentrytitle>systemd.time</refentrytitle>
+ <manvolnum>7</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ };
+
+
+ pathOptions = commonUnitOptions // {
+
+ pathConfig = mkOption {
+ default = {};
+ example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
+ type = types.attrsOf unitOption;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Path]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.path</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ };
+
+
+ mountOptions = commonUnitOptions // {
+
+ what = mkOption {
+ example = "/dev/sda1";
+ type = types.str;
+ description = "Absolute path of device node, file or other resource. (Mandatory)";
+ };
+
+ where = mkOption {
+ example = "/mnt";
+ type = types.str;
+ description = ''
+ Absolute path of a directory of the mount point.
+ Will be created if it doesn't exist. (Mandatory)
+ '';
+ };
+
+ type = mkOption {
+ default = "";
+ example = "ext4";
+ type = types.str;
+ description = "File system type.";
+ };
+
+ options = mkOption {
+ default = "";
+ example = "noatime";
+ type = types.commas;
+ description = "Options used to mount the file system.";
+ };
+
+ mountConfig = mkOption {
+ default = {};
+ example = { DirectoryMode = "0775"; };
+ type = types.attrsOf unitOption;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Mount]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.mount</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+ };
+
+ automountOptions = commonUnitOptions // {
+
+ where = mkOption {
+ example = "/mnt";
+ type = types.str;
+ description = ''
+ Absolute path of a directory of the mount point.
+ Will be created if it doesn't exist. (Mandatory)
+ '';
+ };
+
+ automountConfig = mkOption {
+ default = {};
+ example = { DirectoryMode = "0775"; };
+ type = types.attrsOf unitOption;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Automount]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.automount</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+ };
+
+ targetOptions = commonUnitOptions;
+
+ sliceOptions = commonUnitOptions // {
+
+ sliceConfig = mkOption {
+ default = {};
+ example = { MemoryMax = "2G"; };
+ type = types.attrsOf unitOption;
+ description = ''
+ Each attribute in this set specifies an option in the
+ <literal>[Slice]</literal> section of the unit. See
+ <citerefentry><refentrytitle>systemd.slice</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ '';
+ };
+
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd.nix
new file mode 100644
index 000000000000..74d6957678f5
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/systemd.nix
@@ -0,0 +1,1178 @@
+{ config, lib, pkgs, utils, ... }:
+
+with utils;
+with lib;
+with import ./systemd-unit-options.nix { inherit config lib; };
+with import ./systemd-lib.nix { inherit config lib pkgs; };
+
+let
+
+ cfg = config.systemd;
+
+ systemd = cfg.package;
+
+ upstreamSystemUnits =
+ [ # Targets.
+ "basic.target"
+ "sysinit.target"
+ "sockets.target"
+ "exit.target"
+ "graphical.target"
+ "multi-user.target"
+ "network.target"
+ "network-pre.target"
+ "network-online.target"
+ "nss-lookup.target"
+ "nss-user-lookup.target"
+ "time-sync.target"
+ "cryptsetup.target"
+ "sigpwr.target"
+ "timers.target"
+ "paths.target"
+ "rpcbind.target"
+
+ # Rescue mode.
+ "rescue.target"
+ "rescue.service"
+
+ # Udev.
+ "systemd-udevd-control.socket"
+ "systemd-udevd-kernel.socket"
+ "systemd-udevd.service"
+ "systemd-udev-settle.service"
+ "systemd-udev-trigger.service"
+ # hwdb.bin is managed by NixOS
+ # "systemd-hwdb-update.service"
+
+ # Consoles.
+ "getty.target"
+ "getty-pre.target"
+ "getty@.service"
+ "serial-getty@.service"
+ "console-getty.service"
+ "container-getty@.service"
+ "systemd-vconsole-setup.service"
+
+ # Hardware (started by udev when a relevant device is plugged in).
+ "sound.target"
+ "bluetooth.target"
+ "printer.target"
+ "smartcard.target"
+
+ # Login stuff.
+ "systemd-logind.service"
+ "autovt@.service"
+ "systemd-user-sessions.service"
+ "dbus-org.freedesktop.import1.service"
+ "dbus-org.freedesktop.machine1.service"
+ "user@.service"
+ "user-runtime-dir@.service"
+
+ # Journal.
+ "systemd-journald.socket"
+ "systemd-journald.service"
+ "systemd-journal-flush.service"
+ "systemd-journal-catalog-update.service"
+ ] ++ (optional (!config.boot.isContainer) "systemd-journald-audit.socket") ++ [
+ "systemd-journald-dev-log.socket"
+ "syslog.socket"
+
+ # Coredumps.
+ "systemd-coredump.socket"
+ "systemd-coredump@.service"
+
+ # Kernel module loading.
+ "systemd-modules-load.service"
+ "kmod-static-nodes.service"
+
+ # Filesystems.
+ "systemd-fsck@.service"
+ "systemd-fsck-root.service"
+ "systemd-remount-fs.service"
+ "local-fs.target"
+ "local-fs-pre.target"
+ "remote-fs.target"
+ "remote-fs-pre.target"
+ "swap.target"
+ "dev-hugepages.mount"
+ "dev-mqueue.mount"
+ "sys-fs-fuse-connections.mount"
+ ] ++ (optional (!config.boot.isContainer) "sys-kernel-config.mount") ++ [
+ "sys-kernel-debug.mount"
+
+ # Maintaining state across reboots.
+ "systemd-random-seed.service"
+ "systemd-backlight@.service"
+ "systemd-rfkill.service"
+ "systemd-rfkill.socket"
+
+ # Hibernate / suspend.
+ "hibernate.target"
+ "suspend.target"
+ "suspend-then-hibernate.target"
+ "sleep.target"
+ "hybrid-sleep.target"
+ "systemd-hibernate.service"
+ "systemd-hybrid-sleep.service"
+ "systemd-suspend.service"
+ "systemd-suspend-then-hibernate.service"
+
+ # Reboot stuff.
+ "reboot.target"
+ "systemd-reboot.service"
+ "poweroff.target"
+ "systemd-poweroff.service"
+ "halt.target"
+ "systemd-halt.service"
+ "shutdown.target"
+ "umount.target"
+ "final.target"
+ "kexec.target"
+ "systemd-kexec.service"
+ "systemd-update-utmp.service"
+
+ # Password entry.
+ "systemd-ask-password-console.path"
+ "systemd-ask-password-console.service"
+ "systemd-ask-password-wall.path"
+ "systemd-ask-password-wall.service"
+
+ # Slices / containers.
+ "slices.target"
+ "user.slice"
+ "machine.slice"
+ "machines.target"
+ "systemd-importd.service"
+ "systemd-machined.service"
+ "systemd-nspawn@.service"
+
+ # Temporary file creation / cleanup.
+ "systemd-tmpfiles-clean.service"
+ "systemd-tmpfiles-clean.timer"
+ "systemd-tmpfiles-setup.service"
+ "systemd-tmpfiles-setup-dev.service"
+
+ # Misc.
+ "systemd-sysctl.service"
+ "dbus-org.freedesktop.timedate1.service"
+ "dbus-org.freedesktop.locale1.service"
+ "dbus-org.freedesktop.hostname1.service"
+ "systemd-timedated.service"
+ "systemd-localed.service"
+ "systemd-hostnamed.service"
+ "systemd-exit.service"
+ "systemd-update-done.service"
+ ] ++ optionals config.services.journald.enableHttpGateway [
+ "systemd-journal-gatewayd.socket"
+ "systemd-journal-gatewayd.service"
+ ] ++ cfg.additionalUpstreamSystemUnits;
+
+ upstreamSystemWants =
+ [ "sysinit.target.wants"
+ "sockets.target.wants"
+ "local-fs.target.wants"
+ "multi-user.target.wants"
+ "timers.target.wants"
+ ];
+
+ upstreamUserUnits =
+ [ "basic.target"
+ "bluetooth.target"
+ "default.target"
+ "exit.target"
+ "graphical-session-pre.target"
+ "graphical-session.target"
+ "paths.target"
+ "printer.target"
+ "shutdown.target"
+ "smartcard.target"
+ "sockets.target"
+ "sound.target"
+ "systemd-exit.service"
+ "systemd-tmpfiles-clean.service"
+ "systemd-tmpfiles-clean.timer"
+ "systemd-tmpfiles-setup.service"
+ "timers.target"
+ ];
+
+ makeJobScript = name: text:
+ let
+ scriptName = replaceChars [ "\\" "@" ] [ "-" "_" ] (shellEscape name);
+ out = pkgs.writeTextFile {
+ # The derivation name is different from the script file name
+ # to keep the script file name short to avoid cluttering logs.
+ name = "unit-script-${scriptName}";
+ executable = true;
+ destination = "/bin/${scriptName}";
+ text = ''
+ #!${pkgs.runtimeShell} -e
+ ${text}
+ '';
+ checkPhase = ''
+ ${pkgs.stdenv.shell} -n "$out/bin/${scriptName}"
+ '';
+ };
+ in "${out}/bin/${scriptName}";
+
+ unitConfig = { config, options, ... }: {
+ config = {
+ unitConfig =
+ optionalAttrs (config.requires != [])
+ { Requires = toString config.requires; }
+ // optionalAttrs (config.wants != [])
+ { Wants = toString config.wants; }
+ // optionalAttrs (config.after != [])
+ { After = toString config.after; }
+ // optionalAttrs (config.before != [])
+ { Before = toString config.before; }
+ // optionalAttrs (config.bindsTo != [])
+ { BindsTo = toString config.bindsTo; }
+ // optionalAttrs (config.partOf != [])
+ { PartOf = toString config.partOf; }
+ // optionalAttrs (config.conflicts != [])
+ { Conflicts = toString config.conflicts; }
+ // optionalAttrs (config.requisite != [])
+ { Requisite = toString config.requisite; }
+ // optionalAttrs (config.restartTriggers != [])
+ { X-Restart-Triggers = toString config.restartTriggers; }
+ // optionalAttrs (config.description != "") {
+ Description = config.description; }
+ // optionalAttrs (config.documentation != []) {
+ Documentation = toString config.documentation; }
+ // optionalAttrs (config.onFailure != []) {
+ OnFailure = toString config.onFailure; }
+ // optionalAttrs (options.startLimitIntervalSec.isDefined) {
+ StartLimitIntervalSec = toString config.startLimitIntervalSec;
+ };
+ };
+ };
+
+ serviceConfig = { name, config, ... }: {
+ config = mkMerge
+ [ { # Default path for systemd services. Should be quite minimal.
+ path = mkAfter
+ [ pkgs.coreutils
+ pkgs.findutils
+ pkgs.gnugrep
+ pkgs.gnused
+ systemd
+ ];
+ environment.PATH = "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}";
+ }
+ (mkIf (config.preStart != "")
+ { serviceConfig.ExecStartPre =
+ makeJobScript "${name}-pre-start" config.preStart;
+ })
+ (mkIf (config.script != "")
+ { serviceConfig.ExecStart =
+ makeJobScript "${name}-start" config.script + " " + config.scriptArgs;
+ })
+ (mkIf (config.postStart != "")
+ { serviceConfig.ExecStartPost =
+ makeJobScript "${name}-post-start" config.postStart;
+ })
+ (mkIf (config.reload != "")
+ { serviceConfig.ExecReload =
+ makeJobScript "${name}-reload" config.reload;
+ })
+ (mkIf (config.preStop != "")
+ { serviceConfig.ExecStop =
+ makeJobScript "${name}-pre-stop" config.preStop;
+ })
+ (mkIf (config.postStop != "")
+ { serviceConfig.ExecStopPost =
+ makeJobScript "${name}-post-stop" config.postStop;
+ })
+ ];
+ };
+
+ mountConfig = { config, ... }: {
+ config = {
+ mountConfig =
+ { What = config.what;
+ Where = config.where;
+ } // optionalAttrs (config.type != "") {
+ Type = config.type;
+ } // optionalAttrs (config.options != "") {
+ Options = config.options;
+ };
+ };
+ };
+
+ automountConfig = { config, ... }: {
+ config = {
+ automountConfig =
+ { Where = config.where;
+ };
+ };
+ };
+
+ commonUnitText = def: ''
+ [Unit]
+ ${attrsToSection def.unitConfig}
+ '';
+
+ targetToUnit = name: def:
+ { inherit (def) aliases wantedBy requiredBy enable;
+ text =
+ ''
+ [Unit]
+ ${attrsToSection def.unitConfig}
+ '';
+ };
+
+ serviceToUnit = name: def:
+ { inherit (def) aliases wantedBy requiredBy enable;
+ text = commonUnitText def +
+ ''
+ [Service]
+ ${let env = cfg.globalEnvironment // def.environment;
+ in concatMapStrings (n:
+ let s = optionalString (env.${n} != null)
+ "Environment=${builtins.toJSON "${n}=${env.${n}}"}\n";
+ # systemd max line length is now 1MiB
+ # https://github.com/systemd/systemd/commit/e6dde451a51dc5aaa7f4d98d39b8fe735f73d2af
+ in if stringLength s >= 1048576 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${name}.service’ is too long." else s) (attrNames env)}
+ ${if def.reloadIfChanged then ''
+ X-ReloadIfChanged=true
+ '' else if !def.restartIfChanged then ''
+ X-RestartIfChanged=false
+ '' else ""}
+ ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"}
+ ${attrsToSection def.serviceConfig}
+ '';
+ };
+
+ socketToUnit = name: def:
+ { inherit (def) aliases wantedBy requiredBy enable;
+ text = commonUnitText def +
+ ''
+ [Socket]
+ ${attrsToSection def.socketConfig}
+ ${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
+ ${concatStringsSep "\n" (map (s: "ListenDatagram=${s}") def.listenDatagrams)}
+ '';
+ };
+
+ timerToUnit = name: def:
+ { inherit (def) aliases wantedBy requiredBy enable;
+ text = commonUnitText def +
+ ''
+ [Timer]
+ ${attrsToSection def.timerConfig}
+ '';
+ };
+
+ pathToUnit = name: def:
+ { inherit (def) aliases wantedBy requiredBy enable;
+ text = commonUnitText def +
+ ''
+ [Path]
+ ${attrsToSection def.pathConfig}
+ '';
+ };
+
+ mountToUnit = name: def:
+ { inherit (def) aliases wantedBy requiredBy enable;
+ text = commonUnitText def +
+ ''
+ [Mount]
+ ${attrsToSection def.mountConfig}
+ '';
+ };
+
+ automountToUnit = name: def:
+ { inherit (def) aliases wantedBy requiredBy enable;
+ text = commonUnitText def +
+ ''
+ [Automount]
+ ${attrsToSection def.automountConfig}
+ '';
+ };
+
+ sliceToUnit = name: def:
+ { inherit (def) aliases wantedBy requiredBy enable;
+ text = commonUnitText def +
+ ''
+ [Slice]
+ ${attrsToSection def.sliceConfig}
+ '';
+ };
+
+ logindHandlerType = types.enum [
+ "ignore" "poweroff" "reboot" "halt" "kexec" "suspend"
+ "hibernate" "hybrid-sleep" "suspend-then-hibernate" "lock"
+ ];
+
+ proxy_env = config.networking.proxy.envVars;
+
+in
+
+{
+ ###### interface
+
+ options = {
+
+ systemd.package = mkOption {
+ default = pkgs.systemd;
+ defaultText = "pkgs.systemd";
+ type = types.package;
+ description = "The systemd package.";
+ };
+
+ systemd.units = mkOption {
+ description = "Definition of systemd units.";
+ default = {};
+ type = with types; attrsOf (submodule (
+ { name, config, ... }:
+ { options = concreteUnitOptions;
+ config = {
+ unit = mkDefault (makeUnit name config);
+ };
+ }));
+ };
+
+ systemd.packages = mkOption {
+ default = [];
+ type = types.listOf types.package;
+ example = literalExample "[ pkgs.systemd-cryptsetup-generator ]";
+ description = "Packages providing systemd units and hooks.";
+ };
+
+ systemd.targets = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
+ description = "Definition of systemd target units.";
+ };
+
+ systemd.services = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ]);
+ description = "Definition of systemd service units.";
+ };
+
+ systemd.sockets = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ]);
+ description = "Definition of systemd socket units.";
+ };
+
+ systemd.timers = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ]);
+ description = "Definition of systemd timer units.";
+ };
+
+ systemd.paths = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
+ description = "Definition of systemd path units.";
+ };
+
+ systemd.mounts = mkOption {
+ default = [];
+ type = with types; listOf (submodule [ { options = mountOptions; } unitConfig mountConfig ]);
+ description = ''
+ Definition of systemd mount units.
+ This is a list instead of an attrSet, because systemd mandates the names to be derived from
+ the 'where' attribute.
+ '';
+ };
+
+ systemd.automounts = mkOption {
+ default = [];
+ type = with types; listOf (submodule [ { options = automountOptions; } unitConfig automountConfig ]);
+ description = ''
+ Definition of systemd automount units.
+ This is a list instead of an attrSet, because systemd mandates the names to be derived from
+ the 'where' attribute.
+ '';
+ };
+
+ systemd.slices = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig] );
+ description = "Definition of slice configurations.";
+ };
+
+ systemd.generators = mkOption {
+ type = types.attrsOf types.path;
+ default = {};
+ example = { systemd-gpt-auto-generator = "/dev/null"; };
+ description = ''
+ Definition of systemd generators.
+ For each <literal>NAME = VALUE</literal> pair of the attrSet, a link is generated from
+ <literal>/etc/systemd/system-generators/NAME</literal> to <literal>VALUE</literal>.
+ '';
+ };
+
+ systemd.shutdown = mkOption {
+ type = types.attrsOf types.path;
+ default = {};
+ description = ''
+ Definition of systemd shutdown executables.
+ For each <literal>NAME = VALUE</literal> pair of the attrSet, a link is generated from
+ <literal>/etc/systemd/system-shutdown/NAME</literal> to <literal>VALUE</literal>.
+ '';
+ };
+
+ systemd.defaultUnit = mkOption {
+ default = "multi-user.target";
+ type = types.str;
+ description = "Default unit started when the system boots.";
+ };
+
+ systemd.ctrlAltDelUnit = mkOption {
+ default = "reboot.target";
+ type = types.str;
+ example = "poweroff.target";
+ description = ''
+ Target that should be started when Ctrl-Alt-Delete is pressed.
+ '';
+ };
+
+ systemd.globalEnvironment = mkOption {
+ type = with types; attrsOf (nullOr (oneOf [ str path package ]));
+ default = {};
+ example = { TZ = "CET"; };
+ description = ''
+ Environment variables passed to <emphasis>all</emphasis> systemd units.
+ '';
+ };
+
+ systemd.enableCgroupAccounting = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether to enable cgroup accounting.
+ '';
+ };
+
+ systemd.coredump.enable = mkOption {
+ default = true;
+ type = types.bool;
+ description = ''
+ Whether core dumps should be processed by
+ <command>systemd-coredump</command>. If disabled, core dumps
+ appear in the current directory of the crashing process.
+ '';
+ };
+
+ systemd.coredump.extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ example = "Storage=journal";
+ description = ''
+ Extra config options for systemd-coredump. See coredump.conf(5) man page
+ for available options.
+ '';
+ };
+
+ systemd.extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ example = "DefaultLimitCORE=infinity";
+ description = ''
+ Extra config options for systemd. See man systemd-system.conf for
+ available options.
+ '';
+ };
+
+ services.journald.console = mkOption {
+ default = "";
+ type = types.str;
+ description = "If non-empty, write log messages to the specified TTY device.";
+ };
+
+ services.journald.rateLimitInterval = mkOption {
+ default = "30s";
+ type = types.str;
+ description = ''
+ Configures the rate limiting interval that is applied to all
+ messages generated on the system. This rate limiting is applied
+ per-service, so that two services which log do not interfere with
+ each other's limit. The value may be specified in the following
+ units: s, min, h, ms, us. To turn off any kind of rate limiting,
+ set either value to 0.
+
+ See <option>services.journald.rateLimitBurst</option> for important
+ considerations when setting this value.
+ '';
+ };
+
+ services.journald.rateLimitBurst = mkOption {
+ default = 10000;
+ type = types.int;
+ description = ''
+ Configures the rate limiting burst limit (number of messages per
+ interval) that is applied to all messages generated on the system.
+ This rate limiting is applied per-service, so that two services
+ which log do not interfere with each other's limit.
+
+ Note that the effective rate limit is multiplied by a factor derived
+ from the available free disk space for the journal as described on
+ <link xlink:href="https://www.freedesktop.org/software/systemd/man/journald.conf.html">
+ journald.conf(5)</link>.
+
+ Note that the total amount of logs stored is limited by journald settings
+ such as <literal>SystemMaxUse</literal>, which defaults to a 4 GB cap.
+
+ It is thus recommended to compute what period of time that you will be
+ able to store logs for when an application logs at full burst rate.
+ With default settings for log lines that are 100 Bytes long, this can
+ amount to just a few hours.
+ '';
+ };
+
+ services.journald.extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ example = "Storage=volatile";
+ description = ''
+ Extra config options for systemd-journald. See man journald.conf
+ for available options.
+ '';
+ };
+
+ services.journald.enableHttpGateway = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to enable the HTTP gateway to the journal.
+ '';
+ };
+
+ services.journald.forwardToSyslog = mkOption {
+ default = config.services.rsyslogd.enable || config.services.syslog-ng.enable;
+ defaultText = "services.rsyslogd.enable || services.syslog-ng.enable";
+ type = types.bool;
+ description = ''
+ Whether to forward log messages to syslog.
+ '';
+ };
+
+ services.logind.extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ example = "IdleAction=lock";
+ description = ''
+ Extra config options for systemd-logind. See
+ <link xlink:href="https://www.freedesktop.org/software/systemd/man/logind.conf.html">
+ logind.conf(5)</link> for available options.
+ '';
+ };
+
+ services.logind.killUserProcesses = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Specifies whether the processes of a user should be killed
+ when the user logs out. If true, the scope unit corresponding
+ to the session and all processes inside that scope will be
+ terminated. If false, the scope is "abandoned" (see
+ <link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.scope.html#">
+ systemd.scope(5)</link>), and processes are not killed.
+ </para>
+
+ <para>
+ See <link xlink:href="https://www.freedesktop.org/software/systemd/man/logind.conf.html#KillUserProcesses=">logind.conf(5)</link>
+ for more details.
+ '';
+ };
+
+ services.logind.lidSwitch = mkOption {
+ default = "suspend";
+ example = "ignore";
+ type = logindHandlerType;
+
+ description = ''
+ Specifies what to be done when the laptop lid is closed.
+ '';
+ };
+
+ services.logind.lidSwitchDocked = mkOption {
+ default = "ignore";
+ example = "suspend";
+ type = logindHandlerType;
+
+ description = ''
+ Specifies what to be done when the laptop lid is closed
+ and another screen is added.
+ '';
+ };
+
+ services.logind.lidSwitchExternalPower = mkOption {
+ default = config.services.logind.lidSwitch;
+ defaultText = "services.logind.lidSwitch";
+ example = "ignore";
+ type = logindHandlerType;
+
+ description = ''
+ Specifies what to do when the laptop lid is closed and the system is
+ on external power. By default use the same action as specified in
+ services.logind.lidSwitch.
+ '';
+ };
+
+ systemd.sleep.extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ example = "HibernateDelaySec=1h";
+ description = ''
+ Extra config options for systemd sleep state logic.
+ See sleep.conf.d(5) man page for available options.
+ '';
+ };
+
+ systemd.user.extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ example = "DefaultCPUAccounting=yes";
+ description = ''
+ Extra config options for systemd user instances. See man systemd-user.conf for
+ available options.
+ '';
+ };
+
+ systemd.tmpfiles.rules = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ example = [ "d /tmp 1777 root root 10d" ];
+ description = ''
+ Rules for creating and cleaning up temporary files
+ automatically. See
+ <citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for the exact format.
+ '';
+ };
+
+ systemd.tmpfiles.packages = mkOption {
+ type = types.listOf types.package;
+ default = [];
+ example = literalExample "[ pkgs.lvm2 ]";
+ apply = map getLib;
+ description = ''
+ List of packages containing <command>systemd-tmpfiles</command> rules.
+
+ All files ending in .conf found in
+ <filename><replaceable>pkg</replaceable>/lib/tmpfiles.d</filename>
+ will be included.
+ If this folder does not exist or does not contain any files an error will be returned instead.
+
+ If a <filename>lib</filename> output is available, rules are searched there and only there.
+ If there is no <filename>lib</filename> output it will fall back to <filename>out</filename>
+ and if that does not exist either, the default output will be used.
+ '';
+ };
+
+ systemd.user.units = mkOption {
+ description = "Definition of systemd per-user units.";
+ default = {};
+ type = with types; attrsOf (submodule (
+ { name, config, ... }:
+ { options = concreteUnitOptions;
+ config = {
+ unit = mkDefault (makeUnit name config);
+ };
+ }));
+ };
+
+ systemd.user.paths = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
+ description = "Definition of systemd per-user path units.";
+ };
+
+ systemd.user.services = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
+ description = "Definition of systemd per-user service units.";
+ };
+
+ systemd.user.slices = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ] );
+ description = "Definition of systemd per-user slice units.";
+ };
+
+ systemd.user.sockets = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] );
+ description = "Definition of systemd per-user socket units.";
+ };
+
+ systemd.user.targets = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
+ description = "Definition of systemd per-user target units.";
+ };
+
+ systemd.user.timers = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
+ description = "Definition of systemd per-user timer units.";
+ };
+
+ systemd.additionalUpstreamSystemUnits = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ example = [ "debug-shell.service" "systemd-quotacheck.service" ];
+ description = ''
+ Additional units shipped with systemd that shall be enabled.
+ '';
+ };
+
+ systemd.suppressedSystemUnits = mkOption {
+ default = [ ];
+ type = types.listOf types.str;
+ example = [ "systemd-backlight@.service" ];
+ description = ''
+ A list of units to suppress when generating system systemd configuration directory. This has
+ priority over upstream units, <option>systemd.units</option>, and
+ <option>systemd.additionalUpstreamSystemUnits</option>. The main purpose of this is to
+ suppress a upstream systemd unit with any modifications made to it by other NixOS modules.
+ '';
+ };
+
+ systemd.watchdog.device = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/dev/watchdog";
+ description = ''
+ The path to a hardware watchdog device which will be managed by systemd.
+ If not specified, systemd will default to /dev/watchdog.
+ '';
+ };
+
+ systemd.watchdog.runtimeTime = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "30s";
+ description = ''
+ The amount of time which can elapse before a watchdog hardware device
+ will automatically reboot the system. Valid time units include "ms",
+ "s", "min", "h", "d", and "w".
+ '';
+ };
+
+ systemd.watchdog.rebootTime = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "10m";
+ description = ''
+ The amount of time which can elapse after a reboot has been triggered
+ before a watchdog hardware device will automatically reboot the system.
+ Valid time units include "ms", "s", "min", "h", "d", and "w".
+ '';
+ };
+
+ systemd.watchdog.kexecTime = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "10m";
+ description = ''
+ The amount of time which can elapse when kexec is being executed before
+ a watchdog hardware device will automatically reboot the system. This
+ option should only be enabled if reloadTime is also enabled. Valid
+ time units include "ms", "s", "min", "h", "d", and "w".
+ '';
+ };
+ };
+
+
+ ###### implementation
+
+ config = {
+
+ warnings = concatLists (mapAttrsToList (name: service:
+ let
+ type = service.serviceConfig.Type or "";
+ restart = service.serviceConfig.Restart or "no";
+ in optional
+ (type == "oneshot" && (restart == "always" || restart == "on-success"))
+ "Service '${name}.service' with 'Type=oneshot' cannot have 'Restart=always' or 'Restart=on-success'")
+ cfg.services);
+
+ system.build.units = cfg.units;
+
+ system.nssModules = [ systemd.out ];
+ system.nssDatabases = {
+ hosts = (mkMerge [
+ [ "mymachines" ]
+ (mkOrder 1600 [ "myhostname" ] # 1600 to ensure it's always the last
+ )
+ ]);
+ passwd = (mkMerge [
+ (mkAfter [ "systemd" ])
+ ]);
+ group = (mkMerge [
+ (mkAfter [ "systemd" ])
+ ]);
+ };
+
+ environment.systemPackages = [ systemd ];
+
+ environment.etc = let
+ # generate contents for /etc/systemd/system-${type} from attrset of links and packages
+ hooks = type: links: pkgs.runCommand "system-${type}" {
+ preferLocalBuild = true;
+ packages = cfg.packages;
+ } ''
+ set -e
+ mkdir -p $out
+ for package in $packages
+ do
+ for hook in $package/lib/systemd/system-${type}/*
+ do
+ ln -s $hook $out/
+ done
+ done
+ ${concatStrings (mapAttrsToList (exec: target: "ln -s ${target} $out/${exec};\n") links)}
+ '';
+
+ enabledUpstreamSystemUnits = filter (n: ! elem n cfg.suppressedSystemUnits) upstreamSystemUnits;
+ enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedSystemUnits) cfg.units;
+ in ({
+ "systemd/system".source = generateUnits "system" enabledUnits enabledUpstreamSystemUnits upstreamSystemWants;
+
+ "systemd/user".source = generateUnits "user" cfg.user.units upstreamUserUnits [];
+
+ "systemd/system.conf".text = ''
+ [Manager]
+ ${optionalString config.systemd.enableCgroupAccounting ''
+ DefaultCPUAccounting=yes
+ DefaultIOAccounting=yes
+ DefaultBlockIOAccounting=yes
+ DefaultIPAccounting=yes
+ ''}
+ DefaultLimitCORE=infinity
+ ${optionalString (config.systemd.watchdog.device != null) ''
+ WatchdogDevice=${config.systemd.watchdog.device}
+ ''}
+ ${optionalString (config.systemd.watchdog.runtimeTime != null) ''
+ RuntimeWatchdogSec=${config.systemd.watchdog.runtimeTime}
+ ''}
+ ${optionalString (config.systemd.watchdog.rebootTime != null) ''
+ RebootWatchdogSec=${config.systemd.watchdog.rebootTime}
+ ''}
+ ${optionalString (config.systemd.watchdog.kexecTime != null) ''
+ KExecWatchdogSec=${config.systemd.watchdog.kexecTime}
+ ''}
+
+ ${config.systemd.extraConfig}
+ '';
+
+ "systemd/user.conf".text = ''
+ [Manager]
+ ${config.systemd.user.extraConfig}
+ '';
+
+ "systemd/journald.conf".text = ''
+ [Journal]
+ Storage=persistent
+ RateLimitInterval=${config.services.journald.rateLimitInterval}
+ RateLimitBurst=${toString config.services.journald.rateLimitBurst}
+ ${optionalString (config.services.journald.console != "") ''
+ ForwardToConsole=yes
+ TTYPath=${config.services.journald.console}
+ ''}
+ ${optionalString (config.services.journald.forwardToSyslog) ''
+ ForwardToSyslog=yes
+ ''}
+ ${config.services.journald.extraConfig}
+ '';
+
+ "systemd/coredump.conf".text =
+ ''
+ [Coredump]
+ ${config.systemd.coredump.extraConfig}
+ '';
+
+ "systemd/logind.conf".text = ''
+ [Login]
+ KillUserProcesses=${if config.services.logind.killUserProcesses then "yes" else "no"}
+ HandleLidSwitch=${config.services.logind.lidSwitch}
+ HandleLidSwitchDocked=${config.services.logind.lidSwitchDocked}
+ HandleLidSwitchExternalPower=${config.services.logind.lidSwitchExternalPower}
+ ${config.services.logind.extraConfig}
+ '';
+
+ "systemd/sleep.conf".text = ''
+ [Sleep]
+ ${config.systemd.sleep.extraConfig}
+ '';
+
+ # install provided sysctl snippets
+ "sysctl.d/50-coredump.conf".source = "${systemd}/example/sysctl.d/50-coredump.conf";
+ "sysctl.d/50-default.conf".source = "${systemd}/example/sysctl.d/50-default.conf";
+
+ "tmpfiles.d".source = (pkgs.symlinkJoin {
+ name = "tmpfiles.d";
+ paths = map (p: p + "/lib/tmpfiles.d") cfg.tmpfiles.packages;
+ postBuild = ''
+ for i in $(cat $pathsPath); do
+ (test -d "$i" && test $(ls "$i"/*.conf | wc -l) -ge 1) || (
+ echo "ERROR: The path '$i' from systemd.tmpfiles.packages contains no *.conf files."
+ exit 1
+ )
+ done
+ '' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) ''
+ rm -f $out/${removePrefix "tmpfiles.d/" name}
+ '') config.system.build.etc.targets;
+ }) + "/*";
+
+ "systemd/system-generators" = { source = hooks "generators" cfg.generators; };
+ "systemd/system-shutdown" = { source = hooks "shutdown" cfg.shutdown; };
+ });
+
+ services.dbus.enable = true;
+
+ users.users.systemd-network.uid = config.ids.uids.systemd-network;
+ users.groups.systemd-network.gid = config.ids.gids.systemd-network;
+ users.users.systemd-resolve.uid = config.ids.uids.systemd-resolve;
+ users.groups.systemd-resolve.gid = config.ids.gids.systemd-resolve;
+
+ # Target for ‘charon send-keys’ to hook into.
+ users.groups.keys.gid = config.ids.gids.keys;
+
+ systemd.targets.keys =
+ { description = "Security Keys";
+ unitConfig.X-StopOnReconfiguration = true;
+ };
+
+ systemd.tmpfiles.packages = [
+ # Default tmpfiles rules provided by systemd
+ (pkgs.runCommand "systemd-default-tmpfiles" {} ''
+ mkdir -p $out/lib/tmpfiles.d
+ cd $out/lib/tmpfiles.d
+
+ ln -s "${systemd}/example/tmpfiles.d/home.conf"
+ ln -s "${systemd}/example/tmpfiles.d/journal-nocow.conf"
+ ln -s "${systemd}/example/tmpfiles.d/static-nodes-permissions.conf"
+ ln -s "${systemd}/example/tmpfiles.d/systemd.conf"
+ ln -s "${systemd}/example/tmpfiles.d/systemd-nologin.conf"
+ ln -s "${systemd}/example/tmpfiles.d/systemd-nspawn.conf"
+ ln -s "${systemd}/example/tmpfiles.d/systemd-tmp.conf"
+ ln -s "${systemd}/example/tmpfiles.d/tmp.conf"
+ ln -s "${systemd}/example/tmpfiles.d/var.conf"
+ ln -s "${systemd}/example/tmpfiles.d/x11.conf"
+ '')
+ # User-specified tmpfiles rules
+ (pkgs.writeTextFile {
+ name = "nixos-tmpfiles.d";
+ destination = "/lib/tmpfiles.d/00-nixos.conf";
+ text = ''
+ # This file is created automatically and should not be modified.
+ # Please change the option ‘systemd.tmpfiles.rules’ instead.
+
+ ${concatStringsSep "\n" cfg.tmpfiles.rules}
+ '';
+ })
+ ];
+
+ systemd.units =
+ mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
+ // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
+ // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices
+ // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
+ // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets
+ // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers
+ // listToAttrs (map
+ (v: let n = escapeSystemdPath v.where;
+ in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
+ // listToAttrs (map
+ (v: let n = escapeSystemdPath v.where;
+ in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
+
+ systemd.user.units =
+ mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.user.paths
+ // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services
+ // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.user.slices
+ // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.user.sockets
+ // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.user.targets
+ // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.user.timers;
+
+ system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled
+ [ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET"
+ "SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC"
+ "CRYPTO_SHA256" "DMIID" "AUTOFS4_FS" "TMPFS_POSIX_ACL"
+ "TMPFS_XATTR" "SECCOMP"
+ ];
+
+ users.groups.systemd-journal.gid = config.ids.gids.systemd-journal;
+ users.users.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
+ users.groups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
+
+ # Generate timer units for all services that have a ‘startAt’ value.
+ systemd.timers =
+ mapAttrs (name: service:
+ { wantedBy = [ "timers.target" ];
+ timerConfig.OnCalendar = service.startAt;
+ })
+ (filterAttrs (name: service: service.enable && service.startAt != []) cfg.services);
+
+ # Generate timer units for all services that have a ‘startAt’ value.
+ systemd.user.timers =
+ mapAttrs (name: service:
+ { wantedBy = [ "timers.target" ];
+ timerConfig.OnCalendar = service.startAt;
+ })
+ (filterAttrs (name: service: service.startAt != []) cfg.user.services);
+
+ systemd.sockets.systemd-journal-gatewayd.wantedBy =
+ optional config.services.journald.enableHttpGateway "sockets.target";
+
+ # Provide the systemd-user PAM service, required to run systemd
+ # user instances.
+ security.pam.services.systemd-user =
+ { # Ensure that pam_systemd gets included. This is special-cased
+ # in systemd to provide XDG_RUNTIME_DIR.
+ startSession = true;
+ };
+
+ # Some overrides to upstream units.
+ systemd.services."systemd-backlight@".restartIfChanged = false;
+ systemd.services."systemd-fsck@".restartIfChanged = false;
+ systemd.services."systemd-fsck@".path = [ config.system.path ];
+ systemd.services."user@".restartIfChanged = false;
+ systemd.services.systemd-journal-flush.restartIfChanged = false;
+ systemd.services.systemd-random-seed.restartIfChanged = false;
+ systemd.services.systemd-remount-fs.restartIfChanged = false;
+ systemd.services.systemd-update-utmp.restartIfChanged = false;
+ systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
+ systemd.services.systemd-udev-settle.restartIfChanged = false; # Causes long delays in nixos-rebuild
+ # Restarting systemd-logind breaks X11
+ # - upstream commit: https://cgit.freedesktop.org/xorg/xserver/commit/?id=dc48bd653c7e101
+ # - systemd announcement: https://github.com/systemd/systemd/blob/22043e4317ecd2bc7834b48a6d364de76bb26d91/NEWS#L103-L112
+ # - this might be addressed in the future by xorg
+ #systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ];
+ systemd.services.systemd-logind.restartIfChanged = false;
+ systemd.services.systemd-logind.stopIfChanged = false;
+ # The user-runtime-dir@ service is managed by systemd-logind we should not touch it or else we break the users' sessions.
+ systemd.services."user-runtime-dir@".stopIfChanged = false;
+ systemd.services."user-runtime-dir@".restartIfChanged = false;
+ systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
+ systemd.services.systemd-journald.stopIfChanged = false;
+ systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
+ systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
+ systemd.targets.network-online.wantedBy = [ "multi-user.target" ];
+ systemd.services.systemd-importd.environment = proxy_env;
+
+ # Don't bother with certain units in containers.
+ systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container";
+ systemd.services.systemd-random-seed.unitConfig.ConditionVirtualization = "!container";
+
+ boot.kernel.sysctl = mkIf (!cfg.coredump.enable) {
+ "kernel.core_pattern" = "core";
+ };
+ };
+
+ # FIXME: Remove these eventually.
+ imports =
+ [ (mkRenamedOptionModule [ "boot" "systemd" "sockets" ] [ "systemd" "sockets" ])
+ (mkRenamedOptionModule [ "boot" "systemd" "targets" ] [ "systemd" "targets" ])
+ (mkRenamedOptionModule [ "boot" "systemd" "services" ] [ "systemd" "services" ])
+ (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ])
+ (mkRemovedOptionModule [ "systemd" "generator-packages" ] "Use systemd.packages instead.")
+ ];
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/timesyncd.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/timesyncd.nix
new file mode 100644
index 000000000000..35fb5578b070
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/timesyncd.nix
@@ -0,0 +1,71 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+
+ options = {
+
+ services.timesyncd = {
+ enable = mkOption {
+ default = !config.boot.isContainer;
+ type = types.bool;
+ description = ''
+ Enables the systemd NTP client daemon.
+ '';
+ };
+ servers = mkOption {
+ default = config.networking.timeServers;
+ description = ''
+ The set of NTP servers from which to synchronise.
+ '';
+ };
+ extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ example = ''
+ PollIntervalMaxSec=180
+ '';
+ description = ''
+ Extra config options for systemd-timesyncd. See
+ <link xlink:href="https://www.freedesktop.org/software/systemd/man/timesyncd.conf.html">
+ timesyncd.conf(5)</link> for available options.
+ '';
+ };
+ };
+ };
+
+ config = mkIf config.services.timesyncd.enable {
+
+ systemd.additionalUpstreamSystemUnits = [ "systemd-timesyncd.service" ];
+
+ systemd.services.systemd-timesyncd = {
+ wantedBy = [ "sysinit.target" ];
+ aliases = [ "dbus-org.freedesktop.timesync1.service" ];
+ restartTriggers = [ config.environment.etc."systemd/timesyncd.conf".source ];
+ };
+
+ environment.etc."systemd/timesyncd.conf".text = ''
+ [Time]
+ NTP=${concatStringsSep " " config.services.timesyncd.servers}
+ ${config.services.timesyncd.extraConfig}
+ '';
+
+ users.users.systemd-timesync = {
+ uid = config.ids.uids.systemd-timesync;
+ group = "systemd-timesync";
+ };
+ users.groups.systemd-timesync.gid = config.ids.gids.systemd-timesync;
+
+ system.activationScripts.systemd-timesyncd-migration = mkIf (versionOlder config.system.stateVersion "19.09") ''
+ # workaround an issue of systemd-timesyncd not starting due to upstream systemd reverting their dynamic users changes
+ # - https://github.com/NixOS/nixpkgs/pull/61321#issuecomment-492423742
+ # - https://github.com/systemd/systemd/issues/12131
+ if [ -L /var/lib/systemd/timesync ]; then
+ rm /var/lib/systemd/timesync
+ mv /var/lib/private/systemd/timesync /var/lib/systemd/timesync
+ fi
+ '';
+ };
+
+}
diff --git a/infra/libkookie/nixpkgs/nixos/modules/system/boot/tmp.nix b/infra/libkookie/nixpkgs/nixos/modules/system/boot/tmp.nix
new file mode 100644
index 000000000000..26eb172210e7
--- /dev/null
+++ b/infra/libkookie/nixpkgs/nixos/modules/system/boot/tmp.nix
@@ -0,0 +1,39 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+
+ ###### interface
+
+ options = {
+
+ boot.cleanTmpDir = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to delete all files in <filename>/tmp</filename> during boot.
+ '';
+ };
+
+ boot.tmpOnTmpfs = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to mount a tmpfs on <filename>/tmp</filename> during boot.
+ '';
+ };
+
+ };
+
+ ###### implementation
+
+ config = {
+
+ systemd.additionalUpstreamSystemUnits = optional config.boot.tmpOnTmpfs "tmp.mount";
+
+ systemd.tmpfiles.rules = optional config.boot.cleanTmpDir "D! /tmp 1777 root root";
+
+ };
+
+}