at 23.11-beta 20 kB view raw
1{ lib, options, config, utils, pkgs, ... }: 2 3with lib; 4 5let 6 inherit (utils) systemdUtils escapeSystemdPath; 7 inherit (systemdUtils.lib) 8 generateUnits 9 pathToUnit 10 serviceToUnit 11 sliceToUnit 12 socketToUnit 13 targetToUnit 14 timerToUnit 15 mountToUnit 16 automountToUnit; 17 18 19 cfg = config.boot.initrd.systemd; 20 21 # Copied from fedora 22 upstreamUnits = [ 23 "basic.target" 24 "ctrl-alt-del.target" 25 "emergency.service" 26 "emergency.target" 27 "final.target" 28 "halt.target" 29 "initrd-cleanup.service" 30 "initrd-fs.target" 31 "initrd-parse-etc.service" 32 "initrd-root-device.target" 33 "initrd-root-fs.target" 34 "initrd-switch-root.service" 35 "initrd-switch-root.target" 36 "initrd.target" 37 "kexec.target" 38 "kmod-static-nodes.service" 39 "local-fs-pre.target" 40 "local-fs.target" 41 "multi-user.target" 42 "paths.target" 43 "poweroff.target" 44 "reboot.target" 45 "rescue.service" 46 "rescue.target" 47 "rpcbind.target" 48 "shutdown.target" 49 "sigpwr.target" 50 "slices.target" 51 "sockets.target" 52 "swap.target" 53 "sysinit.target" 54 "sys-kernel-config.mount" 55 "syslog.socket" 56 "systemd-ask-password-console.path" 57 "systemd-ask-password-console.service" 58 "systemd-fsck@.service" 59 "systemd-halt.service" 60 "systemd-journald-audit.socket" 61 "systemd-journald-dev-log.socket" 62 "systemd-journald.service" 63 "systemd-journald.socket" 64 "systemd-kexec.service" 65 "systemd-modules-load.service" 66 "systemd-poweroff.service" 67 "systemd-reboot.service" 68 "systemd-sysctl.service" 69 "systemd-tmpfiles-setup-dev.service" 70 "systemd-tmpfiles-setup.service" 71 "timers.target" 72 "umount.target" 73 ] ++ cfg.additionalUpstreamUnits; 74 75 upstreamWants = [ 76 "sysinit.target.wants" 77 ]; 78 79 enabledUpstreamUnits = filter (n: ! elem n cfg.suppressedUnits) upstreamUnits; 80 enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedUnits) cfg.units; 81 jobScripts = concatLists (mapAttrsToList (_: unit: unit.jobScripts or []) (filterAttrs (_: v: v.enable) cfg.services)); 82 83 stage1Units = generateUnits { 84 type = "initrd"; 85 units = enabledUnits; 86 upstreamUnits = enabledUpstreamUnits; 87 inherit upstreamWants; 88 inherit (cfg) packages package; 89 }; 90 91 fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems; 92 93 needMakefs = lib.any (fs: fs.autoFormat) fileSystems; 94 95 kernel-name = config.boot.kernelPackages.kernel.name or "kernel"; 96 modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; }; 97 firmware = config.hardware.firmware; 98 # Determine the set of modules that we need to mount the root FS. 99 modulesClosure = pkgs.makeModulesClosure { 100 rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules; 101 kernel = modulesTree; 102 firmware = firmware; 103 allowMissing = false; 104 }; 105 106 initrdBinEnv = pkgs.buildEnv { 107 name = "initrd-bin-env"; 108 paths = map getBin cfg.initrdBin; 109 pathsToLink = ["/bin" "/sbin"]; 110 postBuild = concatStringsSep "\n" (mapAttrsToList (n: v: "ln -sf '${v}' $out/bin/'${n}'") cfg.extraBin); 111 }; 112 113 initialRamdisk = pkgs.makeInitrdNG { 114 name = "initrd-${kernel-name}"; 115 inherit (config.boot.initrd) compressor compressorArgs prepend; 116 inherit (cfg) strip; 117 118 contents = map (path: { object = path; symlink = ""; }) (subtractLists cfg.suppressedStorePaths cfg.storePaths) 119 ++ mapAttrsToList (_: v: { object = v.source; symlink = v.target; }) (filterAttrs (_: v: v.enable) cfg.contents); 120 }; 121 122in { 123 options.boot.initrd.systemd = { 124 enable = mkEnableOption (lib.mdDoc "systemd in initrd") // { 125 description = lib.mdDoc '' 126 Whether to enable systemd in initrd. The unit options such as 127 {option}`boot.initrd.systemd.services` are the same as their 128 stage 2 counterparts such as {option}`systemd.services`, 129 except that `restartTriggers` and `reloadTriggers` are not 130 supported. 131 ''; 132 }; 133 134 package = lib.mkOption { 135 type = lib.types.package; 136 default = config.systemd.package; 137 defaultText = lib.literalExpression "config.systemd.package"; 138 description = '' 139 The systemd package to use. 140 ''; 141 }; 142 143 extraConfig = mkOption { 144 default = ""; 145 type = types.lines; 146 example = "DefaultLimitCORE=infinity"; 147 description = lib.mdDoc '' 148 Extra config options for systemd. See systemd-system.conf(5) man page 149 for available options. 150 ''; 151 }; 152 153 managerEnvironment = mkOption { 154 type = with types; attrsOf (nullOr (oneOf [ str path package ])); 155 default = {}; 156 example = { SYSTEMD_LOG_LEVEL = "debug"; }; 157 description = lib.mdDoc '' 158 Environment variables of PID 1. These variables are 159 *not* passed to started units. 160 ''; 161 }; 162 163 contents = mkOption { 164 description = lib.mdDoc "Set of files that have to be linked into the initrd"; 165 example = literalExpression '' 166 { 167 "/etc/hostname".text = "mymachine"; 168 } 169 ''; 170 default = {}; 171 type = utils.systemdUtils.types.initrdContents; 172 }; 173 174 storePaths = mkOption { 175 description = lib.mdDoc '' 176 Store paths to copy into the initrd as well. 177 ''; 178 type = with types; listOf (oneOf [ singleLineStr package ]); 179 default = []; 180 }; 181 182 strip = mkOption { 183 description = lib.mdDoc '' 184 Whether to completely strip executables and libraries copied to the initramfs. 185 186 Setting this to false may save on the order of 30MiB on the 187 machine building the system (by avoiding a binutils 188 reference), at the cost of ~1MiB of initramfs size. This puts 189 this option firmly in the territory of micro-optimisation. 190 ''; 191 type = types.bool; 192 default = true; 193 }; 194 195 extraBin = mkOption { 196 description = lib.mdDoc '' 197 Tools to add to /bin 198 ''; 199 example = literalExpression '' 200 { 201 umount = ''${pkgs.util-linux}/bin/umount; 202 } 203 ''; 204 type = types.attrsOf types.path; 205 default = {}; 206 }; 207 208 suppressedStorePaths = mkOption { 209 description = lib.mdDoc '' 210 Store paths specified in the storePaths option that 211 should not be copied. 212 ''; 213 type = types.listOf types.singleLineStr; 214 default = []; 215 }; 216 217 emergencyAccess = mkOption { 218 type = with types; oneOf [ bool (nullOr (passwdEntry str)) ]; 219 description = lib.mdDoc '' 220 Set to true for unauthenticated emergency access, and false for 221 no emergency access. 222 223 Can also be set to a hashed super user password to allow 224 authenticated access to the emergency mode. 225 ''; 226 default = false; 227 }; 228 229 initrdBin = mkOption { 230 type = types.listOf types.package; 231 default = []; 232 description = lib.mdDoc '' 233 Packages to include in /bin for the stage 1 emergency shell. 234 ''; 235 }; 236 237 additionalUpstreamUnits = mkOption { 238 default = [ ]; 239 type = types.listOf types.str; 240 example = [ "debug-shell.service" "systemd-quotacheck.service" ]; 241 description = lib.mdDoc '' 242 Additional units shipped with systemd that shall be enabled. 243 ''; 244 }; 245 246 suppressedUnits = mkOption { 247 default = [ ]; 248 type = types.listOf types.str; 249 example = [ "systemd-backlight@.service" ]; 250 description = lib.mdDoc '' 251 A list of units to skip when generating system systemd configuration directory. This has 252 priority over upstream units, {option}`boot.initrd.systemd.units`, and 253 {option}`boot.initrd.systemd.additionalUpstreamUnits`. The main purpose of this is to 254 prevent a upstream systemd unit from being added to the initrd with any modifications made to it 255 by other NixOS modules. 256 ''; 257 }; 258 259 units = mkOption { 260 description = lib.mdDoc "Definition of systemd units."; 261 default = {}; 262 visible = "shallow"; 263 type = systemdUtils.types.units; 264 }; 265 266 packages = mkOption { 267 default = []; 268 type = types.listOf types.package; 269 example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]"; 270 description = lib.mdDoc "Packages providing systemd units and hooks."; 271 }; 272 273 targets = mkOption { 274 default = {}; 275 visible = "shallow"; 276 type = systemdUtils.types.initrdTargets; 277 description = lib.mdDoc "Definition of systemd target units."; 278 }; 279 280 services = mkOption { 281 default = {}; 282 type = systemdUtils.types.initrdServices; 283 visible = "shallow"; 284 description = lib.mdDoc "Definition of systemd service units."; 285 }; 286 287 sockets = mkOption { 288 default = {}; 289 type = systemdUtils.types.initrdSockets; 290 visible = "shallow"; 291 description = lib.mdDoc "Definition of systemd socket units."; 292 }; 293 294 timers = mkOption { 295 default = {}; 296 type = systemdUtils.types.initrdTimers; 297 visible = "shallow"; 298 description = lib.mdDoc "Definition of systemd timer units."; 299 }; 300 301 paths = mkOption { 302 default = {}; 303 type = systemdUtils.types.initrdPaths; 304 visible = "shallow"; 305 description = lib.mdDoc "Definition of systemd path units."; 306 }; 307 308 mounts = mkOption { 309 default = []; 310 type = systemdUtils.types.initrdMounts; 311 visible = "shallow"; 312 description = lib.mdDoc '' 313 Definition of systemd mount units. 314 This is a list instead of an attrSet, because systemd mandates the names to be derived from 315 the 'where' attribute. 316 ''; 317 }; 318 319 automounts = mkOption { 320 default = []; 321 type = systemdUtils.types.automounts; 322 visible = "shallow"; 323 description = lib.mdDoc '' 324 Definition of systemd automount units. 325 This is a list instead of an attrSet, because systemd mandates the names to be derived from 326 the 'where' attribute. 327 ''; 328 }; 329 330 slices = mkOption { 331 default = {}; 332 type = systemdUtils.types.slices; 333 visible = "shallow"; 334 description = lib.mdDoc "Definition of slice configurations."; 335 }; 336 337 enableTpm2 = mkOption { 338 default = true; 339 type = types.bool; 340 description = lib.mdDoc '' 341 Whether to enable TPM2 support in the initrd. 342 ''; 343 }; 344 }; 345 346 config = mkIf (config.boot.initrd.enable && cfg.enable) { 347 assertions = map (name: { 348 assertion = lib.attrByPath name (throw "impossible") config.boot.initrd == ""; 349 message = '' 350 systemd stage 1 does not support 'boot.initrd.${lib.concatStringsSep "." name}'. Please 351 convert it to analogous systemd units in 'boot.initrd.systemd'. 352 353 Definitions: 354 ${lib.concatMapStringsSep "\n" ({ file, ... }: " - ${file}") (lib.attrByPath name (throw "impossible") options.boot.initrd).definitionsWithLocations} 355 ''; 356 }) [ 357 [ "preFailCommands" ] 358 [ "preDeviceCommands" ] 359 [ "preLVMCommands" ] 360 [ "postDeviceCommands" ] 361 [ "postResumeCommands" ] 362 [ "postMountCommands" ] 363 [ "extraUdevRulesCommands" ] 364 [ "extraUtilsCommands" ] 365 [ "extraUtilsCommandsTest" ] 366 [ "network" "postCommands" ] 367 ]; 368 369 system.build = { inherit initialRamdisk; }; 370 371 boot.initrd.availableKernelModules = [ 372 # systemd needs this for some features 373 "autofs" 374 # systemd-cryptenroll 375 ] ++ lib.optional cfg.enableTpm2 "tpm-tis" 376 ++ lib.optional (cfg.enableTpm2 && !(pkgs.stdenv.hostPlatform.isRiscV64 || pkgs.stdenv.hostPlatform.isArmv7)) "tpm-crb"; 377 378 boot.initrd.systemd = { 379 initrdBin = [pkgs.bash pkgs.coreutils cfg.package.kmod cfg.package]; 380 extraBin = { 381 less = "${pkgs.less}/bin/less"; 382 mount = "${cfg.package.util-linux}/bin/mount"; 383 umount = "${cfg.package.util-linux}/bin/umount"; 384 fsck = "${cfg.package.util-linux}/bin/fsck"; 385 }; 386 387 managerEnvironment.PATH = "/bin:/sbin"; 388 389 contents = { 390 "/tmp/.keep".text = "systemd requires the /tmp mount point in the initrd cpio archive"; 391 "/init".source = "${cfg.package}/lib/systemd/systemd"; 392 "/etc/systemd/system".source = stage1Units; 393 394 "/etc/systemd/system.conf".text = '' 395 [Manager] 396 DefaultEnvironment=PATH=/bin:/sbin 397 ${cfg.extraConfig} 398 ManagerEnvironment=${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment)} 399 ''; 400 401 "/lib/modules".source = "${modulesClosure}/lib/modules"; 402 "/lib/firmware".source = "${modulesClosure}/lib/firmware"; 403 404 "/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules; 405 406 # We can use either ! or * to lock the root account in the 407 # console, but some software like OpenSSH won't even allow you 408 # to log in with an SSH key if you use ! so we use * instead 409 "/etc/shadow".text = "root:${if isBool cfg.emergencyAccess then optionalString (!cfg.emergencyAccess) "*" else cfg.emergencyAccess}:::::::"; 410 411 "/bin".source = "${initrdBinEnv}/bin"; 412 "/sbin".source = "${initrdBinEnv}/sbin"; 413 414 "/etc/sysctl.d/nixos.conf".text = "kernel.modprobe = /sbin/modprobe"; 415 "/etc/modprobe.d/systemd.conf".source = "${cfg.package}/lib/modprobe.d/systemd.conf"; 416 "/etc/modprobe.d/ubuntu.conf".source = pkgs.runCommand "initrd-kmod-blacklist-ubuntu" { } '' 417 ${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out 418 ''; 419 "/etc/modprobe.d/debian.conf".source = pkgs.kmod-debian-aliases; 420 421 "/etc/os-release".source = config.boot.initrd.osRelease; 422 "/etc/initrd-release".source = config.boot.initrd.osRelease; 423 424 } // optionalAttrs (config.environment.etc ? "modprobe.d/nixos.conf") { 425 "/etc/modprobe.d/nixos.conf".source = config.environment.etc."modprobe.d/nixos.conf".source; 426 }; 427 428 storePaths = [ 429 # systemd tooling 430 "${cfg.package}/lib/systemd/systemd-fsck" 431 "${cfg.package}/lib/systemd/systemd-hibernate-resume" 432 "${cfg.package}/lib/systemd/systemd-journald" 433 (lib.mkIf needMakefs "${cfg.package}/lib/systemd/systemd-makefs") 434 "${cfg.package}/lib/systemd/systemd-modules-load" 435 "${cfg.package}/lib/systemd/systemd-remount-fs" 436 "${cfg.package}/lib/systemd/systemd-shutdown" 437 "${cfg.package}/lib/systemd/systemd-sulogin-shell" 438 "${cfg.package}/lib/systemd/systemd-sysctl" 439 440 # generators 441 "${cfg.package}/lib/systemd/system-generators/systemd-debug-generator" 442 "${cfg.package}/lib/systemd/system-generators/systemd-fstab-generator" 443 "${cfg.package}/lib/systemd/system-generators/systemd-gpt-auto-generator" 444 "${cfg.package}/lib/systemd/system-generators/systemd-hibernate-resume-generator" 445 "${cfg.package}/lib/systemd/system-generators/systemd-run-generator" 446 447 # utilities needed by systemd 448 "${cfg.package.util-linux}/bin/mount" 449 "${cfg.package.util-linux}/bin/umount" 450 "${cfg.package.util-linux}/bin/sulogin" 451 452 # so NSS can look up usernames 453 "${pkgs.glibc}/lib/libnss_files.so.2" 454 ] ++ optionals (cfg.package.withCryptsetup && cfg.enableTpm2) [ 455 # tpm2 support 456 "${cfg.package}/lib/cryptsetup/libcryptsetup-token-systemd-tpm2.so" 457 pkgs.tpm2-tss 458 ] ++ optionals cfg.package.withCryptsetup [ 459 # fido2 support 460 "${cfg.package}/lib/cryptsetup/libcryptsetup-token-systemd-fido2.so" 461 "${pkgs.libfido2}/lib/libfido2.so.1" 462 ] ++ jobScripts; 463 464 targets.initrd.aliases = ["default.target"]; 465 units = 466 mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths 467 // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services 468 // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices 469 // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets 470 // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets 471 // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers 472 // listToAttrs (map 473 (v: let n = escapeSystemdPath v.where; 474 in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts) 475 // listToAttrs (map 476 (v: let n = escapeSystemdPath v.where; 477 in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts); 478 479 # make sure all the /dev nodes are set up 480 services.systemd-tmpfiles-setup-dev.wantedBy = ["sysinit.target"]; 481 482 services.initrd-nixos-activation = { 483 after = [ "initrd-fs.target" ]; 484 requiredBy = [ "initrd.target" ]; 485 unitConfig.AssertPathExists = "/etc/initrd-release"; 486 serviceConfig.Type = "oneshot"; 487 description = "NixOS Activation"; 488 489 script = /* bash */ '' 490 set -uo pipefail 491 export PATH="/bin:${cfg.package.util-linux}/bin" 492 493 # Figure out what closure to boot 494 closure= 495 for o in $(< /proc/cmdline); do 496 case $o in 497 init=*) 498 IFS== read -r -a initParam <<< "$o" 499 closure="$(dirname "''${initParam[1]}")" 500 ;; 501 esac 502 done 503 504 # Sanity check 505 if [ -z "''${closure:-}" ]; then 506 echo 'No init= parameter on the kernel command line' >&2 507 exit 1 508 fi 509 510 # If we are not booting a NixOS closure (e.g. init=/bin/sh), 511 # we don't know what root to prepare so we don't do anything 512 if ! [ -x "/sysroot$(readlink "/sysroot$closure/prepare-root" || echo "$closure/prepare-root")" ]; then 513 echo "NEW_INIT=''${initParam[1]}" > /etc/switch-root.conf 514 echo "$closure does not look like a NixOS installation - not activating" 515 exit 0 516 fi 517 echo 'NEW_INIT=' > /etc/switch-root.conf 518 519 520 # We need to propagate /run for things like /run/booted-system 521 # and /run/current-system. 522 mkdir -p /sysroot/run 523 mount --bind /run /sysroot/run 524 525 # Initialize the system 526 export IN_NIXOS_SYSTEMD_STAGE1=true 527 exec chroot /sysroot $closure/prepare-root 528 ''; 529 }; 530 531 # This will either call systemctl with the new init as the last parameter (which 532 # is the case when not booting a NixOS system) or with an empty string, causing 533 # systemd to bypass its verification code that checks whether the next file is a systemd 534 # and using its compiled-in value 535 services.initrd-switch-root.serviceConfig = { 536 EnvironmentFile = "-/etc/switch-root.conf"; 537 ExecStart = [ 538 "" 539 ''systemctl --no-block switch-root /sysroot "''${NEW_INIT}"'' 540 ]; 541 }; 542 543 services.panic-on-fail = { 544 wantedBy = ["emergency.target"]; 545 unitConfig = { 546 DefaultDependencies = false; 547 ConditionKernelCommandLine = [ 548 "|boot.panic_on_fail" 549 "|stage1panic" 550 ]; 551 }; 552 script = '' 553 echo c > /proc/sysrq-trigger 554 ''; 555 serviceConfig.Type = "oneshot"; 556 }; 557 }; 558 559 boot.kernelParams = lib.mkIf (config.boot.resumeDevice != "") [ "resume=${config.boot.resumeDevice}" ]; 560 }; 561}