summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorWill Fancher <elvishjerricco@gmail.com>2022-03-19 23:00:06 -0400
committerWill Fancher <elvishjerricco@gmail.com>2022-03-22 21:28:43 -0400
commit2d4ebf1259149ac52c191f461eef4eae6c3671fc (patch)
treee7a95cda3d80c24f28a57f0a4a71ee5d665ec9d5 /nixos
parent25113740a5063483645cab857716a77437cd881e (diff)
downloadnixpkgs-2d4ebf1259149ac52c191f461eef4eae6c3671fc.tar
nixpkgs-2d4ebf1259149ac52c191f461eef4eae6c3671fc.tar.gz
nixpkgs-2d4ebf1259149ac52c191f461eef4eae6c3671fc.tar.bz2
nixpkgs-2d4ebf1259149ac52c191f461eef4eae6c3671fc.tar.lz
nixpkgs-2d4ebf1259149ac52c191f461eef4eae6c3671fc.tar.xz
nixpkgs-2d4ebf1259149ac52c191f461eef4eae6c3671fc.tar.zst
nixpkgs-2d4ebf1259149ac52c191f461eef4eae6c3671fc.zip
initrd: Optional systemd-based initrd
Diffstat (limited to 'nixos')
-rw-r--r--nixos/lib/systemd-lib.nix29
-rw-r--r--nixos/lib/systemd-types.nix1
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/system/boot/stage-1.nix8
-rw-r--r--nixos/modules/system/boot/systemd.nix3
-rw-r--r--nixos/modules/system/boot/systemd/initrd.nix355
6 files changed, 384 insertions, 13 deletions
diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix
index 27c8b5b0471..3129fbe7bdb 100644
--- a/nixos/lib/systemd-lib.nix
+++ b/nixos/lib/systemd-lib.nix
@@ -291,16 +291,10 @@ in rec {
     };
   };
 
-  serviceConfig = { name, config, ... }: {
+  mkServiceConfig = path: { name, config, ... }: {
     config = mkMerge
       [ { # Default path for systemd services.  Should be quite minimal.
-          path = mkAfter
-            [ pkgs.coreutils
-              pkgs.findutils
-              pkgs.gnugrep
-              pkgs.gnused
-              systemd
-            ];
+          path = mkAfter path;
           environment.PATH = "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}";
         }
         (mkIf (config.preStart != "")
@@ -330,6 +324,16 @@ in rec {
       ];
   };
 
+  serviceConfig = mkServiceConfig [
+    pkgs.coreutils
+    pkgs.findutils
+    pkgs.gnugrep
+    pkgs.gnused
+    systemd
+  ];
+
+  initrdServiceConfig = mkServiceConfig [];
+
   mountConfig = { config, ... }: {
     config = {
       mountConfig =
@@ -387,6 +391,15 @@ in rec {
         '';
     };
 
+  initrdServiceToUnit = name: def:
+    { inherit (def) aliases wantedBy requiredBy enable;
+      text = commonUnitText def +
+        ''
+          [Service]
+          ${attrsToSection def.serviceConfig}
+        '';
+    };
+
   socketToUnit = name: def:
     { inherit (def) aliases wantedBy requiredBy enable;
       text = commonUnitText def +
diff --git a/nixos/lib/systemd-types.nix b/nixos/lib/systemd-types.nix
index a7a324f187c..b303335ffc1 100644
--- a/nixos/lib/systemd-types.nix
+++ b/nixos/lib/systemd-types.nix
@@ -12,6 +12,7 @@ rec {
     }));
 
   services = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ]);
+  initrdServices = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig initrdServiceConfig ]);
 
   targets = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig ]);
 
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 49d1105247a..2c1272ecd5a 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -1172,6 +1172,7 @@
   ./system/boot/systemd/nspawn.nix
   ./system/boot/systemd/tmpfiles.nix
   ./system/boot/systemd/user.nix
+  ./system/boot/systemd/initrd.nix
   ./system/boot/timesyncd.nix
   ./system/boot/tmp.nix
   ./system/etc/etc-activation.nix
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 1575c0257d1..aa2f4fb860b 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -706,8 +706,12 @@ in
       }
     ];
 
-    system.build =
-      { inherit bootStage1 initialRamdisk initialRamdiskSecretAppender extraUtils; };
+    system.build = mkMerge [
+      { inherit bootStage1 initialRamdiskSecretAppender extraUtils; }
+
+      # generated in nixos/modules/system/boot/systemd/initrd.nix
+      (mkIf (!config.boot.initrd.systemd.enable) { inherit initialRamdisk; })
+    ];
 
     system.requiredKernelConfig = with config.lib.kernelConfig; [
       (isYes "TMPFS")
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 00116c73ccc..fd550077362 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -11,10 +11,7 @@ let
   systemd = cfg.package;
 
   inherit (systemdUtils.lib)
-    makeUnit
     generateUnits
-    makeJobScript
-    commonUnitText
     targetToUnit
     serviceToUnit
     socketToUnit
diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix
new file mode 100644
index 00000000000..cd626a915a4
--- /dev/null
+++ b/nixos/modules/system/boot/systemd/initrd.nix
@@ -0,0 +1,355 @@
+{ lib, config, utils, pkgs, ... }:
+
+with lib;
+
+let
+  inherit (utils) systemdUtils escapeSystemdPath;
+  inherit (systemdUtils.lib)
+    generateUnits
+    pathToUnit
+    initrdServiceToUnit
+    sliceToUnit
+    socketToUnit
+    targetToUnit
+    timerToUnit
+    mountToUnit
+    automountToUnit;
+
+
+  cfg = config.boot.initrd.systemd;
+
+  # Copied from fedora
+  upstreamUnits = [
+    "basic.target"
+    "ctrl-alt-del.target"
+    "emergency.service"
+    "emergency.target"
+    "final.target"
+    "halt.target"
+    "initrd-cleanup.service"
+    "initrd-fs.target"
+    "initrd-parse-etc.service"
+    "initrd-root-device.target"
+    "initrd-root-fs.target"
+    "initrd-switch-root.service"
+    "initrd-switch-root.target"
+    "initrd.target"
+    "initrd-udevadm-cleanup-db.service"
+    "kexec.target"
+    "kmod-static-nodes.service"
+    "local-fs-pre.target"
+    "local-fs.target"
+    "multi-user.target"
+    "paths.target"
+    "poweroff.target"
+    "reboot.target"
+    "rescue.service"
+    "rescue.target"
+    "rpcbind.target"
+    "shutdown.target"
+    "sigpwr.target"
+    "slices.target"
+    "sockets.target"
+    "swap.target"
+    "sysinit.target"
+    "sys-kernel-config.mount"
+    "syslog.socket"
+    "systemd-ask-password-console.path"
+    "systemd-ask-password-console.service"
+    "systemd-fsck@.service"
+    "systemd-halt.service"
+    "systemd-hibernate-resume@.service"
+    "systemd-journald-audit.socket"
+    "systemd-journald-dev-log.socket"
+    "systemd-journald.service"
+    "systemd-journald.socket"
+    "systemd-kexec.service"
+    "systemd-modules-load.service"
+    "systemd-poweroff.service"
+    "systemd-random-seed.service"
+    "systemd-reboot.service"
+    "systemd-sysctl.service"
+    "systemd-tmpfiles-setup-dev.service"
+    "systemd-tmpfiles-setup.service"
+    "systemd-udevd-control.socket"
+    "systemd-udevd-kernel.socket"
+    "systemd-udevd.service"
+    "systemd-udev-settle.service"
+    "systemd-udev-trigger.service"
+    "systemd-vconsole-setup.service"
+    "timers.target"
+    "umount.target"
+
+    # TODO: Networking
+    # "network-online.target"
+    # "network-pre.target"
+    # "network.target"
+    # "nss-lookup.target"
+    # "nss-user-lookup.target"
+    # "remote-fs-pre.target"
+    # "remote-fs.target"
+  ] ++ cfg.additionalUpstreamUnits;
+
+  upstreamWants = [
+    "sysinit.target.wants"
+  ];
+
+  enabledUpstreamUnits = filter (n: ! elem n cfg.suppressedUnits) upstreamUnits;
+  enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedUnits) cfg.units;
+
+  stage1Units = generateUnits {
+    type = "initrd";
+    units = enabledUnits;
+    upstreamUnits = enabledUpstreamUnits;
+    inherit upstreamWants;
+    inherit (cfg) packages package;
+  };
+
+  fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
+
+  fstab = pkgs.writeText "fstab" (lib.concatMapStringsSep "\n"
+    ({ fsType, mountPoint, device, options, ... }:
+      "${device} /sysroot${mountPoint} ${fsType} ${lib.concatStringsSep "," options}") fileSystems);
+
+  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 = false;
+  };
+
+  initrdBinEnv = pkgs.buildEnv {
+    name = "initrd-emergency-env";
+    paths = map getBin cfg.initrdBin;
+    pathsToLink = ["/bin" "/sbin"];
+  };
+
+  initialRamdisk = pkgs.makeInitrdNG {
+    contents = cfg.objects;
+  };
+
+in {
+  options.boot.initrd.systemd = {
+    enable = mkEnableOption ''systemd in initrd.
+
+      Note: This is in very early development and is highly
+      experimental. Most of the features NixOS supports in initrd are
+      not yet supported by the intrd generated with this option.
+    '';
+
+    package = (lib.mkPackageOption pkgs "systemd" {
+      default = "systemdMinimal";
+    }) // {
+      visible = false;
+    };
+
+    objects = mkOption {
+      description = "List of objects to include in the initrd, and their symlinks";
+      example = literalExpression ''
+        [ { object = "''${systemd}/lib/systemd/systemd"; symlink = "/init"; } ]
+      '';
+      visible = false;
+      type = types.listOf (types.submodule {
+        options = {
+          object = mkOption {
+            type = types.path;
+            description = "The object to include in initrd.";
+          };
+          symlink = mkOption {
+            type = types.nullOr types.path;
+            description = "A symlink to create in initrd pointing to the object.";
+            default = null;
+          };
+        };
+      });
+    };
+
+    emergencyHashedPassword = mkOption {
+      type = types.str;
+      visible = false;
+      description = ''
+        Hashed password for the super user account in stage 1 emergency mode
+
+        Blank for no password, ! for super user disabled.
+      '';
+      default = "!";
+    };
+
+    initrdBin = mkOption {
+      type = types.listOf types.package;
+      default = [];
+      visible = false;
+      description = ''
+        Packages to include in /bin for the stage 1 emergency shell.
+      '';
+    };
+
+    additionalUpstreamUnits = mkOption {
+      default = [ ];
+      type = types.listOf types.str;
+      visible = false;
+      example = [ "debug-shell.service" "systemd-quotacheck.service" ];
+      description = ''
+        Additional units shipped with systemd that shall be enabled.
+      '';
+    };
+
+    suppressedUnits = mkOption {
+      default = [ ];
+      type = types.listOf types.str;
+      example = [ "systemd-backlight@.service" ];
+      visible = false;
+      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.
+      '';
+    };
+
+    units = mkOption {
+      description = "Definition of systemd units.";
+      default = {};
+      visible = false;
+      type = systemdUtils.types.units;
+    };
+
+    packages = mkOption {
+      default = [];
+      visible = false;
+      type = types.listOf types.package;
+      example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]";
+      description = "Packages providing systemd units and hooks.";
+    };
+
+    targets = mkOption {
+      default = {};
+      visible = false;
+      type = systemdUtils.types.targets;
+      description = "Definition of systemd target units.";
+    };
+
+    services = mkOption {
+      default = {};
+      type = systemdUtils.types.initrdServices;
+      visible = false;
+      description = "Definition of systemd service units.";
+    };
+
+    sockets = mkOption {
+      default = {};
+      type = systemdUtils.types.sockets;
+      visible = false;
+      description = "Definition of systemd socket units.";
+    };
+
+    timers = mkOption {
+      default = {};
+      type = systemdUtils.types.timers;
+      visible = false;
+      description = "Definition of systemd timer units.";
+    };
+
+    paths = mkOption {
+      default = {};
+      type = systemdUtils.types.paths;
+      visible = false;
+      description = "Definition of systemd path units.";
+    };
+
+    mounts = mkOption {
+      default = [];
+      type = systemdUtils.types.mounts;
+      visible = false;
+      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.
+      '';
+    };
+
+    automounts = mkOption {
+      default = [];
+      type = systemdUtils.types.automounts;
+      visible = false;
+      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.
+      '';
+    };
+
+    slices = mkOption {
+      default = {};
+      type = systemdUtils.types.slices;
+      visible = false;
+      description = "Definition of slice configurations.";
+    };
+  };
+
+  config = mkIf (config.boot.initrd.enable && cfg.enable) {
+    system.build = { inherit initialRamdisk; };
+    boot.initrd.systemd = {
+      initrdBin = [pkgs.bash pkgs.coreutils pkgs.kmod cfg.package];
+
+      objects = [
+        { object = "${cfg.package}/lib/systemd/systemd"; symlink = "/init"; }
+        { object = stage1Units; symlink = "/etc/systemd/system"; }
+
+        # TODO: Limit this to the bare necessities
+        { object = "${cfg.package}/lib"; }
+
+        { object = "${cfg.package.util-linux}/bin/mount"; }
+        { object = "${cfg.package.util-linux}/bin/umount"; }
+        { object = "${cfg.package.util-linux}/bin/sulogin"; }
+
+        # TODO: Not sure why this needs to be here for the recovery shell to work
+        { object = "${pkgs.glibc}/lib/libnss_files.so"; }
+
+        { object = config.environment.etc.os-release.source; symlink = "/etc/initrd-release"; }
+        { object = config.environment.etc.os-release.source; symlink = "/etc/os-release"; }
+        { object = fstab; symlink = "/etc/fstab"; }
+        {
+          object = "${modulesClosure}/lib/modules";
+          symlink = "/lib/modules";
+        }
+        {
+          symlink = "/etc/modules-load.d/nixos.conf";
+          object = pkgs.writeText "nixos.conf"
+            (lib.concatStringsSep "\n" config.boot.initrd.kernelModules);
+        }
+        {
+          object = builtins.toFile "passwd" "root:x:0:0:System Administrator:/root:/bin/bash";
+          symlink = "/etc/passwd";
+        }
+        {
+          object = builtins.toFile "shadow" "root:${config.boot.initrd.systemd.emergencyHashedPassword}:::::::";
+          symlink = "/etc/shadow";
+        }
+        { object = "${initrdBinEnv}/bin"; symlink = "/bin"; }
+        { object = "${initrdBinEnv}/sbin"; symlink = "/sbin"; }
+        { object = builtins.toFile "bashrc" "PATH=/bin:/sbin"; symlink = "/etc/bashrc"; }
+        { object = builtins.toFile "sysctl.conf" "kernel.modprobe = /sbin/modprobe"; symlink = "/etc/sysctl.d/nixos.conf"; }
+      ];
+
+      targets.initrd.aliases = ["default.target"];
+      units =
+           mapAttrs' (n: v: nameValuePair "${n}.path"    (pathToUnit          n v)) cfg.paths
+        // mapAttrs' (n: v: nameValuePair "${n}.service" (initrdServiceToUnit 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);
+    };
+  };
+}