1{
2 lib,
3 options,
4 config,
5 utils,
6 pkgs,
7 ...
8}:
9
10with lib;
11
12let
13 inherit (utils) systemdUtils escapeSystemdPath;
14 inherit (systemdUtils.unitOptions) unitOption;
15 inherit (systemdUtils.lib)
16 generateUnits
17 pathToUnit
18 serviceToUnit
19 sliceToUnit
20 socketToUnit
21 targetToUnit
22 timerToUnit
23 mountToUnit
24 automountToUnit
25 attrsToSection
26 ;
27
28 cfg = config.boot.initrd.systemd;
29
30 upstreamUnits = [
31 "basic.target"
32 "ctrl-alt-del.target"
33 "debug-shell.service"
34 "emergency.service"
35 "emergency.target"
36 "final.target"
37 "halt.target"
38 "initrd-cleanup.service"
39 "initrd-fs.target"
40 "initrd-parse-etc.service"
41 "initrd-root-device.target"
42 "initrd-root-fs.target"
43 "initrd-switch-root.service"
44 "initrd-switch-root.target"
45 "initrd.target"
46 "kexec.target"
47 "kmod-static-nodes.service"
48 "local-fs-pre.target"
49 "local-fs.target"
50 "modprobe@.service"
51 "multi-user.target"
52 "paths.target"
53 "poweroff.target"
54 "reboot.target"
55 "rescue.service"
56 "rescue.target"
57 "rpcbind.target"
58 "shutdown.target"
59 "sigpwr.target"
60 "slices.target"
61 "sockets.target"
62 "swap.target"
63 "sysinit.target"
64 "sys-kernel-config.mount"
65 "syslog.socket"
66 "systemd-ask-password-console.path"
67 "systemd-ask-password-console.service"
68 "systemd-fsck@.service"
69 "systemd-halt.service"
70 "systemd-hibernate-resume.service"
71 "systemd-journald-audit.socket"
72 "systemd-journald-dev-log.socket"
73 "systemd-journald.service"
74 "systemd-journald.socket"
75 "systemd-kexec.service"
76 "systemd-modules-load.service"
77 "systemd-poweroff.service"
78 "systemd-reboot.service"
79 "systemd-sysctl.service"
80 "timers.target"
81 "umount.target"
82 "systemd-bsod.service"
83 ]
84 ++ cfg.additionalUpstreamUnits;
85
86 upstreamWants = [
87 "sysinit.target.wants"
88 ];
89
90 enabledUpstreamUnits = filter (n: !elem n cfg.suppressedUnits) upstreamUnits;
91 enabledUnits = filterAttrs (n: v: !elem n cfg.suppressedUnits) cfg.units;
92 jobScripts = concatLists (
93 mapAttrsToList (_: unit: unit.jobScripts or [ ]) (filterAttrs (_: v: v.enable) cfg.services)
94 );
95
96 stage1Units = generateUnits {
97 type = "initrd";
98 units = enabledUnits;
99 upstreamUnits = enabledUpstreamUnits;
100 inherit upstreamWants;
101 inherit (cfg) packages package;
102 };
103
104 kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
105
106 initrdBinEnv = pkgs.buildEnv {
107 name = "initrd-bin-env";
108 paths = map getBin cfg.initrdBin;
109 pathsToLink = [
110 "/bin"
111 "/sbin"
112 ];
113
114 # Make sure sbin and bin have the same contents, and add extraBin
115 postBuild = ''
116 find $out/bin -maxdepth 1 -type l -print0 | xargs --null cp --no-dereference --no-clobber -t $out/sbin/
117 find $out/sbin -maxdepth 1 -type l -print0 | xargs --null cp --no-dereference --no-clobber -t $out/bin/
118 ${concatStringsSep "\n" (
119 mapAttrsToList (n: v: ''
120 ln -sf '${v}' $out/bin/'${n}'
121 ln -sf '${v}' $out/sbin/'${n}'
122 '') cfg.extraBin
123 )}
124 '';
125 };
126
127 initialRamdisk = pkgs.makeInitrdNG {
128 name = "initrd-${kernel-name}";
129 inherit (config.boot.initrd) compressor compressorArgs prepend;
130
131 contents = lib.filter (
132 { source, enable, ... }: (!lib.elem source cfg.suppressedStorePaths) && enable
133 ) cfg.storePaths;
134 };
135
136in
137{
138 imports = [
139 (lib.mkRemovedOptionModule [ "boot" "initrd" "systemd" "strip" ] ''
140 The option to strip ELF files in initrd has been removed.
141 It only saved ~1MiB of initramfs size, but caused a few issues
142 like unloadable kernel modules.
143 '')
144 (lib.mkRemovedOptionModule [
145 "boot"
146 "initrd"
147 "systemd"
148 "extraConfig"
149 ] "Use boot.initrd.systemd.settings.Manager instead.")
150 ];
151
152 options.boot.initrd.systemd = {
153 enable = mkEnableOption "systemd in initrd" // {
154 description = ''
155 Whether to enable systemd in initrd. The unit options such as
156 {option}`boot.initrd.systemd.services` are the same as their
157 stage 2 counterparts such as {option}`systemd.services`,
158 except that `restartTriggers` and `reloadTriggers` are not
159 supported.
160 '';
161 };
162
163 package = lib.mkOption {
164 type = lib.types.package;
165 default = config.systemd.package;
166 defaultText = lib.literalExpression "config.systemd.package";
167 description = ''
168 The systemd package to use.
169 '';
170 };
171
172 settings.Manager = mkOption {
173 default = { };
174 defaultText = lib.literalExpression ''
175 {
176 DefaultEnvironment = "PATH=/bin:/sbin";
177 }
178 '';
179 type = lib.types.submodule {
180 freeformType = types.attrsOf unitOption;
181 };
182 example = {
183 WatchdogDevice = "/dev/watchdog";
184 RuntimeWatchdogSec = "30s";
185 RebootWatchdogSec = "10min";
186 KExecWatchdogSec = "5min";
187 };
188 description = ''
189 Options for the global systemd service manager used in initrd. See {manpage}`systemd-system.conf(5)` man page
190 for available options.
191 '';
192 };
193
194 managerEnvironment = mkOption {
195 type =
196 with types;
197 attrsOf (
198 nullOr (oneOf [
199 str
200 path
201 package
202 ])
203 );
204 default = { };
205 defaultText = ''
206 {
207 PATH = "/bin:/sbin";
208 }
209 '';
210 example = {
211 SYSTEMD_LOG_LEVEL = "debug";
212 };
213 description = ''
214 Environment variables of PID 1. These variables are
215 *not* passed to started units.
216 '';
217 };
218
219 contents = mkOption {
220 description = "Set of files that have to be linked into the initrd";
221 example = literalExpression ''
222 {
223 "/etc/machine-id".source = /etc/machine-id;
224 }
225 '';
226 default = { };
227 type = utils.systemdUtils.types.initrdContents;
228 };
229
230 storePaths = mkOption {
231 description = ''
232 Store paths to copy into the initrd as well.
233 '';
234 type = utils.systemdUtils.types.initrdStorePath;
235 default = [ ];
236 };
237
238 extraBin = mkOption {
239 description = ''
240 Tools to add to /bin
241 '';
242 example = literalExpression ''
243 {
244 umount = ''${pkgs.util-linux}/bin/umount;
245 }
246 '';
247 type = types.attrsOf types.path;
248 default = { };
249 };
250
251 suppressedStorePaths = mkOption {
252 description = ''
253 Store paths specified in the storePaths option that
254 should not be copied.
255 '';
256 type = types.listOf types.singleLineStr;
257 default = [ ];
258 };
259
260 root = lib.mkOption {
261 type = lib.types.enum [
262 "fstab"
263 "gpt-auto"
264 ];
265 default = "fstab";
266 example = "gpt-auto";
267 description = ''
268 Controls how systemd will interpret the root FS in initrd. See
269 {manpage}`kernel-command-line(7)`. NixOS currently does not
270 allow specifying the root file system itself this
271 way. Instead, the `fstab` value is used in order to interpret
272 the root file system specified with the `fileSystems` option.
273 '';
274 };
275
276 emergencyAccess = mkOption {
277 type =
278 with types;
279 oneOf [
280 bool
281 (nullOr (passwdEntry str))
282 ];
283 description = ''
284 Set to true for unauthenticated emergency access, and false or
285 null for no emergency access.
286
287 Can also be set to a hashed super user password to allow
288 authenticated access to the emergency mode.
289
290 For emergency access after initrd, use `${options.systemd.enableEmergencyMode}` instead.
291 '';
292 default = false;
293 };
294
295 initrdBin = mkOption {
296 type = types.listOf types.package;
297 default = [ ];
298 description = ''
299 Packages to include in /bin for the stage 1 emergency shell.
300 '';
301 };
302
303 additionalUpstreamUnits = mkOption {
304 default = [ ];
305 type = types.listOf types.str;
306 example = [
307 "debug-shell.service"
308 "systemd-quotacheck.service"
309 ];
310 description = ''
311 Additional units shipped with systemd that shall be enabled.
312 '';
313 };
314
315 suppressedUnits = mkOption {
316 default = [ ];
317 type = types.listOf types.str;
318 example = [ "systemd-backlight@.service" ];
319 description = ''
320 A list of units to skip when generating system systemd configuration directory. This has
321 priority over upstream units, {option}`boot.initrd.systemd.units`, and
322 {option}`boot.initrd.systemd.additionalUpstreamUnits`. The main purpose of this is to
323 prevent a upstream systemd unit from being added to the initrd with any modifications made to it
324 by other NixOS modules.
325 '';
326 };
327
328 units = mkOption {
329 description = "Definition of systemd units.";
330 default = { };
331 visible = "shallow";
332 type = systemdUtils.types.units;
333 };
334
335 packages = mkOption {
336 default = [ ];
337 type = types.listOf types.package;
338 example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]";
339 description = "Packages providing systemd units and hooks.";
340 };
341
342 targets = mkOption {
343 default = { };
344 visible = "shallow";
345 type = systemdUtils.types.initrdTargets;
346 description = "Definition of systemd target units.";
347 };
348
349 services = mkOption {
350 default = { };
351 type = systemdUtils.types.initrdServices;
352 visible = "shallow";
353 description = "Definition of systemd service units.";
354 };
355
356 sockets = mkOption {
357 default = { };
358 type = systemdUtils.types.initrdSockets;
359 visible = "shallow";
360 description = "Definition of systemd socket units.";
361 };
362
363 timers = mkOption {
364 default = { };
365 type = systemdUtils.types.initrdTimers;
366 visible = "shallow";
367 description = "Definition of systemd timer units.";
368 };
369
370 paths = mkOption {
371 default = { };
372 type = systemdUtils.types.initrdPaths;
373 visible = "shallow";
374 description = "Definition of systemd path units.";
375 };
376
377 mounts = mkOption {
378 default = [ ];
379 type = systemdUtils.types.initrdMounts;
380 visible = "shallow";
381 description = ''
382 Definition of systemd mount units.
383 This is a list instead of an attrSet, because systemd mandates the names to be derived from
384 the 'where' attribute.
385 '';
386 };
387
388 automounts = mkOption {
389 default = [ ];
390 type = systemdUtils.types.automounts;
391 visible = "shallow";
392 description = ''
393 Definition of systemd automount units.
394 This is a list instead of an attrSet, because systemd mandates the names to be derived from
395 the 'where' attribute.
396 '';
397 };
398
399 slices = mkOption {
400 default = { };
401 type = systemdUtils.types.slices;
402 visible = "shallow";
403 description = "Definition of slice configurations.";
404 };
405 };
406
407 config = mkIf (config.boot.initrd.enable && cfg.enable) {
408 assertions = [
409 {
410 assertion =
411 cfg.root == "fstab" -> any (fs: fs.mountPoint == "/") (builtins.attrValues config.fileSystems);
412 message = "The ‘fileSystems’ option does not specify your root file system.";
413 }
414 ]
415 ++
416 map
417 (name: {
418 assertion = lib.attrByPath name (throw "impossible") config.boot.initrd == "";
419 message = ''
420 systemd stage 1 does not support 'boot.initrd.${lib.concatStringsSep "." name}'. Please
421 convert it to analogous systemd units in 'boot.initrd.systemd'.
422
423 Definitions:
424 ${lib.concatMapStringsSep "\n" ({ file, ... }: " - ${file}")
425 (lib.attrByPath name (throw "impossible") options.boot.initrd).definitionsWithLocations
426 }
427 '';
428 })
429 [
430 [ "preFailCommands" ]
431 [ "preDeviceCommands" ]
432 [ "preLVMCommands" ]
433 [ "postDeviceCommands" ]
434 [ "postResumeCommands" ]
435 [ "postMountCommands" ]
436 [ "extraUdevRulesCommands" ]
437 [ "extraUtilsCommands" ]
438 [ "extraUtilsCommandsTest" ]
439 [
440 "network"
441 "postCommands"
442 ]
443 ];
444
445 system.build = { inherit initialRamdisk; };
446
447 boot.initrd.availableKernelModules = [
448 # systemd needs this for some features
449 "autofs"
450 # systemd-cryptenroll
451 ]
452 ++ lib.optional cfg.package.withEfi "efivarfs";
453
454 boot.kernelParams = [
455 "root=${config.boot.initrd.systemd.root}"
456 ]
457 ++ lib.optional (config.boot.resumeDevice != "") "resume=${config.boot.resumeDevice}"
458 # `systemd` mounts root in initrd as read-only unless "rw" is on the kernel command line.
459 # For NixOS activation to succeed, we need to have root writable in initrd.
460 ++ lib.optional (config.boot.initrd.systemd.root == "gpt-auto") "rw";
461
462 boot.initrd.systemd = {
463 # bashInteractive is easier to use and also required by debug-shell.service
464 initrdBin = [
465 pkgs.bashInteractive
466 pkgs.coreutils
467 cfg.package
468 ]
469 ++ lib.optional (config.system.build.kernel.config.isYes "MODULES") cfg.package.kmod;
470 extraBin = {
471 less = "${pkgs.less}/bin/less";
472 mount = "${cfg.package.util-linux}/bin/mount";
473 umount = "${cfg.package.util-linux}/bin/umount";
474 fsck = "${cfg.package.util-linux}/bin/fsck";
475 };
476
477 managerEnvironment.PATH = "/bin:/sbin";
478 settings.Manager.ManagerEnvironment = lib.concatStringsSep " " (
479 lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment
480 );
481 settings.Manager.DefaultEnvironment = "PATH=/bin:/sbin";
482
483 contents = {
484 "/init".source = "${cfg.package}/lib/systemd/systemd";
485 "/etc/systemd/system".source = stage1Units;
486
487 "/etc/systemd/system.conf".text = ''
488 [Manager]
489 ${attrsToSection cfg.settings.Manager}
490 '';
491
492 # We can use either ! or * to lock the root account in the
493 # console, but some software like OpenSSH won't even allow you
494 # to log in with an SSH key if you use ! so we use * instead
495 "/etc/shadow".text =
496 let
497 ea = cfg.emergencyAccess;
498 access = ea != null && !(isBool ea && !ea);
499 passwd = if isString ea then ea else "";
500 in
501 "root:${if access then passwd else "*"}:::::::";
502
503 "/bin".source = "${initrdBinEnv}/bin";
504 "/sbin".source = "${initrdBinEnv}/sbin";
505 "/usr/bin".source = "${initrdBinEnv}/bin";
506 "/usr/sbin".source = "${initrdBinEnv}/sbin";
507
508 "/etc/os-release".source = config.boot.initrd.osRelease;
509 "/etc/initrd-release".source = config.boot.initrd.osRelease;
510
511 # For systemd-journald's _HOSTNAME field; needs to be set early, cannot be backfilled.
512 "/etc/hostname".text = config.networking.hostName;
513
514 }
515 // optionalAttrs (config.environment.etc ? "modprobe.d/nixos.conf") {
516 "/etc/modprobe.d/nixos.conf".source = config.environment.etc."modprobe.d/nixos.conf".source;
517 }
518 // optionalAttrs (with config.system.build.kernel.config; isSet "MODULES" -> isYes "MODULES") {
519 "/lib".source = "${config.system.build.modulesClosure}/lib";
520
521 "/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules;
522
523 "/etc/sysctl.d/nixos.conf".text = "kernel.modprobe = /sbin/modprobe";
524 "/etc/modprobe.d/systemd.conf".source = "${cfg.package}/lib/modprobe.d/systemd.conf";
525 "/etc/modprobe.d/ubuntu.conf".source = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
526 "/etc/modprobe.d/debian.conf".source = pkgs.kmod-debian-aliases;
527 };
528
529 storePaths = [
530 # systemd tooling
531 "${cfg.package}/lib/systemd/systemd-executor"
532 "${cfg.package}/lib/systemd/systemd-fsck"
533 "${cfg.package}/lib/systemd/systemd-hibernate-resume"
534 "${cfg.package}/lib/systemd/systemd-journald"
535 "${cfg.package}/lib/systemd/systemd-makefs"
536 "${cfg.package}/lib/systemd/systemd-modules-load"
537 "${cfg.package}/lib/systemd/systemd-remount-fs"
538 "${cfg.package}/lib/systemd/systemd-shutdown"
539 "${cfg.package}/lib/systemd/systemd-sulogin-shell"
540 "${cfg.package}/lib/systemd/systemd-sysctl"
541 "${cfg.package}/lib/systemd/systemd-bsod"
542 "${cfg.package}/lib/systemd/systemd-sysroot-fstab-check"
543
544 # generators
545 "${cfg.package}/lib/systemd/system-generators/systemd-debug-generator"
546 "${cfg.package}/lib/systemd/system-generators/systemd-fstab-generator"
547 "${cfg.package}/lib/systemd/system-generators/systemd-gpt-auto-generator"
548 "${cfg.package}/lib/systemd/system-generators/systemd-hibernate-resume-generator"
549 "${cfg.package}/lib/systemd/system-generators/systemd-run-generator"
550
551 # utilities needed by systemd
552 "${cfg.package.util-linux}/bin/mount"
553 "${cfg.package.util-linux}/bin/umount"
554 "${cfg.package.util-linux}/bin/sulogin"
555
556 # required for services generated with writeShellScript and friends
557 pkgs.runtimeShell
558 # some tools like xfs still want the sh symlink
559 "${pkgs.bashNonInteractive}/bin"
560
561 # so NSS can look up usernames
562 "${pkgs.glibc}/lib/libnss_files.so.2"
563
564 # Resolving sysroot symlinks without code exec
565 "${config.system.nixos-init.package}/bin/chroot-realpath"
566 # Find the etc paths
567 "${config.system.nixos-init.package}/bin/find-etc"
568 ]
569 ++ lib.optionals config.system.nixos-init.enable [
570 "${config.system.nixos-init.package}/bin/initrd-init"
571 ]
572 ++ jobScripts
573 ++ map (c: builtins.removeAttrs c [ "text" ]) (builtins.attrValues cfg.contents);
574
575 targets.initrd.aliases = [ "default.target" ];
576 units =
577 mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit v)) cfg.paths
578 // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit v)) cfg.services
579 // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit v)) cfg.slices
580 // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit v)) cfg.sockets
581 // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit v)) cfg.targets
582 // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit v)) cfg.timers
583 // listToAttrs (
584 map (
585 v:
586 let
587 n = escapeSystemdPath v.where;
588 in
589 nameValuePair "${n}.mount" (mountToUnit v)
590 ) cfg.mounts
591 )
592 // listToAttrs (
593 map (
594 v:
595 let
596 n = escapeSystemdPath v.where;
597 in
598 nameValuePair "${n}.automount" (automountToUnit v)
599 ) cfg.automounts
600 );
601
602 services.initrd-find-nixos-closure = lib.mkIf (!config.system.nixos-init.enable) {
603 description = "Find NixOS closure";
604
605 unitConfig = {
606 RequiresMountsFor = "/sysroot/nix/store";
607 DefaultDependencies = false;
608 };
609 before = [
610 "initrd.target"
611 "shutdown.target"
612 ];
613 conflicts = [ "shutdown.target" ];
614 requiredBy = [ "initrd.target" ];
615 serviceConfig = {
616 Type = "oneshot";
617 RemainAfterExit = true;
618 };
619
620 script = # bash
621 ''
622 set -uo pipefail
623 export PATH="/bin:${
624 lib.makeBinPath [
625 cfg.package.util-linux
626 config.system.nixos-init.package
627 ]
628 }"
629
630 # Figure out what closure to boot
631 closure=
632 for o in $(< /proc/cmdline); do
633 case $o in
634 init=*)
635 IFS="=" read -r -a initParam <<< "$o"
636 closure="''${initParam[1]}"
637 ;;
638 esac
639 done
640
641 # Sanity check
642 if [ -z "''${closure:-}" ]; then
643 echo 'No init= parameter on the kernel command line' >&2
644 exit 1
645 fi
646
647 # Resolve symlinks in the init parameter. We need this for some boot loaders
648 # (e.g. boot.loader.generationsDir).
649 closure="$(chroot-realpath /sysroot "$closure")"
650
651 # Assume the directory containing the init script is the closure.
652 closure="$(dirname "$closure")"
653
654 ln --symbolic "$closure" /nixos-closure
655
656 # If we are not booting a NixOS closure (e.g. init=/bin/sh),
657 # we don't know what root to prepare so we don't do anything
658 if ! [ -x "/sysroot$(readlink "/sysroot$closure/prepare-root" || echo "$closure/prepare-root")" ]; then
659 echo "NEW_INIT=''${initParam[1]}" > /etc/switch-root.conf
660 echo "$closure does not look like a NixOS installation - not activating"
661 exit 0
662 fi
663 echo 'NEW_INIT=' > /etc/switch-root.conf
664 '';
665 };
666
667 # We need to propagate /run for things like /run/booted-system
668 # and /run/current-system.
669 mounts = [
670 {
671 where = "/sysroot/run";
672 what = "/run";
673 options = "rbind";
674 unitConfig = {
675 # See the comment on the mount unit for /run/etc-metadata
676 DefaultDependencies = false;
677 };
678 requiredBy = [ "initrd-fs.target" ];
679 before = [ "initrd-fs.target" ];
680 }
681 ];
682
683 services.initrd-nixos-activation = lib.mkIf (!config.system.nixos-init.enable) {
684 after = [ "initrd-switch-root.target" ];
685 requiredBy = [ "initrd-switch-root.service" ];
686 before = [ "initrd-switch-root.service" ];
687 unitConfig.DefaultDependencies = false;
688 unitConfig = {
689 AssertPathExists = "/etc/initrd-release";
690 RequiresMountsFor = [
691 "/sysroot/run"
692 ];
693 };
694 serviceConfig.Type = "oneshot";
695 description = "NixOS Activation";
696
697 script = # bash
698 ''
699 set -uo pipefail
700 export PATH="/bin:${cfg.package.util-linux}/bin"
701
702 closure="$(realpath /nixos-closure)"
703
704 # Initialize the system
705 export IN_NIXOS_SYSTEMD_STAGE1=true
706 exec chroot /sysroot "$closure/prepare-root"
707 '';
708 };
709
710 services.initrd-switch-root =
711 if config.system.nixos-init.enable then
712 {
713 path = [
714 cfg.package
715 cfg.package.util-linux
716 config.system.nixos-init.package
717 ];
718 environment = {
719 FIRMWARE = "${config.hardware.firmware}/lib/firmware";
720 MODPROBE_BINARY = "${pkgs.kmod}/bin/modprobe";
721 NIX_STORE_MOUNT_OPTS = lib.concatStringsSep "," config.boot.nixStoreMountOpts;
722 }
723 // lib.optionalAttrs (config.environment.usrbinenv != null) {
724 ENV_BINARY = config.environment.usrbinenv;
725 }
726 // lib.optionalAttrs (config.environment.binsh != null) {
727 SH_BINARY = config.environment.binsh;
728 };
729 serviceConfig = {
730 ExecStart = [
731 ""
732 "${config.system.nixos-init.package}/bin/initrd-init"
733 ];
734 };
735 }
736 else
737 # This will either call systemctl with the new init as the last parameter (which
738 # is the case when not booting a NixOS system) or with an empty string, causing
739 # systemd to bypass its verification code that checks whether the next file is a systemd
740 # and using its compiled-in value
741 {
742 serviceConfig = {
743 EnvironmentFile = "-/etc/switch-root.conf";
744 ExecStart = [
745 ""
746 ''systemctl --no-block switch-root /sysroot "''${NEW_INIT}"''
747 ];
748 };
749 };
750
751 services.panic-on-fail = {
752 wantedBy = [ "emergency.target" ];
753 unitConfig = {
754 DefaultDependencies = false;
755 ConditionKernelCommandLine = [
756 "|boot.panic_on_fail"
757 "|stage1panic"
758 ];
759 };
760 serviceConfig = {
761 Type = "oneshot";
762 ExecStart = "${pkgs.coreutils}/bin/echo c";
763 StandardOutput = "file:/proc/sysrq-trigger";
764 };
765 };
766 };
767 };
768}