Files
nixpkgs/nixos/modules/system/boot/systemd/initrd.nix

858 lines
27 KiB
Nix
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
lib,
options,
config,
utils,
pkgs,
...
}:
let
inherit (lib)
filter
elem
filterAttrs
concatLists
mapAttrsToList
getBin
concatStringsSep
mkEnableOption
mkOption
types
literalExpression
mkIf
any
isBool
isString
optionalAttrs
mapAttrs'
nameValuePair
listToAttrs
;
inherit (utils) systemdUtils escapeSystemdPath;
inherit (systemdUtils.unitOptions) unitOption;
inherit (systemdUtils.lib)
generateUnits
pathToUnit
serviceToUnit
sliceToUnit
socketToUnit
targetToUnit
timerToUnit
mountToUnit
automountToUnit
settingsToSections
;
cfg = config.boot.initrd.systemd;
withKmod =
let
kconfig = config.system.build.kernel.config;
in
kconfig.isSet "MODULES" -> kconfig.isYes "MODULES";
upstreamUnits = [
"basic.target"
"breakpoint-pre-udev.service"
"breakpoint-pre-basic.service"
"breakpoint-pre-mount.service"
"breakpoint-pre-switch-root.service"
"ctrl-alt-del.target"
"debug-shell.service"
"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"
"kexec.target"
"kmod-static-nodes.service"
"local-fs-pre.target"
"local-fs.target"
"modprobe@.service"
"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-factory-reset-complete.service"
"factory-reset-now.target"
"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-reboot.service"
"systemd-sysctl.service"
"timers.target"
"umount.target"
"systemd-bsod.service"
]
++ cfg.additionalUpstreamUnits;
upstreamWants = [
"sysinit.target.wants"
];
enabledUpstreamUnits = filter (n: !elem n cfg.suppressedUnits) upstreamUnits;
enabledUnits = filterAttrs (n: v: !elem n cfg.suppressedUnits) cfg.units;
jobScripts = concatLists (
mapAttrsToList (_: unit: unit.jobScripts or [ ]) (filterAttrs (_: v: v.enable) cfg.services)
);
unitEnv = pkgs.buildEnv {
name = "initrd-unit-env";
paths = concatLists (
mapAttrsToList (_: unit: unit.path or [ ]) (filterAttrs (_: v: v.enable) cfg.services)
);
pathsToLink = [
"/bin"
"/sbin"
];
};
stage1Units = generateUnits {
type = "initrd";
units = enabledUnits;
upstreamUnits = enabledUpstreamUnits;
inherit upstreamWants;
inherit (cfg) packages package;
};
kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
initrdBinEnv = pkgs.buildEnv {
name = "initrd-bin-env";
paths = map getBin cfg.initrdBin;
pathsToLink = [
"/bin"
"/sbin"
];
# Make sure sbin and bin have the same contents, and add extraBin
postBuild = ''
find $out/bin -maxdepth 1 -type l -print0 | xargs --null cp --no-dereference --no-clobber -t $out/sbin/
find $out/sbin -maxdepth 1 -type l -print0 | xargs --null cp --no-dereference --no-clobber -t $out/bin/
${concatStringsSep "\n" (
mapAttrsToList (n: v: ''
ln -sf '${v}' $out/bin/'${n}'
ln -sf '${v}' $out/sbin/'${n}'
'') cfg.extraBin
)}
'';
};
initialRamdisk = pkgs.makeInitrdNG {
name = "initrd-${kernel-name}";
inherit (config.boot.initrd) compressor compressorArgs prepend;
contents = lib.filter (
{ source, enable, ... }: (!lib.elem source cfg.suppressedStorePaths) && enable
) cfg.storePaths;
};
in
{
imports = [
(lib.mkRemovedOptionModule [ "boot" "initrd" "systemd" "strip" ] ''
The option to strip ELF files in initrd has been removed.
It only saved ~1MiB of initramfs size, but caused a few issues
like unloadable kernel modules.
'')
(lib.mkRemovedOptionModule [
"boot"
"initrd"
"systemd"
"extraConfig"
] "Use boot.initrd.systemd.settings.Manager instead.")
];
options.boot.initrd.systemd = {
enable = mkEnableOption "systemd in initrd" // {
default = true;
description = ''
Whether to enable systemd in initrd. The unit options such as
{option}`boot.initrd.systemd.services` are the same as their
stage 2 counterparts such as {option}`systemd.services`,
except that `restartTriggers` and `reloadTriggers` are not
supported.
'';
};
package = lib.mkOption {
type = lib.types.package;
default = config.systemd.package;
defaultText = lib.literalExpression "config.systemd.package";
description = ''
The systemd package to use.
'';
};
settings.Manager = mkOption {
default = { };
defaultText = lib.literalExpression ''
{
DefaultEnvironment = "PATH=/bin:/sbin";
}
'';
type = lib.types.submodule {
freeformType = types.attrsOf unitOption;
};
example = {
WatchdogDevice = "/dev/watchdog";
RuntimeWatchdogSec = "30s";
RebootWatchdogSec = "10min";
KExecWatchdogSec = "5min";
};
description = ''
Options for the global systemd service manager used in initrd. See {manpage}`systemd-system.conf(5)`
for available options.
'';
};
managerEnvironment = mkOption {
type =
with types;
attrsOf (
nullOr (oneOf [
str
path
package
])
);
default = { };
defaultText = ''
{
PATH = "/bin:/sbin";
}
'';
example = {
SYSTEMD_LOG_LEVEL = "debug";
};
description = ''
Environment variables of PID 1. These variables are
*not* passed to started units.
'';
};
contents = mkOption {
description = "Set of files that have to be linked into the initrd";
example = literalExpression ''
{
"/etc/machine-id".source = /etc/machine-id;
}
'';
default = { };
type = utils.systemdUtils.types.initrdContents;
};
storePaths = mkOption {
description = ''
Store paths to copy into the initrd as well.
'';
type = utils.systemdUtils.types.initrdStorePath;
default = [ ];
};
extraBin = mkOption {
description = ''
Tools to add to /bin
'';
example = literalExpression ''
{
umount = "''${pkgs.util-linux}/bin/umount";
}
'';
type = types.attrsOf types.path;
default = { };
};
suppressedStorePaths = mkOption {
description = ''
Store paths specified in the storePaths option that
should not be copied.
'';
type = types.listOf types.singleLineStr;
default = [ ];
};
root = lib.mkOption {
type = lib.types.nullOr (
lib.types.enum [
"fstab"
"gpt-auto"
]
);
default = "fstab";
example = "gpt-auto";
description = ''
Controls how systemd will interpret the root FS in initrd. See
the description of the `root=` argument in {manpage}`kernel-command-line(7)`.
When set to "gpt-auto", {manpage}`systemd-gpt-auto-generator(8)`
automatically discovers and mounts the root, EFI System (ESP),
swap, and various other partitions based on their partition
type, allowing them to be effectively omitted from
{option}`fileSystems.<name>`.
Note however that {option}`boot.initrd.supportedFilesystems`
will not be inferred for partitions without an explicit
{option}`fileSystems.<name>.fsType`, therefore the filesystem
names of all partitions mounted through "gpt-auto" must be
manually appended to `supportedFilesystems`.
If root shall be omitted, set this option to `null`.
'';
};
emergencyAccess = mkOption {
type =
with types;
oneOf [
bool
(nullOr (passwdEntry str))
];
description = ''
Set to true for unauthenticated emergency access, and false or
null for no emergency access.
Can also be set to a hashed super user password to allow
authenticated access to the emergency mode.
For emergency access after initrd, use `${options.systemd.enableEmergencyMode}` instead.
'';
default = false;
};
initrdBin = mkOption {
type = types.listOf types.package;
default = [ ];
description = ''
Packages to include in /bin for the stage 1 emergency shell.
'';
};
additionalUpstreamUnits = mkOption {
default = [ ];
type = types.listOf types.str;
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" ];
description = ''
A list of units to skip when generating system systemd configuration directory. This has
priority over upstream units, {option}`boot.initrd.systemd.units`, and
{option}`boot.initrd.systemd.additionalUpstreamUnits`. The main purpose of this is to
prevent a upstream systemd unit from being added to the initrd with any modifications made to it
by other NixOS modules.
'';
};
shell.enable = lib.mkEnableOption "" // {
default = config.environment.shell.enable;
internal = true;
description = ''
Whether to enable a shell in the initrd.
In contrast to `environment.shell.enable`, this option actually
strictly disables all shells in the initrd because they're not copied
into it anymore. Paths that use a shell (e.g. via the `script` option),
will break if this option is set.
Only set this option if you're sure that you can recover from potential
issues.
'';
};
units = mkOption {
description = "Definition of systemd units.";
default = { };
visible = "shallow";
type = systemdUtils.types.units;
};
packages = mkOption {
default = [ ];
type = types.listOf types.package;
example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]";
description = "Packages providing systemd units and hooks.";
};
targets = mkOption {
default = { };
visible = "shallow";
type = systemdUtils.types.initrdTargets;
description = "Definition of systemd target units.";
};
services = mkOption {
default = { };
type = systemdUtils.types.initrdServices;
visible = "shallow";
description = "Definition of systemd service units.";
};
sockets = mkOption {
default = { };
type = systemdUtils.types.initrdSockets;
visible = "shallow";
description = "Definition of systemd socket units.";
};
timers = mkOption {
default = { };
type = systemdUtils.types.initrdTimers;
visible = "shallow";
description = "Definition of systemd timer units.";
};
paths = mkOption {
default = { };
type = systemdUtils.types.initrdPaths;
visible = "shallow";
description = "Definition of systemd path units.";
};
mounts = mkOption {
default = [ ];
type = systemdUtils.types.initrdMounts;
visible = "shallow";
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 = "shallow";
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 = "shallow";
description = "Definition of slice configurations.";
};
};
config = mkIf (config.boot.initrd.enable && cfg.enable) {
assertions =
let
obsoleteOpt =
opts: msgFn:
lib.flip map opts (opt: {
assertion = lib.attrByPath opt (throw "impossible") config.boot.initrd == "";
message = ''
${msgFn (lib.concatStringsSep "." opt)}
Definitions:
${lib.concatMapStringsSep "\n" ({ file, ... }: " - ${file}")
(lib.attrByPath opt (throw "impossible") options.boot.initrd).definitionsWithLocations
}
'';
});
in
[
{
assertion =
cfg.root == "fstab" -> any (fs: fs.mountPoint == "/") (builtins.attrValues config.fileSystems);
message = "The fileSystems option does not specify your root file system.";
}
]
++
obsoleteOpt
[
[ "preFailCommands" ]
[ "preDeviceCommands" ]
[ "preLVMCommands" ]
[ "postDeviceCommands" ]
[ "postResumeCommands" ]
[ "postMountCommands" ]
[
"network"
"postCommands"
]
]
(name: ''
systemd stage 1 does not support `boot.initrd.${name}`. Instead, create systemd services using the `boot.initrd.systemd.services` options, which has an API matching the stage 2 `systemd.services` options. Refer to `bootup(7)`, specifically the sections on "Bootup in the Initrd" and "System Manager Bootup", for information about when various units happen, and order services accordingly.
'')
++
obsoleteOpt
[
[ "extraUtilsCommands" ]
[ "extraUtilsCommandsTest" ]
]
(name: ''
systemd stage 1 does not support `boot.initrd.${name}`. Instead, use `boot.initrd.systemd.initrdBin`, `boot.initrd.systemd.extraBin`, `boot.initrd.systemd.contents`, or `boot.initrd.systemd.storePaths` to add files to the initrd.
'')
++ obsoleteOpt [ [ "extraUdevRulesCommands" ] ] (name: ''
systemd stage 1 does not support `boot.initrd.${name}`. Instead, use `boot.initrd.services.udev` to configure udev.
'');
system.build = { inherit initialRamdisk; };
boot.initrd.availableKernelModules = [
# systemd needs this for some features
"autofs"
# systemd-cryptenroll
]
++ lib.optional cfg.package.withEfi "efivarfs";
boot.kernelParams =
lib.optional (config.boot.initrd.systemd.root != null) "root=${config.boot.initrd.systemd.root}"
++ lib.optional (config.boot.resumeDevice != "") "resume=${config.boot.resumeDevice}"
# `systemd` mounts root in initrd as read-only unless "rw" is on the kernel command line.
# For NixOS activation to succeed, we need to have root writable in initrd.
++ lib.optional (config.boot.initrd.systemd.root == "gpt-auto") "rw";
boot.initrd.systemd = {
initrdBin = [
pkgs.coreutils
cfg.package
]
++ lib.optional withKmod cfg.package.kmod
++ lib.optionals cfg.shell.enable [
# bashInteractive is easier to use and also required by debug-shell.service
pkgs.bashInteractive
];
extraBin = {
less = "${pkgs.less}/bin/less";
mount = "${cfg.package.util-linux}/bin/mount";
umount = "${cfg.package.util-linux}/bin/umount";
fsck = "${cfg.package.util-linux}/bin/fsck";
};
managerEnvironment.PATH = "/bin:/sbin";
settings.Manager.ManagerEnvironment = lib.concatStringsSep " " (
lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment
);
settings.Manager.DefaultEnvironment = "PATH=/bin:/sbin";
contents = {
"/init".source = "${cfg.package}/lib/systemd/systemd";
"/etc/systemd/system".source = stage1Units;
"/etc/systemd/system.conf".text = settingsToSections cfg.settings;
# We can use either ! or * to lock the root account in the
# console, but some software like OpenSSH won't even allow you
# to log in with an SSH key if you use ! so we use * instead
"/etc/shadow".text =
let
ea = cfg.emergencyAccess;
access = ea != null && !(isBool ea && !ea);
passwd = if isString ea then ea else "";
in
"root:${if access then passwd else "*"}:::::::";
"/bin".source = "${initrdBinEnv}/bin";
"/sbin".source = "${initrdBinEnv}/sbin";
"/usr/bin".source = "${initrdBinEnv}/bin";
"/usr/sbin".source = "${initrdBinEnv}/sbin";
"/etc/os-release".source = config.boot.initrd.osRelease;
"/etc/initrd-release".source = config.boot.initrd.osRelease;
# For systemd-journald's _HOSTNAME field; needs to be set early, cannot be backfilled.
"/etc/hostname".text = config.networking.hostName;
}
// optionalAttrs (config.environment.etc ? "modprobe.d/nixos.conf") {
"/etc/modprobe.d/nixos.conf".source = config.environment.etc."modprobe.d/nixos.conf".source;
}
// optionalAttrs withKmod {
"/lib".source = "${config.system.build.modulesClosure}/lib";
"/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules;
"/etc/sysctl.d/nixos.conf".text = "kernel.modprobe = /sbin/modprobe";
"/etc/modprobe.d/systemd.conf".source = "${cfg.package}/lib/modprobe.d/systemd.conf";
"/etc/modprobe.d/ubuntu.conf".source = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
"/etc/modprobe.d/debian.conf".source = pkgs.kmod-debian-aliases;
};
storePaths = [
# systemd tooling
"${cfg.package}/lib/systemd/systemd-executor"
"${cfg.package}/lib/systemd/systemd-fsck"
"${cfg.package}/lib/systemd/systemd-hibernate-resume"
"${cfg.package}/lib/systemd/systemd-journald"
"${cfg.package}/lib/systemd/systemd-makefs"
"${cfg.package}/lib/systemd/systemd-modules-load"
"${cfg.package}/lib/systemd/systemd-remount-fs"
"${cfg.package}/lib/systemd/systemd-shutdown"
"${cfg.package}/lib/systemd/systemd-sulogin-shell"
"${cfg.package}/lib/systemd/systemd-sysctl"
"${cfg.package}/lib/systemd/systemd-bsod"
"${cfg.package}/lib/systemd/systemd-sysroot-fstab-check"
"${cfg.package}/lib/systemd/systemd-factory-reset"
# generators
"${cfg.package}/lib/systemd/system-generators/systemd-debug-generator"
"${cfg.package}/lib/systemd/system-generators/systemd-fstab-generator"
"${cfg.package}/lib/systemd/system-generators/systemd-gpt-auto-generator"
"${cfg.package}/lib/systemd/system-generators/systemd-hibernate-resume-generator"
"${cfg.package}/lib/systemd/system-generators/systemd-run-generator"
"${cfg.package}/lib/systemd/system-generators/systemd-factory-reset-generator"
# utilities needed by systemd
"${cfg.package.util-linux}/bin/mount"
"${cfg.package.util-linux}/bin/umount"
"${cfg.package.util-linux}/bin/sulogin"
# Resolving sysroot symlinks without code exec
"${config.system.nixos-init.package}/bin/resolve-in-root"
# Find the etc paths
"${config.system.nixos-init.package}/bin/find-etc"
]
++ lib.optionals config.system.nixos-init.enable [
"${config.system.nixos-init.package}/bin/initrd-init"
]
++ lib.optionals cfg.shell.enable [
# required for services generated with writeShellScript and friends
pkgs.runtimeShell
# some tools like xfs still want the sh symlink
"${pkgs.bashNonInteractive}/bin"
]
++ jobScripts
++ [ unitEnv ]
++ map (c: removeAttrs c [ "text" ]) (builtins.attrValues cfg.contents)
++ lib.optional (pkgs.stdenv.hostPlatform.libc == "glibc") "${pkgs.glibc}/lib/libnss_files.so.2";
targets.initrd.aliases = [ "default.target" ];
units =
mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit v)) cfg.paths
// mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit v)) cfg.services
// mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit v)) cfg.slices
// mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit v)) cfg.sockets
// mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit v)) cfg.targets
// mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit v)) cfg.timers
// listToAttrs (
map (
v:
let
n = escapeSystemdPath v.where;
in
nameValuePair "${n}.mount" (mountToUnit v)
) cfg.mounts
)
// listToAttrs (
map (
v:
let
n = escapeSystemdPath v.where;
in
nameValuePair "${n}.automount" (automountToUnit v)
) cfg.automounts
);
services."modprobe@" = lib.mkIf withKmod {
serviceConfig.ExecSearchPath = lib.makeBinPath [ cfg.package.kmod ];
};
services.initrd-find-nixos-closure = lib.mkIf (!config.system.nixos-init.enable) {
description = "Find NixOS closure";
unitConfig = {
RequiresMountsFor = "/sysroot/nix/store";
DefaultDependencies = false;
};
before = [
"initrd.target"
"shutdown.target"
];
conflicts = [ "shutdown.target" ];
requiredBy = [ "initrd.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = # bash
''
set -uo pipefail
export PATH="/bin:${
lib.makeBinPath [
cfg.package.util-linux
config.system.nixos-init.package
]
}"
# Figure out what closure to boot
closure=
for o in $(< /proc/cmdline); do
case $o in
init=*)
IFS="=" read -r -a initParam <<< "$o"
closure="''${initParam[1]}"
;;
esac
done
# Sanity check
if [ -z "''${closure:-}" ]; then
echo 'No init= parameter on the kernel command line' >&2
exit 1
fi
# Resolve symlinks in the init parameter. We need this for some boot loaders
# (e.g. boot.loader.generationsDir).
closure="$(resolve-in-root /sysroot "$closure")"
# Assume the directory containing the init script is the closure.
closure="$(dirname "$closure")"
ln --symbolic "$closure" /nixos-closure
# If we are not booting a NixOS closure (e.g. init=/bin/sh),
# we don't know what root to prepare so we don't do anything
if ! [ -x "/sysroot$(readlink "/sysroot$closure/prepare-root" || echo "$closure/prepare-root")" ]; then
echo "NEW_INIT=''${initParam[1]}" > /etc/switch-root.conf
echo "$closure does not look like a NixOS installation - not activating"
exit 0
fi
echo 'NEW_INIT=' > /etc/switch-root.conf
'';
};
# We need to propagate /run for things like /run/booted-system
# and /run/current-system.
mounts = [
{
where = "/sysroot/run";
what = "/run";
options = "rbind";
unitConfig = {
# See the comment on the mount unit for /run/etc-metadata
DefaultDependencies = false;
};
requiredBy = [ "initrd-fs.target" ];
before = [ "initrd-fs.target" ];
}
];
services.initrd-nixos-activation = lib.mkIf (!config.system.nixos-init.enable) {
after = [ "initrd-switch-root.target" ];
requiredBy = [ "initrd-switch-root.service" ];
before = [ "initrd-switch-root.service" ];
unitConfig.DefaultDependencies = false;
unitConfig = {
AssertPathExists = "/etc/initrd-release";
RequiresMountsFor = [
"/sysroot/run"
];
};
serviceConfig.Type = "oneshot";
serviceConfig.EnvironmentFile = "-/etc/switch-root.conf";
description = "NixOS Activation";
script = # bash
''
set -uo pipefail
export PATH="/bin:${cfg.package.util-linux}/bin"
# A non-NixOS closure (e.g. init=/bin/sh) has no prepare-root;
# initrd-find-nixos-closure records this as a non-empty NEW_INIT.
# Skip activation and let initrd-switch-root hand over to it directly.
if [ -n "''${NEW_INIT:-}" ]; then
echo "$NEW_INIT is not a NixOS system - not activating"
exit 0
fi
closure="$(realpath /nixos-closure)"
# Initialize the system
export IN_NIXOS_SYSTEMD_STAGE1=true
exec chroot /sysroot "$closure/prepare-root"
'';
};
services.initrd-switch-root =
if config.system.nixos-init.enable then
{
path = [
cfg.package
cfg.package.util-linux
config.system.nixos-init.package
];
serviceConfig = {
ExecStart = [
""
"${config.system.nixos-init.package}/bin/initrd-init"
];
};
}
else
# This will either call systemctl with the new init as the last parameter (which
# is the case when not booting a NixOS system) or with an empty string, causing
# systemd to bypass its verification code that checks whether the next file is a systemd
# and using its compiled-in value
{
serviceConfig = {
EnvironmentFile = "-/etc/switch-root.conf";
ExecStart = [
""
''systemctl --no-block switch-root /sysroot "''${NEW_INIT}"''
];
};
};
services.panic-on-fail = {
wantedBy = [ "emergency.target" ];
unitConfig = {
DefaultDependencies = false;
ConditionKernelCommandLine = [
"|boot.panic_on_fail"
"|stage1panic"
];
};
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.coreutils}/bin/echo c";
StandardOutput = "file:/proc/sysrq-trigger";
};
};
};
};
}