From 52c98fc3e9b593f1b98caeb00864f884e897866f Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Sat, 19 Mar 2022 04:02:39 -0400 Subject: nixos: systemd: Split unit types into separate module --- nixos/modules/system/boot/systemd.nix | 28 +++++++++------------------- nixos/modules/system/boot/systemd/user.nix | 24 +++++++----------------- 2 files changed, 16 insertions(+), 36 deletions(-) (limited to 'nixos/modules') diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 057474c607ac8..396457e1a9f90 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -14,10 +14,6 @@ let makeUnit generateUnits makeJobScript - unitConfig - serviceConfig - mountConfig - automountConfig commonUnitText targetToUnit serviceToUnit @@ -185,13 +181,7 @@ in systemd.units = mkOption { description = "Definition of systemd units."; default = {}; - type = with types; attrsOf (submodule ( - { name, config, ... }: - { options = concreteUnitOptions; - config = { - unit = mkDefault (makeUnit name config); - }; - })); + type = systemdUtils.types.units; }; systemd.packages = mkOption { @@ -203,37 +193,37 @@ in systemd.targets = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] ); + type = systemdUtils.types.targets; description = "Definition of systemd target units."; }; systemd.services = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ]); + type = systemdUtils.types.services; description = "Definition of systemd service units."; }; systemd.sockets = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ]); + type = systemdUtils.types.sockets; description = "Definition of systemd socket units."; }; systemd.timers = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ]); + type = systemdUtils.types.timers; description = "Definition of systemd timer units."; }; systemd.paths = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]); + type = systemdUtils.types.paths; description = "Definition of systemd path units."; }; systemd.mounts = mkOption { default = []; - type = with types; listOf (submodule [ { options = mountOptions; } unitConfig mountConfig ]); + type = systemdUtils.types.mounts; description = '' Definition of systemd mount units. This is a list instead of an attrSet, because systemd mandates the names to be derived from @@ -243,7 +233,7 @@ in systemd.automounts = mkOption { default = []; - type = with types; listOf (submodule [ { options = automountOptions; } unitConfig automountConfig ]); + type = systemdUtils.types.automounts; description = '' Definition of systemd automount units. This is a list instead of an attrSet, because systemd mandates the names to be derived from @@ -253,7 +243,7 @@ in systemd.slices = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig] ); + type = systemdUtils.types.slices; description = "Definition of slice configurations."; }; diff --git a/nixos/modules/system/boot/systemd/user.nix b/nixos/modules/system/boot/systemd/user.nix index e30f83f3457f8..82bad43e9f440 100644 --- a/nixos/modules/system/boot/systemd/user.nix +++ b/nixos/modules/system/boot/systemd/user.nix @@ -12,10 +12,6 @@ let (systemdUtils.lib) makeUnit generateUnits - makeJobScript - unitConfig - serviceConfig - commonUnitText targetToUnit serviceToUnit socketToUnit @@ -57,48 +53,42 @@ in { 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); - }; - })); + type = systemdUtils.types.units; }; systemd.user.paths = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]); + type = systemdUtils.types.paths; description = "Definition of systemd per-user path units."; }; systemd.user.services = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] ); + type = systemdUtils.types.services; description = "Definition of systemd per-user service units."; }; systemd.user.slices = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ] ); + type = systemdUtils.types.slices; description = "Definition of systemd per-user slice units."; }; systemd.user.sockets = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] ); + type = systemdUtils.types.sockets; description = "Definition of systemd per-user socket units."; }; systemd.user.targets = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] ); + type = systemdUtils.types.targets; description = "Definition of systemd per-user target units."; }; systemd.user.timers = mkOption { default = {}; - type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] ); + type = systemdUtils.types.timers; description = "Definition of systemd per-user timer units."; }; -- cgit 1.4.1 From 25113740a5063483645cab857716a77437cd881e Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Sat, 19 Mar 2022 22:58:38 -0400 Subject: nixos: systemd-lib: Make generateUnits general with default args --- nixos/lib/systemd-lib.nix | 21 +++++++++++++-------- nixos/modules/system/boot/systemd.nix | 7 ++++++- nixos/modules/system/boot/systemd/nspawn.nix | 8 +++++++- nixos/modules/system/boot/systemd/user.nix | 7 ++++++- 4 files changed, 32 insertions(+), 11 deletions(-) (limited to 'nixos/modules') diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix index a472d97f5cc74..27c8b5b047132 100644 --- a/nixos/lib/systemd-lib.nix +++ b/nixos/lib/systemd-lib.nix @@ -120,10 +120,15 @@ in rec { (if isList value then value else [value])) as)); - generateUnits = generateUnits' true; - - generateUnits' = allowCollisions: type: units: upstreamUnits: upstreamWants: - pkgs.runCommand "${type}-units" + generateUnits = { allowCollisions ? true, type, units, upstreamUnits, upstreamWants, packages ? cfg.packages, package ? cfg.package }: + let + typeDir = ({ + system = "system"; + initrd = "system"; + user = "user"; + nspawn = "nspawn"; + }).${type}; + in pkgs.runCommand "${type}-units" { preferLocalBuild = true; allowSubstitutes = false; } '' @@ -131,7 +136,7 @@ in rec { # Copy the upstream systemd units we're interested in. for i in ${toString upstreamUnits}; do - fn=${cfg.package}/example/systemd/${type}/$i + fn=${package}/example/systemd/${typeDir}/$i if ! [ -e $fn ]; then echo "missing $fn"; false; fi if [ -L $fn ]; then target="$(readlink "$fn")" @@ -148,7 +153,7 @@ in rec { # 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 + fn=${package}/example/systemd/${typeDir}/$i if ! [ -e $fn ]; then echo "missing $fn"; false; fi x=$out/$(basename $fn) mkdir $x @@ -160,14 +165,14 @@ in rec { done # Symlink all units provided listed in systemd.packages. - packages="${toString cfg.packages}" + packages="${toString 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 + for fn in $i/etc/systemd/${typeDir}/* $i/lib/systemd/${typeDir}/*; do if ! [[ "$fn" =~ .wants$ ]]; then if [[ -d "$fn" ]]; then targetDir="$out/$(basename "$fn")" diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 396457e1a9f90..00116c73ccc60 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -461,7 +461,12 @@ in 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/system".source = generateUnits { + type = "system"; + units = enabledUnits; + upstreamUnits = enabledUpstreamSystemUnits; + upstreamWants = upstreamSystemWants; + }; "systemd/system.conf".text = '' [Manager] diff --git a/nixos/modules/system/boot/systemd/nspawn.nix b/nixos/modules/system/boot/systemd/nspawn.nix index 0c6822319a5b0..bf9995d03cc18 100644 --- a/nixos/modules/system/boot/systemd/nspawn.nix +++ b/nixos/modules/system/boot/systemd/nspawn.nix @@ -116,7 +116,13 @@ in { in mkMerge [ (mkIf (cfg != {}) { - environment.etc."systemd/nspawn".source = mkIf (cfg != {}) (generateUnits' false "nspawn" units [] []); + environment.etc."systemd/nspawn".source = mkIf (cfg != {}) (generateUnits { + allowCollisions = false; + type = "nspawn"; + inherit units; + upstreamUnits = []; + upstreamWants = []; + }); }) { systemd.targets.multi-user.wants = [ "machines.target" ]; diff --git a/nixos/modules/system/boot/systemd/user.nix b/nixos/modules/system/boot/systemd/user.nix index 82bad43e9f440..4951aef95584b 100644 --- a/nixos/modules/system/boot/systemd/user.nix +++ b/nixos/modules/system/boot/systemd/user.nix @@ -109,7 +109,12 @@ in { ]; environment.etc = { - "systemd/user".source = generateUnits "user" cfg.units upstreamUserUnits []; + "systemd/user".source = generateUnits { + type = "user"; + inherit (cfg) units; + upstreamUnits = upstreamUserUnits; + upstreamWants = []; + }; "systemd/user.conf".text = '' [Manager] -- cgit 1.4.1 From 2d4ebf1259149ac52c191f461eef4eae6c3671fc Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Sat, 19 Mar 2022 23:00:06 -0400 Subject: initrd: Optional systemd-based initrd --- nixos/lib/systemd-lib.nix | 29 ++- nixos/lib/systemd-types.nix | 1 + nixos/modules/module-list.nix | 1 + nixos/modules/system/boot/stage-1.nix | 8 +- nixos/modules/system/boot/systemd.nix | 3 - nixos/modules/system/boot/systemd/initrd.nix | 355 +++++++++++++++++++++++++++ pkgs/os-specific/linux/systemd/default.nix | 2 +- 7 files changed, 385 insertions(+), 14 deletions(-) create mode 100644 nixos/modules/system/boot/systemd/initrd.nix (limited to 'nixos/modules') diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix index 27c8b5b047132..3129fbe7bdb96 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 a7a324f187c28..b303335ffc1f3 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 49d1105247abf..2c1272ecd5a51 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 1575c0257d1c6..aa2f4fb860b70 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 00116c73ccc60..fd55007736269 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 0000000000000..cd626a915a414 --- /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, , and + . 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); + }; + }; +} diff --git a/pkgs/os-specific/linux/systemd/default.nix b/pkgs/os-specific/linux/systemd/default.nix index 3a3a419093b75..4cbed9b7cbf10 100644 --- a/pkgs/os-specific/linux/systemd/default.nix +++ b/pkgs/os-specific/linux/systemd/default.nix @@ -603,7 +603,7 @@ stdenv.mkDerivation { # runtime; otherwise we can't and we need to reboot. interfaceVersion = 2; - inherit withCryptsetup; + inherit withCryptsetup util-linux; tests = { inherit (nixosTests) switchTest; -- cgit 1.4.1 From be10e86c9587d3207c8d4a850a72ed88124d1aac Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Sat, 19 Mar 2022 23:00:27 -0400 Subject: systemd-initrd: Partially fix qemu-vm TODO: Auto formatting / partitioning / resizing --- nixos/modules/virtualisation/qemu-vm.nix | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'nixos/modules') diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 5143893589470..f8653a6f34a82 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -953,6 +953,28 @@ in }; } // lib.mapAttrs' mkSharedDir cfg.sharedDirectories); + boot.initrd.systemd = lib.mkIf (config.boot.initrd.systemd.enable && cfg.writableStore) { + mounts = [{ + where = "/sysroot/nix/store"; + what = "overlay"; + type = "overlay"; + options = "lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/store,workdir=/sysroot/nix/.rw-store/work"; + wantedBy = ["local-fs.target"]; + before = ["local-fs.target"]; + requires = ["sysroot-nix-.ro\\x2dstore.mount" "sysroot-nix-.rw\\x2dstore.mount" "rw-store.service"]; + after = ["sysroot-nix-.ro\\x2dstore.mount" "sysroot-nix-.rw\\x2dstore.mount" "rw-store.service"]; + unitConfig.IgnoreOnIsolate = true; + }]; + services.rw-store = { + after = ["sysroot-nix-.rw\\x2dstore.mount"]; + unitConfig.DefaultDependencies = false; + serviceConfig = { + Type = "oneshot"; + ExecStart = "/bin/mkdir -p 0755 /sysroot/nix/.rw-store/store /sysroot/nix/.rw-store/work /sysroot/nix/store"; + }; + }; + }; + swapDevices = mkVMOverride [ ]; boot.initrd.luks.devices = mkVMOverride {}; -- cgit 1.4.1 From 1abf15417972cd54eb7013e4a87f6b2de30d8590 Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Sun, 20 Mar 2022 16:00:43 -0400 Subject: systemd-initrd: Add PATH to everything --- nixos/modules/system/boot/systemd/initrd.nix | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'nixos/modules') diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index cd626a915a414..ecdb6b58de778 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -307,6 +307,14 @@ in { { object = "${cfg.package.util-linux}/bin/umount"; } { object = "${cfg.package.util-linux}/bin/sulogin"; } + { + object = builtins.toFile "system.conf" '' + [Manager] + DefaultEnvironment=PATH=/bin:/sbin + ''; + symlink = "/etc/systemd/system.conf"; + } + # TODO: Not sure why this needs to be here for the recovery shell to work { object = "${pkgs.glibc}/lib/libnss_files.so"; } @@ -332,7 +340,6 @@ in { } { 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"; } ]; -- cgit 1.4.1 From 213de9b10874370c9f14e41c8551fba701ed8030 Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Sun, 20 Mar 2022 16:02:47 -0400 Subject: systemd-initrd: autoFormat and autoResize in initrd --- nixos/modules/system/boot/systemd/initrd.nix | 22 +++++++++++++++++++--- nixos/modules/tasks/filesystems.nix | 2 +- nixos/modules/virtualisation/qemu-vm.nix | 2 ++ 3 files changed, 22 insertions(+), 4 deletions(-) (limited to 'nixos/modules') diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index ecdb6b58de778..8cf575eb61ad1 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -108,8 +108,9 @@ let 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); + ({ fsType, mountPoint, device, options, autoFormat, autoResize, ... }@fs: let + opts = options ++ optional autoFormat "x-systemd.makefs" ++ optional autoResize "x-systemd.growfs"; + in "${device} /sysroot${mountPoint} ${fsType} ${lib.concatStringsSep "," opts}") fileSystems); kernel-name = config.boot.kernelPackages.kernel.name or "kernel"; modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; }; @@ -294,7 +295,7 @@ in { config = mkIf (config.boot.initrd.enable && cfg.enable) { system.build = { inherit initialRamdisk; }; boot.initrd.systemd = { - initrdBin = [pkgs.bash pkgs.coreutils pkgs.kmod cfg.package]; + initrdBin = [pkgs.bash pkgs.coreutils pkgs.kmod cfg.package] ++ config.system.fsPackages; objects = [ { object = "${cfg.package}/lib/systemd/systemd"; symlink = "/init"; } @@ -357,6 +358,21 @@ in { // listToAttrs (map (v: let n = escapeSystemdPath v.where; in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts); + + # The unit in /run/systemd/generator shadows the unit in + # /etc/systemd/system, but will still apply drop-ins from + # /etc/systemd/system/foo.service.d/ + # + # We need IgnoreOnIsolate, otherwise the Requires dependency of + # a mount unit on its makefs unit causes it to be unmounted when + # we isolate for switch-root. Use a dummy package so that + # generateUnits will generate drop-ins instead of unit files. + packages = [(pkgs.runCommand "dummy" {} '' + mkdir -p $out/etc/systemd/system + touch $out/etc/systemd/system/systemd-{makefs,growfs}@.service + '')]; + services."systemd-makefs@".unitConfig.IgnoreOnIsolate = true; + services."systemd-growfs@".unitConfig.IgnoreOnIsolate = true; }; }; } diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix index f3da6771197e6..6fcc762ca88ca 100644 --- a/nixos/modules/tasks/filesystems.nix +++ b/nixos/modules/tasks/filesystems.nix @@ -323,7 +323,7 @@ in unitConfig.DefaultDependencies = false; # needed to prevent a cycle serviceConfig.Type = "oneshot"; }; - in listToAttrs (map formatDevice (filter (fs: fs.autoFormat) fileSystems)) // { + in listToAttrs (map formatDevice (filter (fs: fs.autoFormat && !(utils.fsNeededForBoot fs)) fileSystems)) // { # Mount /sys/fs/pstore for evacuating panic logs and crashdumps from persistent storage onto the disk using systemd-pstore. # This cannot be done with the other special filesystems because the pstore module (which creates the mount point) is not loaded then. "mount-pstore" = { diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index f8653a6f34a82..05738b9cfc1b8 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -923,6 +923,8 @@ in mkVMOverride (cfg.fileSystems // { "/".device = cfg.bootDevice; + "/".fsType = "ext4"; + "/".autoFormat = true; "/tmp" = mkIf config.boot.tmpOnTmpfs { device = "tmpfs"; -- cgit 1.4.1 From 5bfe21331548db28279776debc073b02de71e2e3 Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Tue, 22 Mar 2022 07:11:54 -0400 Subject: Clarify suppressed units description --- nixos/modules/system/boot/systemd.nix | 5 +++-- nixos/modules/system/boot/systemd/initrd.nix | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'nixos/modules') diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index fd55007736269..a21b2bab91587 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -339,10 +339,11 @@ in type = types.listOf types.str; example = [ "systemd-backlight@.service" ]; description = '' - A list of units to suppress when generating system systemd configuration directory. This has + A list of units to skip when generating system systemd configuration directory. This has priority over upstream units, , and . The main purpose of this is to - suppress a upstream systemd unit with any modifications made to it by other NixOS modules. + prevent a upstream systemd unit from being added to the initrd with any modifications made to it + by other NixOS modules. ''; }; diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 8cf575eb61ad1..2aeb6c5c965bf 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -205,10 +205,11 @@ in { 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, , and - . The main purpose of this is to - suppress a upstream systemd unit with any modifications made to it by other NixOS modules. + A list of units to skip when generating system systemd configuration directory. This has + priority over upstream units, , and + . 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. ''; }; -- cgit 1.4.1 From 74bae067487b1292edf5e97962de2bccb014ca15 Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Thu, 24 Mar 2022 18:51:32 +0100 Subject: systemd-initrd: use pkgs.fakeNss, document why we need libnss_files.so --- nixos/modules/system/boot/systemd/initrd.nix | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'nixos/modules') diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 2aeb6c5c965bf..61f8334cc384e 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -317,9 +317,6 @@ in { symlink = "/etc/systemd/system.conf"; } - # 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"; } @@ -332,10 +329,10 @@ in { 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 = "${pkgs.fakeNss}/etc/passwd"; symlink = "/etc/passwd"; } + # so NSS can look up usernames + { object = "${pkgs.glibc}/lib/libnss_files.so"; } { object = builtins.toFile "shadow" "root:${config.boot.initrd.systemd.emergencyHashedPassword}:::::::"; symlink = "/etc/shadow"; -- cgit 1.4.1 From e3083decc4dea10e34ac08b705ea74808cefa524 Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Thu, 24 Mar 2022 18:57:16 +0100 Subject: systemd-initrd, systemd-lib: drop initrdServiceToUnit We can just use serviceToUnit here. --- nixos/lib/systemd-lib.nix | 16 ---------------- nixos/modules/system/boot/systemd/initrd.nix | 14 +++++++------- 2 files changed, 7 insertions(+), 23 deletions(-) (limited to 'nixos/modules') diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix index 75f1d83c2cfd8..aba63886e0f58 100644 --- a/nixos/lib/systemd-lib.nix +++ b/nixos/lib/systemd-lib.nix @@ -391,22 +391,6 @@ in rec { ''; }; - initrdServiceToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy enable; - text = commonUnitText def + - '' - [Service] - ${let env = 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)} - ${attrsToSection def.serviceConfig} - ''; - }; - socketToUnit = name: def: { inherit (def) aliases wantedBy requiredBy enable; text = commonUnitText def + diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 61f8334cc384e..0e73e02066d44 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -7,7 +7,7 @@ let inherit (systemdUtils.lib) generateUnits pathToUnit - initrdServiceToUnit + serviceToUnit sliceToUnit socketToUnit targetToUnit @@ -344,12 +344,12 @@ in { 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 + 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) -- cgit 1.4.1 From b7c62b8238db03d4bb9808dbe80e79853d54ce02 Mon Sep 17 00:00:00 2001 From: Janne Heß Date: Fri, 1 Apr 2022 10:44:08 +0200 Subject: nixos/systemd-initrd: Remove unit options that don't work --- nixos/lib/systemd-lib.nix | 42 +-- nixos/lib/systemd-types.nix | 25 +- nixos/lib/systemd-unit-options.nix | 415 ++++++++++++++++++--------- nixos/modules/system/boot/systemd/initrd.nix | 10 +- 4 files changed, 302 insertions(+), 190 deletions(-) (limited to 'nixos/modules') diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix index dbf8d2db5b62a..589f1e2c71854 100644 --- a/nixos/lib/systemd-lib.nix +++ b/nixos/lib/systemd-lib.nix @@ -273,9 +273,9 @@ in rec { { Conflicts = toString config.conflicts; } // optionalAttrs (config.requisite != []) { Requisite = toString config.requisite; } - // optionalAttrs (config.restartTriggers != []) + // optionalAttrs (config ? restartTriggers && config.restartTriggers != []) { X-Restart-Triggers = toString config.restartTriggers; } - // optionalAttrs (config.reloadTriggers != []) + // optionalAttrs (config ? reloadTriggers && config.reloadTriggers != []) { X-Reload-Triggers = toString config.reloadTriggers; } // optionalAttrs (config.description != "") { Description = config.description; } @@ -291,36 +291,8 @@ in rec { }; }; - serviceConfig = { name, config, ... }: { - config = mkMerge - [ { - environment.PATH = mkIf (config.path != []) "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}"; - } - (mkIf (config ? preStart && config.preStart != "") - { serviceConfig.ExecStartPre = - [ (makeJobScript "${name}-pre-start" config.preStart) ]; - }) - (mkIf (config ? script && config.script != "") - { serviceConfig.ExecStart = - makeJobScript "${name}-start" config.script + " " + config.scriptArgs; - }) - (mkIf (config ? postStart && config.postStart != "") - { serviceConfig.ExecStartPost = - [ (makeJobScript "${name}-post-start" config.postStart) ]; - }) - (mkIf (config ? reload && config.reload != "") - { serviceConfig.ExecReload = - makeJobScript "${name}-reload" config.reload; - }) - (mkIf (config ? preStop && config.preStop != "") - { serviceConfig.ExecStop = - makeJobScript "${name}-pre-stop" config.preStop; - }) - (mkIf (config ? postStart && config.postStop != "") - { serviceConfig.ExecStopPost = - makeJobScript "${name}-post-stop" config.postStop; - }) - ]; + serviceConfig = { config, ... }: { + config.environment.PATH = mkIf (config.path != []) "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}"; }; stage2ServiceConfig = { @@ -384,12 +356,12 @@ in rec { # 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 '' + ${if def ? reloadIfChanged && def.reloadIfChanged then '' X-ReloadIfChanged=true - '' else if !def.restartIfChanged then '' + '' else if (def ? restartIfChanged && !def.restartIfChanged) then '' X-RestartIfChanged=false '' else ""} - ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"} + ${optionalString (def ? stopIfChanged && !def.stopIfChanged) "X-StopIfChanged=false"} ${attrsToSection def.serviceConfig} ''; }; diff --git a/nixos/lib/systemd-types.nix b/nixos/lib/systemd-types.nix index e7d504e586bea..71962fab2e177 100644 --- a/nixos/lib/systemd-types.nix +++ b/nixos/lib/systemd-types.nix @@ -11,20 +11,27 @@ rec { config = { unit = mkDefault (systemdUtils.lib.makeUnit name config); }; })); - services = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig stage2ServiceConfig ]); - initrdServices = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig stage1ServiceConfig ]); + services = with types; attrsOf (submodule [ stage2ServiceOptions unitConfig stage2ServiceConfig ]); + initrdServices = with types; attrsOf (submodule [ stage1ServiceOptions unitConfig stage1ServiceConfig ]); - targets = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig ]); + targets = with types; attrsOf (submodule [ stage2CommonUnitOptions unitConfig ]); + initrdTargets = with types; attrsOf (submodule [ stage1CommonUnitOptions unitConfig ]); - sockets = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ]); + sockets = with types; attrsOf (submodule [ stage2SocketOptions unitConfig ]); + initrdSockets = with types; attrsOf (submodule [ stage1SocketOptions unitConfig ]); - timers = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ]); + timers = with types; attrsOf (submodule [ stage2TimerOptions unitConfig ]); + initrdTimers = with types; attrsOf (submodule [ stage1TimerOptions unitConfig ]); - paths = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]); + paths = with types; attrsOf (submodule [ stage2PathOptions unitConfig ]); + initrdPaths = with types; attrsOf (submodule [ stage1PathOptions unitConfig ]); - slices = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ]); + slices = with types; attrsOf (submodule [ stage2SliceOptions unitConfig ]); + initrdSlices = with types; attrsOf (submodule [ stage1SliceOptions unitConfig ]); - mounts = with types; listOf (submodule [ { options = mountOptions; } unitConfig mountConfig ]); + mounts = with types; listOf (submodule [ stage2MountOptions unitConfig mountConfig ]); + initrdMounts = with types; listOf (submodule [ stage1MountOptions unitConfig mountConfig ]); - automounts = with types; listOf (submodule [ { options = automountOptions; } unitConfig automountConfig ]); + automounts = with types; listOf (submodule [ stage2AutomountOptions unitConfig automountConfig ]); + initrdAutomounts = with types; attrsOf (submodule [ stage1AutomountOptions unitConfig automountConfig ]); } diff --git a/nixos/lib/systemd-unit-options.nix b/nixos/lib/systemd-unit-options.nix index 8029ba0e3f6cf..c9d424d391196 100644 --- a/nixos/lib/systemd-unit-options.nix +++ b/nixos/lib/systemd-unit-options.nix @@ -94,7 +94,7 @@ in rec { }; - commonUnitOptions = sharedOptions // { + commonUnitOptions = { options = (sharedOptions // { description = mkOption { default = ""; @@ -191,27 +191,6 @@ in rec { ''; }; - 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. - ''; - }; - - reloadTriggers = mkOption { - default = []; - type = types.listOf unitOption; - description = '' - An arbitrary list of items such as derivations. If any item - in the list changes between reconfigurations, the service will - be reloaded. If anything but a reload trigger changes in the - unit file, the unit will be restarted instead. - ''; - }; - onFailure = mkOption { default = []; type = types.listOf unitNameType; @@ -239,10 +218,39 @@ in rec { ''; }; + }); }; + + stage2CommonUnitOptions = { + imports = [ + commonUnitOptions + ]; + + options = { + 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. + ''; + }; + + reloadTriggers = mkOption { + default = []; + type = types.listOf unitOption; + description = '' + An arbitrary list of items such as derivations. If any item + in the list changes between reconfigurations, the service will + be reloaded. If anything but a reload trigger changes in the + unit file, the unit will be restarted instead. + ''; + }; + }; }; + stage1CommonUnitOptions = commonUnitOptions; - - serviceOptions = commonUnitOptions // { + serviceOptions = { options = { environment = mkOption { default = {}; @@ -276,121 +284,164 @@ in rec { ''; }; - 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 is - ignored. - - This option should not be used anymore in favor of - which allows more granular - control of when a service is reloaded and when a service - is restarted. - ''; - }; - - stopIfChanged = mkOption { - type = types.bool; - default = true; - description = '' - If set, a changed unit is restarted by calling - systemctl stop in the old configuration, - then systemctl start in the new one. - Otherwise, it is restarted in a single step using - systemctl restart in the new configuration. - The latter is less correct because it runs the - ExecStop 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 - systemd.time - 7. This is equivalent - to adding a corresponding timer unit with - set to the value given here. - ''; - apply = v: if isList v then v else [ v ]; - }; + }; }; + + stage2ServiceOptions = { name, config, ... }: { + imports = [ + stage2CommonUnitOptions + serviceOptions + ]; + + options = { + 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 is + ignored. + + This option should not be used anymore in favor of + which allows more granular + control of when a service is reloaded and when a service + is restarted. + ''; + }; + + stopIfChanged = mkOption { + type = types.bool; + default = true; + description = '' + If set, a changed unit is restarted by calling + systemctl stop in the old configuration, + then systemctl start in the new one. + Otherwise, it is restarted in a single step using + systemctl restart in the new configuration. + The latter is less correct because it runs the + ExecStop 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 + systemd.time + 7. This is equivalent + to adding a corresponding timer unit with + set to the value given here. + ''; + apply = v: if isList v then v else [ v ]; + }; + }; + + config = mkMerge + [ (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; + }) + ]; + }; + stage1ServiceOptions = { + imports = [ + stage1CommonUnitOptions + serviceOptions + ]; }; - socketOptions = commonUnitOptions // { + socketOptions = { options = { listenStreams = mkOption { default = []; @@ -424,10 +475,24 @@ in rec { ''; }; + }; }; + + stage2SocketOptions = { + imports = [ + stage2CommonUnitOptions + socketOptions + ]; }; + stage1SocketOptions = { + imports = [ + stage1CommonUnitOptions + socketOptions + ]; + }; - timerOptions = commonUnitOptions // { + + timerOptions = { options = { timerConfig = mkOption { default = {}; @@ -443,10 +508,24 @@ in rec { ''; }; + }; }; + + stage2TimerOptions = { + imports = [ + stage2CommonUnitOptions + timerOptions + ]; }; + stage1TimerOptions = { + imports = [ + stage1CommonUnitOptions + timerOptions + ]; + }; - pathOptions = commonUnitOptions // { + + pathOptions = { options = { pathConfig = mkOption { default = {}; @@ -460,10 +539,24 @@ in rec { ''; }; + }; }; + + stage2PathOptions = { + imports = [ + stage2CommonUnitOptions + pathOptions + ]; + }; + + stage1PathOptions = { + imports = [ + stage1CommonUnitOptions + pathOptions + ]; }; - mountOptions = commonUnitOptions // { + mountOptions = { options = { what = mkOption { example = "/dev/sda1"; @@ -505,9 +598,23 @@ in rec { 5 for details. ''; }; + }; }; + + stage2MountOptions = { + imports = [ + stage2CommonUnitOptions + mountOptions + ]; + }; + + stage1MountOptions = { + imports = [ + stage1CommonUnitOptions + mountOptions + ]; }; - automountOptions = commonUnitOptions // { + automountOptions = { options = { where = mkOption { example = "/mnt"; @@ -529,11 +636,23 @@ in rec { 5 for details. ''; }; + }; }; + + stage2AutomountOptions = { + imports = [ + stage2CommonUnitOptions + automountOptions + ]; }; - targetOptions = commonUnitOptions; + stage1AutomountOptions = { + imports = [ + stage1CommonUnitOptions + automountOptions + ]; + }; - sliceOptions = commonUnitOptions // { + sliceOptions = { options = { sliceConfig = mkOption { default = {}; @@ -547,6 +666,20 @@ in rec { ''; }; + }; }; + + stage2SliceOptions = { + imports = [ + stage2CommonUnitOptions + sliceOptions + ]; + }; + + stage1SliceOptions = { + imports = [ + stage1CommonUnitOptions + sliceOptions + ]; }; } diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 0e73e02066d44..cfe539f76ddc0 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -231,7 +231,7 @@ in { targets = mkOption { default = {}; visible = false; - type = systemdUtils.types.targets; + type = systemdUtils.types.initrdTargets; description = "Definition of systemd target units."; }; @@ -244,28 +244,28 @@ in { sockets = mkOption { default = {}; - type = systemdUtils.types.sockets; + type = systemdUtils.types.initrdSockets; visible = false; description = "Definition of systemd socket units."; }; timers = mkOption { default = {}; - type = systemdUtils.types.timers; + type = systemdUtils.types.initrdTimers; visible = false; description = "Definition of systemd timer units."; }; paths = mkOption { default = {}; - type = systemdUtils.types.paths; + type = systemdUtils.types.initrdPaths; visible = false; description = "Definition of systemd path units."; }; mounts = mkOption { default = []; - type = systemdUtils.types.mounts; + type = systemdUtils.types.initrdMounts; visible = false; description = '' Definition of systemd mount units. -- cgit 1.4.1 From c465c8d7190d4b6ad60eb6574662269663c27811 Mon Sep 17 00:00:00 2001 From: Janne Heß Date: Fri, 1 Apr 2022 10:58:38 +0200 Subject: nixos/systemd-initrd: Make emergency access more flexible --- nixos/modules/system/boot/systemd/initrd.nix | 17 +++++++++++------ nixos/tests/systemd-initrd-simple.nix | 5 ++++- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'nixos/modules') diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index cfe539f76ddc0..b192677e9b8de 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -169,15 +169,17 @@ in { }); }; - emergencyHashedPassword = mkOption { - type = types.str; + emergencyAccess = mkOption { + type = with types; oneOf [ bool singleLineStr ]; visible = false; description = '' - Hashed password for the super user account in stage 1 emergency mode + Set to true for unauthenticated emergency access, and false for + no emergency access. - Blank for no password, ! for super user disabled. + Can also be set to a hashed super user password to allow + authenticated access to the emergency mode. ''; - default = "!"; + default = false; }; initrdBin = mkOption { @@ -334,7 +336,7 @@ in { # so NSS can look up usernames { object = "${pkgs.glibc}/lib/libnss_files.so"; } { - object = builtins.toFile "shadow" "root:${config.boot.initrd.systemd.emergencyHashedPassword}:::::::"; + object = builtins.toFile "shadow" "root:${if isBool cfg.emergencyAccess then "!" else cfg.emergencyAccess}:::::::"; symlink = "/etc/shadow"; } { object = "${initrdBinEnv}/bin"; symlink = "/bin"; } @@ -357,6 +359,9 @@ in { (v: let n = escapeSystemdPath v.where; in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts); + services.emergency = mkIf (isBool cfg.emergencyAccess && cfg.emergencyAccess) { + environment.SYSTEMD_SULOGIN_FORCE = "1"; + }; # The unit in /run/systemd/generator shadows the unit in # /etc/systemd/system, but will still apply drop-ins from # /etc/systemd/system/foo.service.d/ diff --git a/nixos/tests/systemd-initrd-simple.nix b/nixos/tests/systemd-initrd-simple.nix index 4b3f56e472af4..ba62cdf3bbc77 100644 --- a/nixos/tests/systemd-initrd-simple.nix +++ b/nixos/tests/systemd-initrd-simple.nix @@ -2,7 +2,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { name = "systemd-initrd-simple"; machine = { pkgs, ... }: { - boot.initrd.systemd.enable = true; + boot.initrd.systemd = { + enable = true; + emergencyAccess = true; + }; fileSystems = lib.mkVMOverride { "/".autoResize = true; }; -- cgit 1.4.1 From 5653209ed95985034bbb43bb63e04190639477a0 Mon Sep 17 00:00:00 2001 From: Janne Heß Date: Fri, 1 Apr 2022 13:26:06 +0200 Subject: nixos/systemd-initrd: Redo object specifications As requested by @roberth, we now have an option similar to environment.etc. There's also extra store paths to copy and a way to suppress store paths to make customizations possible. We also link mount and umount to /bin to make recovery easier when something fails --- nixos/modules/system/boot/systemd/initrd.nix | 134 +++++++++++++++++---------- 1 file changed, 86 insertions(+), 48 deletions(-) (limited to 'nixos/modules') diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index b192677e9b8de..1d94f7fef8e9b 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -127,10 +127,16 @@ let name = "initrd-emergency-env"; paths = map getBin cfg.initrdBin; pathsToLink = ["/bin" "/sbin"]; + # Make recovery easier + postBuild = '' + ln -s ${cfg.package.util-linux}/bin/mount $out/bin/ + ln -s ${cfg.package.util-linux}/bin/umount $out/bin/ + ''; }; initialRamdisk = pkgs.makeInitrdNG { - contents = cfg.objects; + contents = map (path: { object = path; symlink = ""; }) (subtractLists cfg.suppressedStorePaths cfg.storePaths) + ++ mapAttrsToList (_: v: { object = v.source; symlink = v.target; }) (filterAttrs (_: v: v.enable) cfg.contents); }; in { @@ -142,31 +148,69 @@ in { not yet supported by the intrd generated with this option. ''; - package = (lib.mkPackageOption pkgs "systemd" { + package = (mkPackageOption pkgs "systemd" { default = "systemdMinimal"; }) // { visible = false; }; - objects = mkOption { - description = "List of objects to include in the initrd, and their symlinks"; + contents = mkOption { + description = "Set of files that have to be linked into the initrd"; example = literalExpression '' - [ { object = "''${systemd}/lib/systemd/systemd"; symlink = "/init"; } ] + { + "/etc/hostname".text = "mymachine"; + } ''; visible = false; - type = types.listOf (types.submodule { + default = {}; + type = types.attrsOf (types.submodule ({ config, options, name, ... }: { options = { - object = mkOption { + enable = mkEnableOption "copying of this file to initrd and symlinking it" // { default = true; }; + + target = mkOption { type = types.path; - description = "The object to include in initrd."; + description = '' + Path of the symlink. + ''; + default = name; }; - symlink = mkOption { - type = types.nullOr types.path; - description = "A symlink to create in initrd pointing to the object."; + + text = mkOption { default = null; + type = types.nullOr types.lines; + description = "Text of the file."; + }; + + source = mkOption { + type = types.path; + description = "Path of the source file."; }; }; - }); + + config = { + source = mkIf (config.text != null) ( + let name' = "initrd-" + baseNameOf name; + in mkDerivedConfig options.text (pkgs.writeText name') + ); + }; + })); + }; + + storePaths = mkOption { + description = '' + Store paths to copy into the initrd as well. + ''; + type = types.listOf types.singleLineStr; + default = []; + }; + + suppressedStorePaths = mkOption { + description = '' + Store paths specified in the storePaths option that + should not be copied. + ''; + type = types.listOf types.singleLineStr; + default = []; }; emergencyAccess = mkOption { @@ -300,48 +344,42 @@ in { boot.initrd.systemd = { initrdBin = [pkgs.bash pkgs.coreutils pkgs.kmod cfg.package] ++ config.system.fsPackages; - objects = [ - { object = "${cfg.package}/lib/systemd/systemd"; symlink = "/init"; } - { object = stage1Units; symlink = "/etc/systemd/system"; } + contents = { + "/init".source = "${cfg.package}/lib/systemd/systemd"; + "/etc/systemd/system".source = stage1Units; - # TODO: Limit this to the bare necessities - { object = "${cfg.package}/lib"; } + "/etc/systemd/system.conf".text = '' + [Manager] + DefaultEnvironment=PATH=/bin:/sbin + ''; - { object = "${cfg.package.util-linux}/bin/mount"; } - { object = "${cfg.package.util-linux}/bin/umount"; } - { object = "${cfg.package.util-linux}/bin/sulogin"; } + "/etc/initrd-release".source = config.environment.etc.os-release.source; + "/etc/os-release".source = config.environment.etc.os-release.source; + "/etc/fstab".source = fstab; - { - object = builtins.toFile "system.conf" '' - [Manager] - DefaultEnvironment=PATH=/bin:/sbin - ''; - symlink = "/etc/systemd/system.conf"; - } + "/lib/modules".source = "${modulesClosure}/lib/modules"; - { 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); - } + "/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules; + + "/etc/passwd".source = "${pkgs.fakeNss}/etc/passwd"; + "/etc/shadow".text = "root:${if isBool cfg.emergencyAccess then "!" else cfg.emergencyAccess}:::::::"; + + "/bin".source = "${initrdBinEnv}/bin"; + "/sbin".source = "${initrdBinEnv}/sbin"; + + "/etc/sysctl.d/nixos.conf".text = "kernel.modprobe = /sbin/modprobe"; + }; + + storePaths = [ + # TODO: Limit this to the bare necessities + "${cfg.package}/lib" + + "${cfg.package.util-linux}/bin/mount" + "${cfg.package.util-linux}/bin/umount" + "${cfg.package.util-linux}/bin/sulogin" - { object = "${pkgs.fakeNss}/etc/passwd"; symlink = "/etc/passwd"; } # so NSS can look up usernames - { object = "${pkgs.glibc}/lib/libnss_files.so"; } - { - object = builtins.toFile "shadow" "root:${if isBool cfg.emergencyAccess then "!" else cfg.emergencyAccess}:::::::"; - symlink = "/etc/shadow"; - } - { object = "${initrdBinEnv}/bin"; symlink = "/bin"; } - { object = "${initrdBinEnv}/sbin"; symlink = "/sbin"; } - { object = builtins.toFile "sysctl.conf" "kernel.modprobe = /sbin/modprobe"; symlink = "/etc/sysctl.d/nixos.conf"; } + "${pkgs.glibc}/lib/libnss_files.so" ]; targets.initrd.aliases = ["default.target"]; -- cgit 1.4.1 From 7ebb4ebe404a7ecf6ee9761aab7d02cd4e2a4abc Mon Sep 17 00:00:00 2001 From: Janne Heß Date: Fri, 1 Apr 2022 13:54:09 +0200 Subject: nixos/systemd-stage-1: Append (Initrd) to /etc/initrd-release This is more in line with what dracut does (it appends "Initramfs") and makes it clear where the boot is currently at when it hangs. --- nixos/modules/misc/version.nix | 40 ++++++++++++++++++---------- nixos/modules/system/boot/systemd/initrd.nix | 2 -- 2 files changed, 26 insertions(+), 16 deletions(-) (limited to 'nixos/modules') diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix index 6c072021ed834..5411bf3bc55e1 100644 --- a/nixos/modules/misc/version.nix +++ b/nixos/modules/misc/version.nix @@ -11,6 +11,26 @@ let attrsToText = attrs: concatStringsSep "\n" (mapAttrsToList (n: v: ''${n}="${toString v}"'') attrs); + osReleaseContents = { + NAME = "NixOS"; + ID = "nixos"; + VERSION = "${cfg.release} (${cfg.codeName})"; + VERSION_CODENAME = toLower cfg.codeName; + VERSION_ID = cfg.release; + BUILD_ID = cfg.version; + PRETTY_NAME = "NixOS ${cfg.release} (${cfg.codeName})"; + LOGO = "nix-snowflake"; + HOME_URL = "https://nixos.org/"; + DOCUMENTATION_URL = "https://nixos.org/learn.html"; + SUPPORT_URL = "https://nixos.org/community.html"; + BUG_REPORT_URL = "https://github.com/NixOS/nixpkgs/issues"; + }; + + initrdReleaseContents = osReleaseContents // { + PRETTY_NAME = "${osReleaseContents.PRETTY_NAME} (Initrd)"; + }; + initrdRelease = pkgs.writeText "initrd-release" (attrsToText initrdReleaseContents); + in { imports = [ @@ -115,20 +135,12 @@ in DISTRIB_DESCRIPTION = "NixOS ${cfg.release} (${cfg.codeName})"; }; - "os-release".text = attrsToText { - NAME = "NixOS"; - ID = "nixos"; - VERSION = "${cfg.release} (${cfg.codeName})"; - VERSION_CODENAME = toLower cfg.codeName; - VERSION_ID = cfg.release; - BUILD_ID = cfg.version; - PRETTY_NAME = "NixOS ${cfg.release} (${cfg.codeName})"; - LOGO = "nix-snowflake"; - HOME_URL = "https://nixos.org/"; - DOCUMENTATION_URL = "https://nixos.org/learn.html"; - SUPPORT_URL = "https://nixos.org/community.html"; - BUG_REPORT_URL = "https://github.com/NixOS/nixpkgs/issues"; - }; + "os-release".text = attrsToText osReleaseContents; + }; + + boot.initrd.systemd.contents = { + "/etc/os-release".source = initrdRelease; + "/etc/initrd-release".source = initrdRelease; }; }; diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 1d94f7fef8e9b..30bdc9a3422c7 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -353,8 +353,6 @@ in { DefaultEnvironment=PATH=/bin:/sbin ''; - "/etc/initrd-release".source = config.environment.etc.os-release.source; - "/etc/os-release".source = config.environment.etc.os-release.source; "/etc/fstab".source = fstab; "/lib/modules".source = "${modulesClosure}/lib/modules"; -- cgit 1.4.1