diff options
Diffstat (limited to 'nixpkgs/nixos/modules/system/boot/systemd')
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/coredump.nix | 78 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/homed.nix | 43 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/initrd-secrets.nix | 36 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/initrd.nix | 560 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/journald.nix | 131 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/logind.nix | 205 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix | 132 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/oomd.nix | 57 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/repart.nix | 148 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/shutdown.nix | 66 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix | 136 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/tmpfiles.nix | 225 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/user.nix | 238 | ||||
-rw-r--r-- | nixpkgs/nixos/modules/system/boot/systemd/userdbd.nix | 18 |
14 files changed, 2073 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix b/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix new file mode 100644 index 000000000000..03ef00e5683c --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix @@ -0,0 +1,78 @@ +{ config, lib, pkgs, utils, ... }: + +with lib; + +let + cfg = config.systemd.coredump; + systemd = config.systemd.package; +in { + options = { + systemd.coredump.enable = mkOption { + default = true; + type = types.bool; + description = lib.mdDoc '' + Whether core dumps should be processed by + {command}`systemd-coredump`. If disabled, core dumps + appear in the current directory of the crashing process. + ''; + }; + + systemd.coredump.extraConfig = mkOption { + default = ""; + type = types.lines; + example = "Storage=journal"; + description = lib.mdDoc '' + Extra config options for systemd-coredump. See coredump.conf(5) man page + for available options. + ''; + }; + }; + + config = mkMerge [ + + (mkIf cfg.enable { + systemd.additionalUpstreamSystemUnits = [ + "systemd-coredump.socket" + "systemd-coredump@.service" + ]; + + environment.etc = { + "systemd/coredump.conf".text = + '' + [Coredump] + ${cfg.extraConfig} + ''; + + # install provided sysctl snippets + "sysctl.d/50-coredump.conf".source = + # Fix systemd-coredump error caused by truncation of `kernel.core_pattern` + # when the `systemd` derivation name is too long. This works by substituting + # the path to `systemd` with a symlink that has a constant-length path. + # + # See: https://github.com/NixOS/nixpkgs/issues/213408 + pkgs.substitute { + src = "${systemd}/example/sysctl.d/50-coredump.conf"; + replacements = [ + "--replace" + "${systemd}" + "${pkgs.symlinkJoin { name = "systemd"; paths = [ systemd ]; }}" + ]; + }; + + "sysctl.d/50-default.conf".source = "${systemd}/example/sysctl.d/50-default.conf"; + }; + + users.users.systemd-coredump = { + uid = config.ids.uids.systemd-coredump; + group = "systemd-coredump"; + }; + users.groups.systemd-coredump = {}; + }) + + (mkIf (!cfg.enable) { + boot.kernel.sysctl."kernel.core_pattern" = mkDefault "core"; + }) + + ]; + +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/homed.nix b/nixpkgs/nixos/modules/system/boot/systemd/homed.nix new file mode 100644 index 000000000000..b216820c0c0c --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/homed.nix @@ -0,0 +1,43 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.homed; +in +{ + options.services.homed.enable = lib.mkEnableOption (lib.mdDoc '' + systemd home area/user account manager + ''); + + config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = config.services.nscd.enable; + message = "systemd-homed requires the use of systemd nss module. services.nscd.enable must be set to true,"; + } + ]; + + systemd.additionalUpstreamSystemUnits = [ + "systemd-homed.service" + "systemd-homed-activate.service" + ]; + + # This is mentioned in homed's [Install] section. + # + # While homed appears to work without it, it's probably better + # to follow upstream recommendations. + services.userdbd.enable = lib.mkDefault true; + + systemd.services = { + systemd-homed = { + # These packages are required to manage encrypted volumes + path = config.system.fsPackages; + aliases = [ "dbus-org.freedesktop.home1.service" ]; + wantedBy = [ "multi-user.target" ]; + }; + + systemd-homed-activate = { + wantedBy = [ "systemd-homed.service" ]; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/initrd-secrets.nix b/nixpkgs/nixos/modules/system/boot/systemd/initrd-secrets.nix new file mode 100644 index 000000000000..7b59c0cbe7b8 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/initrd-secrets.nix @@ -0,0 +1,36 @@ +{ config, pkgs, lib, ... }: + +{ + config = lib.mkIf (config.boot.initrd.enable && config.boot.initrd.systemd.enable) { + # Copy secrets into the initrd if they cannot be appended + boot.initrd.systemd.contents = lib.mkIf (!config.boot.loader.supportsInitrdSecrets) + (lib.mapAttrs' (dest: source: lib.nameValuePair "/.initrd-secrets/${dest}" { source = if source == null then dest else source; }) config.boot.initrd.secrets); + + # Copy secrets to their respective locations + boot.initrd.systemd.services.initrd-nixos-copy-secrets = lib.mkIf (config.boot.initrd.secrets != {}) { + description = "Copy secrets into place"; + # Run as early as possible + wantedBy = [ "sysinit.target" ]; + before = [ "cryptsetup-pre.target" ]; + unitConfig.DefaultDependencies = false; + + # We write the secrets to /.initrd-secrets and move them because this allows + # secrets to be written to /run. If we put the secret directly to /run and + # drop this service, we'd mount the /run tmpfs over the secret, making it + # invisible in stage 2. + script = '' + for secret in $(cd /.initrd-secrets; find . -type f -o -type l); do + mkdir -p "$(dirname "/$secret")" + cp "/.initrd-secrets/$secret" "/$secret" + done + ''; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + # The script needs this + boot.initrd.systemd.extraBin.find = "${pkgs.findutils}/bin/find"; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/initrd.nix b/nixpkgs/nixos/modules/system/boot/systemd/initrd.nix new file mode 100644 index 000000000000..be40b8e969a1 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/initrd.nix @@ -0,0 +1,560 @@ +{ lib, options, config, utils, pkgs, ... }: + +with lib; + +let + inherit (utils) systemdUtils escapeSystemdPath; + inherit (systemdUtils.lib) + generateUnits + pathToUnit + serviceToUnit + 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" + "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-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" + "systemd-tmpfiles-setup-dev.service" + "systemd-tmpfiles-setup.service" + "timers.target" + "umount.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; + jobScripts = concatLists (mapAttrsToList (_: unit: unit.jobScripts or []) (filterAttrs (_: v: v.enable) cfg.services)); + + stage1Units = generateUnits { + type = "initrd"; + units = enabledUnits; + upstreamUnits = enabledUpstreamUnits; + inherit upstreamWants; + inherit (cfg) packages package; + }; + + fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems; + + needMakefs = lib.any (fs: fs.autoFormat) 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-bin-env"; + paths = map getBin cfg.initrdBin; + pathsToLink = ["/bin" "/sbin"]; + postBuild = concatStringsSep "\n" (mapAttrsToList (n: v: "ln -sf '${v}' $out/bin/'${n}'") cfg.extraBin); + }; + + initialRamdisk = pkgs.makeInitrdNG { + name = "initrd-${kernel-name}"; + inherit (config.boot.initrd) compressor compressorArgs prepend; + inherit (cfg) strip; + + 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 { + options.boot.initrd.systemd = { + enable = mkEnableOption (lib.mdDoc "systemd in initrd") // { + description = lib.mdDoc '' + 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. + ''; + }; + + extraConfig = mkOption { + default = ""; + type = types.lines; + example = "DefaultLimitCORE=infinity"; + description = lib.mdDoc '' + Extra config options for systemd. See systemd-system.conf(5) man page + for available options. + ''; + }; + + managerEnvironment = mkOption { + type = with types; attrsOf (nullOr (oneOf [ str path package ])); + default = {}; + example = { SYSTEMD_LOG_LEVEL = "debug"; }; + description = lib.mdDoc '' + Environment variables of PID 1. These variables are + *not* passed to started units. + ''; + }; + + contents = mkOption { + description = lib.mdDoc "Set of files that have to be linked into the initrd"; + example = literalExpression '' + { + "/etc/hostname".text = "mymachine"; + } + ''; + default = {}; + type = utils.systemdUtils.types.initrdContents; + }; + + storePaths = mkOption { + description = lib.mdDoc '' + Store paths to copy into the initrd as well. + ''; + type = with types; listOf (oneOf [ singleLineStr package ]); + default = []; + }; + + strip = mkOption { + description = lib.mdDoc '' + Whether to completely strip executables and libraries copied to the initramfs. + + Setting this to false may save on the order of 30MiB on the + machine building the system (by avoiding a binutils + reference), at the cost of ~1MiB of initramfs size. This puts + this option firmly in the territory of micro-optimisation. + ''; + type = types.bool; + default = true; + }; + + extraBin = mkOption { + description = lib.mdDoc '' + Tools to add to /bin + ''; + example = literalExpression '' + { + umount = ''${pkgs.util-linux}/bin/umount; + } + ''; + type = types.attrsOf types.path; + default = {}; + }; + + suppressedStorePaths = mkOption { + description = lib.mdDoc '' + Store paths specified in the storePaths option that + should not be copied. + ''; + type = types.listOf types.singleLineStr; + default = []; + }; + + emergencyAccess = mkOption { + type = with types; oneOf [ bool (nullOr (passwdEntry str)) ]; + description = lib.mdDoc '' + Set to true for unauthenticated emergency access, and false for + no emergency access. + + Can also be set to a hashed super user password to allow + authenticated access to the emergency mode. + ''; + default = false; + }; + + initrdBin = mkOption { + type = types.listOf types.package; + default = []; + description = lib.mdDoc '' + 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 = lib.mdDoc '' + Additional units shipped with systemd that shall be enabled. + ''; + }; + + suppressedUnits = mkOption { + default = [ ]; + type = types.listOf types.str; + example = [ "systemd-backlight@.service" ]; + description = lib.mdDoc '' + 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. + ''; + }; + + units = mkOption { + description = lib.mdDoc "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 = lib.mdDoc "Packages providing systemd units and hooks."; + }; + + targets = mkOption { + default = {}; + visible = "shallow"; + type = systemdUtils.types.initrdTargets; + description = lib.mdDoc "Definition of systemd target units."; + }; + + services = mkOption { + default = {}; + type = systemdUtils.types.initrdServices; + visible = "shallow"; + description = lib.mdDoc "Definition of systemd service units."; + }; + + sockets = mkOption { + default = {}; + type = systemdUtils.types.initrdSockets; + visible = "shallow"; + description = lib.mdDoc "Definition of systemd socket units."; + }; + + timers = mkOption { + default = {}; + type = systemdUtils.types.initrdTimers; + visible = "shallow"; + description = lib.mdDoc "Definition of systemd timer units."; + }; + + paths = mkOption { + default = {}; + type = systemdUtils.types.initrdPaths; + visible = "shallow"; + description = lib.mdDoc "Definition of systemd path units."; + }; + + mounts = mkOption { + default = []; + type = systemdUtils.types.initrdMounts; + visible = "shallow"; + description = lib.mdDoc '' + 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 = lib.mdDoc '' + 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 = lib.mdDoc "Definition of slice configurations."; + }; + + enableTpm2 = mkOption { + default = true; + type = types.bool; + description = lib.mdDoc '' + Whether to enable TPM2 support in the initrd. + ''; + }; + }; + + config = mkIf (config.boot.initrd.enable && cfg.enable) { + assertions = map (name: { + assertion = lib.attrByPath name (throw "impossible") config.boot.initrd == ""; + message = '' + systemd stage 1 does not support 'boot.initrd.${lib.concatStringsSep "." name}'. Please + convert it to analogous systemd units in 'boot.initrd.systemd'. + + Definitions: + ${lib.concatMapStringsSep "\n" ({ file, ... }: " - ${file}") (lib.attrByPath name (throw "impossible") options.boot.initrd).definitionsWithLocations} + ''; + }) [ + [ "preFailCommands" ] + [ "preDeviceCommands" ] + [ "preLVMCommands" ] + [ "postDeviceCommands" ] + [ "postMountCommands" ] + [ "extraUdevRulesCommands" ] + [ "extraUtilsCommands" ] + [ "extraUtilsCommandsTest" ] + [ "network" "postCommands" ] + ]; + + system.build = { inherit initialRamdisk; }; + + boot.initrd.availableKernelModules = [ + # systemd needs this for some features + "autofs4" + # systemd-cryptenroll + ] ++ lib.optional cfg.enableTpm2 "tpm-tis" + ++ lib.optional (cfg.enableTpm2 && !(pkgs.stdenv.hostPlatform.isRiscV64 || pkgs.stdenv.hostPlatform.isArmv7)) "tpm-crb"; + + boot.initrd.systemd = { + initrdBin = [pkgs.bash pkgs.coreutils cfg.package.kmod cfg.package]; + 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"; + + contents = { + "/tmp/.keep".text = "systemd requires the /tmp mount point in the initrd cpio archive"; + "/init".source = "${cfg.package}/lib/systemd/systemd"; + "/etc/systemd/system".source = stage1Units; + + "/etc/systemd/system.conf".text = '' + [Manager] + DefaultEnvironment=PATH=/bin:/sbin + ${cfg.extraConfig} + ManagerEnvironment=${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment)} + ''; + + "/lib/modules".source = "${modulesClosure}/lib/modules"; + "/lib/firmware".source = "${modulesClosure}/lib/firmware"; + + "/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules; + + # 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 = "root:${if isBool cfg.emergencyAccess then optionalString (!cfg.emergencyAccess) "*" else cfg.emergencyAccess}:::::::"; + + "/bin".source = "${initrdBinEnv}/bin"; + "/sbin".source = "${initrdBinEnv}/sbin"; + + "/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.runCommand "initrd-kmod-blacklist-ubuntu" { } '' + ${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out + ''; + "/etc/modprobe.d/debian.conf".source = pkgs.kmod-debian-aliases; + + "/etc/os-release".source = config.boot.initrd.osRelease; + "/etc/initrd-release".source = config.boot.initrd.osRelease; + + } // optionalAttrs (config.environment.etc ? "modprobe.d/nixos.conf") { + "/etc/modprobe.d/nixos.conf".source = config.environment.etc."modprobe.d/nixos.conf".source; + }; + + storePaths = [ + # systemd tooling + "${cfg.package}/lib/systemd/systemd-fsck" + "${cfg.package}/lib/systemd/systemd-hibernate-resume" + "${cfg.package}/lib/systemd/systemd-journald" + (lib.mkIf needMakefs "${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" + + # 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" + + # utilities needed by systemd + "${cfg.package.util-linux}/bin/mount" + "${cfg.package.util-linux}/bin/umount" + "${cfg.package.util-linux}/bin/sulogin" + + # so NSS can look up usernames + "${pkgs.glibc}/lib/libnss_files.so.2" + ] ++ optionals (cfg.package.withCryptsetup && cfg.enableTpm2) [ + # tpm2 support + "${cfg.package}/lib/cryptsetup/libcryptsetup-token-systemd-tpm2.so" + pkgs.tpm2-tss + ] ++ optionals cfg.package.withCryptsetup [ + # fido2 support + "${cfg.package}/lib/cryptsetup/libcryptsetup-token-systemd-fido2.so" + "${pkgs.libfido2}/lib/libfido2.so.1" + ] ++ jobScripts; + + targets.initrd.aliases = ["default.target"]; + units = + 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) + // listToAttrs (map + (v: let n = escapeSystemdPath v.where; + in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts); + + # make sure all the /dev nodes are set up + services.systemd-tmpfiles-setup-dev.wantedBy = ["sysinit.target"]; + + services.initrd-nixos-activation = { + after = [ "initrd-fs.target" ]; + requiredBy = [ "initrd.target" ]; + unitConfig.AssertPathExists = "/etc/initrd-release"; + serviceConfig.Type = "oneshot"; + description = "NixOS Activation"; + + script = /* bash */ '' + set -uo pipefail + export PATH="/bin:${cfg.package.util-linux}/bin" + + # Figure out what closure to boot + closure= + for o in $(< /proc/cmdline); do + case $o in + init=*) + IFS== read -r -a initParam <<< "$o" + closure="$(dirname "''${initParam[1]}")" + ;; + esac + done + + # Sanity check + if [ -z "''${closure:-}" ]; then + echo 'No init= parameter on the kernel command line' >&2 + exit 1 + fi + + # 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. + mkdir -p /sysroot/run + mount --bind /run /sysroot/run + + # Initialize the system + export IN_NIXOS_SYSTEMD_STAGE1=true + exec chroot /sysroot $closure/prepare-root + ''; + }; + + # 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 + services.initrd-switch-root.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" + ]; + }; + script = '' + echo c > /proc/sysrq-trigger + ''; + serviceConfig.Type = "oneshot"; + }; + }; + + boot.kernelParams = lib.mkIf (config.boot.resumeDevice != "") [ "resume=${config.boot.resumeDevice}" ]; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/journald.nix b/nixpkgs/nixos/modules/system/boot/systemd/journald.nix new file mode 100644 index 000000000000..773163bbcb81 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/journald.nix @@ -0,0 +1,131 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.journald; +in { + options = { + services.journald.console = mkOption { + default = ""; + type = types.str; + description = lib.mdDoc "If non-empty, write log messages to the specified TTY device."; + }; + + services.journald.rateLimitInterval = mkOption { + default = "30s"; + type = types.str; + description = lib.mdDoc '' + Configures the rate limiting interval that is applied to all + messages generated on the system. This rate limiting is applied + per-service, so that two services which log do not interfere with + each other's limit. The value may be specified in the following + units: s, min, h, ms, us. To turn off any kind of rate limiting, + set either value to 0. + + See {option}`services.journald.rateLimitBurst` for important + considerations when setting this value. + ''; + }; + + services.journald.rateLimitBurst = mkOption { + default = 10000; + type = types.int; + description = lib.mdDoc '' + Configures the rate limiting burst limit (number of messages per + interval) that is applied to all messages generated on the system. + This rate limiting is applied per-service, so that two services + which log do not interfere with each other's limit. + + Note that the effective rate limit is multiplied by a factor derived + from the available free disk space for the journal as described on + [ + journald.conf(5)](https://www.freedesktop.org/software/systemd/man/journald.conf.html). + + Note that the total amount of logs stored is limited by journald settings + such as `SystemMaxUse`, which defaults to a 4 GB cap. + + It is thus recommended to compute what period of time that you will be + able to store logs for when an application logs at full burst rate. + With default settings for log lines that are 100 Bytes long, this can + amount to just a few hours. + ''; + }; + + services.journald.extraConfig = mkOption { + default = ""; + type = types.lines; + example = "Storage=volatile"; + description = lib.mdDoc '' + Extra config options for systemd-journald. See man journald.conf + for available options. + ''; + }; + + services.journald.enableHttpGateway = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Whether to enable the HTTP gateway to the journal. + ''; + }; + + services.journald.forwardToSyslog = mkOption { + default = config.services.rsyslogd.enable || config.services.syslog-ng.enable; + defaultText = literalExpression "services.rsyslogd.enable || services.syslog-ng.enable"; + type = types.bool; + description = lib.mdDoc '' + Whether to forward log messages to syslog. + ''; + }; + }; + + config = { + systemd.additionalUpstreamSystemUnits = [ + "systemd-journald.socket" + "systemd-journald@.socket" + "systemd-journald-varlink@.socket" + "systemd-journald.service" + "systemd-journald@.service" + "systemd-journal-flush.service" + "systemd-journal-catalog-update.service" + ] ++ (optional (!config.boot.isContainer) "systemd-journald-audit.socket") ++ [ + "systemd-journald-dev-log.socket" + "syslog.socket" + ] ++ optionals cfg.enableHttpGateway [ + "systemd-journal-gatewayd.socket" + "systemd-journal-gatewayd.service" + ]; + + environment.etc = { + "systemd/journald.conf".text = '' + [Journal] + Storage=persistent + RateLimitInterval=${cfg.rateLimitInterval} + RateLimitBurst=${toString cfg.rateLimitBurst} + ${optionalString (cfg.console != "") '' + ForwardToConsole=yes + TTYPath=${cfg.console} + ''} + ${optionalString (cfg.forwardToSyslog) '' + ForwardToSyslog=yes + ''} + ${cfg.extraConfig} + ''; + }; + + users.groups.systemd-journal.gid = config.ids.gids.systemd-journal; + users.users.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway; + users.users.systemd-journal-gateway.group = "systemd-journal-gateway"; + users.groups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway; + + systemd.sockets.systemd-journal-gatewayd.wantedBy = + optional cfg.enableHttpGateway "sockets.target"; + + systemd.services.systemd-journal-flush.restartIfChanged = false; + systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ]; + systemd.services.systemd-journald.stopIfChanged = false; + systemd.services."systemd-journald@".restartTriggers = [ config.environment.etc."systemd/journald.conf".source ]; + systemd.services."systemd-journald@".stopIfChanged = false; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/logind.nix b/nixpkgs/nixos/modules/system/boot/systemd/logind.nix new file mode 100644 index 000000000000..cf01c1882857 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/logind.nix @@ -0,0 +1,205 @@ +{ config, lib, pkgs, utils, ... }: + +with lib; + +let + cfg = config.services.logind; + + logindHandlerType = types.enum [ + "ignore" "poweroff" "reboot" "halt" "kexec" "suspend" + "hibernate" "hybrid-sleep" "suspend-then-hibernate" "lock" + ]; +in +{ + options.services.logind = { + extraConfig = mkOption { + default = ""; + type = types.lines; + example = "IdleAction=lock"; + description = lib.mdDoc '' + Extra config options for systemd-logind. + See [logind.conf(5)](https://www.freedesktop.org/software/systemd/man/logind.conf.html) + for available options. + ''; + }; + + killUserProcesses = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Specifies whether the processes of a user should be killed + when the user logs out. If true, the scope unit corresponding + to the session and all processes inside that scope will be + terminated. If false, the scope is "abandoned" + (see [systemd.scope(5)](https://www.freedesktop.org/software/systemd/man/systemd.scope.html#)), + and processes are not killed. + + See [logind.conf(5)](https://www.freedesktop.org/software/systemd/man/logind.conf.html#KillUserProcesses=) + for more details. + ''; + }; + + powerKey = mkOption { + default = "poweroff"; + example = "ignore"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the power key is pressed. + ''; + }; + + powerKeyLongPress = mkOption { + default = "ignore"; + example = "reboot"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the power key is long-pressed. + ''; + }; + + rebootKey = mkOption { + default = "reboot"; + example = "ignore"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the reboot key is pressed. + ''; + }; + + rebootKeyLongPress = mkOption { + default = "poweroff"; + example = "ignore"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the reboot key is long-pressed. + ''; + }; + + suspendKey = mkOption { + default = "suspend"; + example = "ignore"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the suspend key is pressed. + ''; + }; + + suspendKeyLongPress = mkOption { + default = "hibernate"; + example = "ignore"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the suspend key is long-pressed. + ''; + }; + + hibernateKey = mkOption { + default = "hibernate"; + example = "ignore"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the hibernate key is pressed. + ''; + }; + + hibernateKeyLongPress = mkOption { + default = "ignore"; + example = "suspend"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the hibernate key is long-pressed. + ''; + }; + + lidSwitch = mkOption { + default = "suspend"; + example = "ignore"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the laptop lid is closed. + ''; + }; + + lidSwitchExternalPower = mkOption { + default = cfg.lidSwitch; + defaultText = literalExpression "services.logind.lidSwitch"; + example = "ignore"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the laptop lid is closed + and the system is on external power. By default use + the same action as specified in services.logind.lidSwitch. + ''; + }; + + lidSwitchDocked = mkOption { + default = "ignore"; + example = "suspend"; + type = logindHandlerType; + + description = lib.mdDoc '' + Specifies what to do when the laptop lid is closed + and another screen is added. + ''; + }; + }; + + config = { + systemd.additionalUpstreamSystemUnits = [ + "systemd-logind.service" + "autovt@.service" + "systemd-user-sessions.service" + ] ++ optionals config.systemd.package.withImportd [ + "dbus-org.freedesktop.import1.service" + ] ++ optionals config.systemd.package.withMachined [ + "dbus-org.freedesktop.machine1.service" + ] ++ optionals config.systemd.package.withPortabled [ + "dbus-org.freedesktop.portable1.service" + ] ++ [ + "dbus-org.freedesktop.login1.service" + "user@.service" + "user-runtime-dir@.service" + ]; + + environment.etc = { + "systemd/logind.conf".text = '' + [Login] + KillUserProcesses=${if cfg.killUserProcesses then "yes" else "no"} + HandlePowerKey=${cfg.powerKey} + HandlePowerKeyLongPress=${cfg.powerKeyLongPress} + HandleRebootKey=${cfg.rebootKey} + HandleRebootKeyLongPress=${cfg.rebootKeyLongPress} + HandleSuspendKey=${cfg.suspendKey} + HandleSuspendKeyLongPress=${cfg.suspendKeyLongPress} + HandleHibernateKey=${cfg.hibernateKey} + HandleHibernateKeyLongPress=${cfg.hibernateKeyLongPress} + HandleLidSwitch=${cfg.lidSwitch} + HandleLidSwitchExternalPower=${cfg.lidSwitchExternalPower} + HandleLidSwitchDocked=${cfg.lidSwitchDocked} + ${cfg.extraConfig} + ''; + }; + + # Restarting systemd-logind breaks X11 + # - upstream commit: https://cgit.freedesktop.org/xorg/xserver/commit/?id=dc48bd653c7e101 + # - systemd announcement: https://github.com/systemd/systemd/blob/22043e4317ecd2bc7834b48a6d364de76bb26d91/NEWS#L103-L112 + # - this might be addressed in the future by xorg + #systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ]; + systemd.services.systemd-logind.restartIfChanged = false; + systemd.services.systemd-logind.stopIfChanged = false; + + # The user-runtime-dir@ service is managed by systemd-logind we should not touch it or else we break the users' sessions. + systemd.services."user-runtime-dir@".stopIfChanged = false; + systemd.services."user-runtime-dir@".restartIfChanged = false; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix b/nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix new file mode 100644 index 000000000000..b513aa051f28 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix @@ -0,0 +1,132 @@ +{ config, lib, pkgs, utils, ...}: + +with utils.systemdUtils.unitOptions; +with utils.systemdUtils.lib; +with lib; + +let + cfg = config.systemd.nspawn; + + checkExec = checkUnitConfig "Exec" [ + (assertOnlyFields [ + "Boot" "ProcessTwo" "Parameters" "Environment" "User" "WorkingDirectory" + "PivotRoot" "Capability" "DropCapability" "NoNewPrivileges" "KillSignal" + "Personality" "MachineID" "PrivateUsers" "NotifyReady" "SystemCallFilter" + "LimitCPU" "LimitFSIZE" "LimitDATA" "LimitSTACK" "LimitCORE" "LimitRSS" + "LimitNOFILE" "LimitAS" "LimitNPROC" "LimitMEMLOCK" "LimitLOCKS" + "LimitSIGPENDING" "LimitMSGQUEUE" "LimitNICE" "LimitRTPRIO" "LimitRTTIME" + "OOMScoreAdjust" "CPUAffinity" "Hostname" "ResolvConf" "Timezone" + "LinkJournal" "Ephemeral" "AmbientCapability" + ]) + (assertValueOneOf "Boot" boolValues) + (assertValueOneOf "ProcessTwo" boolValues) + (assertValueOneOf "NotifyReady" boolValues) + ]; + + checkFiles = checkUnitConfig "Files" [ + (assertOnlyFields [ + "ReadOnly" "Volatile" "Bind" "BindReadOnly" "TemporaryFileSystem" + "Overlay" "OverlayReadOnly" "PrivateUsersChown" "BindUser" + "Inaccessible" "PrivateUsersOwnership" + ]) + (assertValueOneOf "ReadOnly" boolValues) + (assertValueOneOf "Volatile" (boolValues ++ [ "state" ])) + (assertValueOneOf "PrivateUsersChown" boolValues) + (assertValueOneOf "PrivateUsersOwnership" [ "off" "chown" "map" "auto" ]) + ]; + + checkNetwork = checkUnitConfig "Network" [ + (assertOnlyFields [ + "Private" "VirtualEthernet" "VirtualEthernetExtra" "Interface" "MACVLAN" + "IPVLAN" "Bridge" "Zone" "Port" + ]) + (assertValueOneOf "Private" boolValues) + (assertValueOneOf "VirtualEthernet" boolValues) + ]; + + instanceOptions = { + options = + (getAttrs [ "enable" ] sharedOptions) + // { + execConfig = mkOption { + default = {}; + example = { Parameters = "/bin/sh"; }; + type = types.addCheck (types.attrsOf unitOption) checkExec; + description = lib.mdDoc '' + Each attribute in this set specifies an option in the + `[Exec]` section of this unit. See + {manpage}`systemd.nspawn(5)` for details. + ''; + }; + + filesConfig = mkOption { + default = {}; + example = { Bind = [ "/home/alice" ]; }; + type = types.addCheck (types.attrsOf unitOption) checkFiles; + description = lib.mdDoc '' + Each attribute in this set specifies an option in the + `[Files]` section of this unit. See + {manpage}`systemd.nspawn(5)` for details. + ''; + }; + + networkConfig = mkOption { + default = {}; + example = { Private = false; }; + type = types.addCheck (types.attrsOf unitOption) checkNetwork; + description = lib.mdDoc '' + Each attribute in this set specifies an option in the + `[Network]` section of this unit. See + {manpage}`systemd.nspawn(5)` for details. + ''; + }; + }; + + }; + + instanceToUnit = name: def: + let base = { + text = '' + [Exec] + ${attrsToSection def.execConfig} + + [Files] + ${attrsToSection def.filesConfig} + + [Network] + ${attrsToSection def.networkConfig} + ''; + } // def; + in base // { unit = makeUnit name base; }; + +in { + + options = { + + systemd.nspawn = mkOption { + default = {}; + type = with types; attrsOf (submodule instanceOptions); + description = lib.mdDoc "Definition of systemd-nspawn configurations."; + }; + + }; + + config = + let + units = mapAttrs' (n: v: let nspawnFile = "${n}.nspawn"; in nameValuePair nspawnFile (instanceToUnit nspawnFile v)) cfg; + in + mkMerge [ + (mkIf (cfg != {}) { + 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/nixpkgs/nixos/modules/system/boot/systemd/oomd.nix b/nixpkgs/nixos/modules/system/boot/systemd/oomd.nix new file mode 100644 index 000000000000..fad755e278c7 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/oomd.nix @@ -0,0 +1,57 @@ +{ config, lib, ... }: let + + cfg = config.systemd.oomd; + +in { + options.systemd.oomd = { + enable = lib.mkEnableOption (lib.mdDoc "the `systemd-oomd` OOM killer") // { default = true; }; + + # Fedora enables the first and third option by default. See the 10-oomd-* files here: + # https://src.fedoraproject.org/rpms/systemd/tree/acb90c49c42276b06375a66c73673ac351025597 + enableRootSlice = lib.mkEnableOption (lib.mdDoc "oomd on the root slice (`-.slice`)"); + enableSystemSlice = lib.mkEnableOption (lib.mdDoc "oomd on the system slice (`system.slice`)"); + enableUserServices = lib.mkEnableOption (lib.mdDoc "oomd on all user services (`user@.service`)"); + + extraConfig = lib.mkOption { + type = with lib.types; attrsOf (oneOf [ str int bool ]); + default = {}; + example = lib.literalExpression ''{ DefaultMemoryPressureDurationSec = "20s"; }''; + description = lib.mdDoc '' + Extra config options for `systemd-oomd`. See {command}`man oomd.conf` + for available options. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.additionalUpstreamSystemUnits = [ + "systemd-oomd.service" + "systemd-oomd.socket" + ]; + systemd.services.systemd-oomd.wantedBy = [ "multi-user.target" ]; + + environment.etc."systemd/oomd.conf".text = lib.generators.toINI {} { + OOM = cfg.extraConfig; + }; + + systemd.oomd.extraConfig.DefaultMemoryPressureDurationSec = lib.mkDefault "20s"; # Fedora default + + users.users.systemd-oom = { + description = "systemd-oomd service user"; + group = "systemd-oom"; + isSystemUser = true; + }; + users.groups.systemd-oom = { }; + + systemd.slices."-".sliceConfig = lib.mkIf cfg.enableRootSlice { + ManagedOOMSwap = "kill"; + }; + systemd.slices."system".sliceConfig = lib.mkIf cfg.enableSystemSlice { + ManagedOOMSwap = "kill"; + }; + systemd.services."user@".serviceConfig = lib.mkIf cfg.enableUserServices { + ManagedOOMMemoryPressure = "kill"; + ManagedOOMMemoryPressureLimit = "50%"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/repart.nix b/nixpkgs/nixos/modules/system/boot/systemd/repart.nix new file mode 100644 index 000000000000..5ac2ace56ba0 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/repart.nix @@ -0,0 +1,148 @@ +{ config, lib, pkgs, utils, ... }: + +let + cfg = config.systemd.repart; + initrdCfg = config.boot.initrd.systemd.repart; + + format = pkgs.formats.ini { }; + + definitionsDirectory = utils.systemdUtils.lib.definitions + "repart.d" + format + (lib.mapAttrs (_n: v: { Partition = v; }) cfg.partitions); +in +{ + options = { + boot.initrd.systemd.repart = { + enable = lib.mkEnableOption (lib.mdDoc "systemd-repart") // { + description = lib.mdDoc '' + Grow and add partitions to a partition table at boot time in the initrd. + systemd-repart only works with GPT partition tables. + + To run systemd-repart after the initrd, see + `options.systemd.repart.enable`. + ''; + }; + + device = lib.mkOption { + type = with lib.types; nullOr str; + description = lib.mdDoc '' + The device to operate on. + + If `device == null`, systemd-repart will operate on the device + backing the root partition. So in order to dynamically *create* the + root partition in the initrd you need to set a device. + ''; + default = null; + example = "/dev/vda"; + }; + }; + + systemd.repart = { + enable = lib.mkEnableOption (lib.mdDoc "systemd-repart") // { + description = lib.mdDoc '' + Grow and add partitions to a partition table. + systemd-repart only works with GPT partition tables. + + To run systemd-repart while in the initrd, see + `options.boot.initrd.systemd.repart.enable`. + ''; + }; + + partitions = lib.mkOption { + type = with lib.types; attrsOf (attrsOf (oneOf [ str int bool ])); + default = { }; + example = { + "10-root" = { + Type = "root"; + }; + "20-home" = { + Type = "home"; + SizeMinBytes = "512M"; + SizeMaxBytes = "2G"; + }; + }; + description = lib.mdDoc '' + Specify partitions as a set of the names of the definition files as the + key and the partition configuration as its value. The partition + configuration can use all upstream options. See <link + xlink:href="https://www.freedesktop.org/software/systemd/man/repart.d.html"/> + for all available options. + ''; + }; + }; + }; + + config = lib.mkIf (cfg.enable || initrdCfg.enable) { + assertions = [ + { + assertion = initrdCfg.enable -> config.boot.initrd.systemd.enable; + message = '' + 'boot.initrd.systemd.repart.enable' requires 'boot.initrd.systemd.enable' to be enabled. + ''; + } + ]; + + boot.initrd.systemd = lib.mkIf initrdCfg.enable { + additionalUpstreamUnits = [ + "systemd-repart.service" + ]; + + storePaths = [ + "${config.boot.initrd.systemd.package}/bin/systemd-repart" + ]; + + contents."/etc/repart.d".source = definitionsDirectory; + + # Override defaults in upstream unit. + services.systemd-repart = + let + deviceUnit = "${utils.escapeSystemdPath initrdCfg.device}.device"; + in + { + # systemd-repart tries to create directories in /var/tmp by default to + # store large temporary files that benefit from persistence on disk. In + # the initrd, however, /var/tmp does not provide more persistence than + # /tmp, so we re-use it here. + environment."TMPDIR" = "/tmp"; + serviceConfig = { + ExecStart = [ + " " # required to unset the previous value. + # When running in the initrd, systemd-repart by default searches + # for definition files in /sysroot or /sysusr. We tell it to look + # in the initrd itself. + ''${config.boot.initrd.systemd.package}/bin/systemd-repart \ + --definitions=/etc/repart.d \ + --dry-run=no ${lib.optionalString (initrdCfg.device != null) initrdCfg.device} + '' + ]; + }; + # systemd-repart needs to run after /sysroot (or /sysuser, but we + # don't have it) has been mounted because otherwise it cannot + # determine the device (i.e disk) to operate on. If you want to run + # systemd-repart without /sysroot (i.e. to create the root + # partition), you have to explicitly tell it which device to operate + # on. The service then needs to be ordered to run after this device + # is available. + requires = lib.mkIf (initrdCfg.device != null) [ deviceUnit ]; + after = + if initrdCfg.device == null then + [ "sysroot.mount" ] + else + [ deviceUnit ]; + }; + }; + + environment.etc = lib.mkIf cfg.enable { + "repart.d".source = definitionsDirectory; + }; + + systemd = lib.mkIf cfg.enable { + additionalUpstreamSystemUnits = [ + "systemd-repart.service" + ]; + }; + }; + + meta.maintainers = with lib.maintainers; [ nikstur ]; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/shutdown.nix b/nixpkgs/nixos/modules/system/boot/systemd/shutdown.nix new file mode 100644 index 000000000000..d7300e940af2 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/shutdown.nix @@ -0,0 +1,66 @@ +{ config, lib, utils, pkgs, ... }: let + + cfg = config.systemd.shutdownRamfs; + + ramfsContents = let + storePaths = map (p: "${p}\n") cfg.storePaths; + contents = lib.mapAttrsToList (_: v: "${v.source}\n${v.target}") (lib.filterAttrs (_: v: v.enable) cfg.contents); + in pkgs.writeText "shutdown-ramfs-contents" (lib.concatStringsSep "\n" (storePaths ++ contents)); + +in { + options.systemd.shutdownRamfs = { + enable = lib.mkEnableOption (lib.mdDoc "pivoting back to an initramfs for shutdown") // { default = true; }; + contents = lib.mkOption { + description = lib.mdDoc "Set of files that have to be linked into the shutdown ramfs"; + example = lib.literalExpression '' + { + "/lib/systemd/system-shutdown/zpool-sync-shutdown".source = writeShellScript "zpool" "exec ''${zfs}/bin/zpool sync" + } + ''; + type = utils.systemdUtils.types.initrdContents; + }; + + storePaths = lib.mkOption { + description = lib.mdDoc '' + Store paths to copy into the shutdown ramfs as well. + ''; + type = lib.types.listOf lib.types.singleLineStr; + default = []; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.shutdownRamfs.contents = { + "/shutdown".source = "${config.systemd.package}/lib/systemd/systemd-shutdown"; + "/etc/initrd-release".source = config.environment.etc.os-release.source; + "/etc/os-release".source = config.environment.etc.os-release.source; + }; + systemd.shutdownRamfs.storePaths = [pkgs.runtimeShell "${pkgs.coreutils}/bin"]; + + systemd.mounts = [{ + what = "tmpfs"; + where = "/run/initramfs"; + type = "tmpfs"; + }]; + + systemd.services.generate-shutdown-ramfs = { + description = "Generate shutdown ramfs"; + wantedBy = [ "shutdown.target" ]; + before = [ "shutdown.target" ]; + unitConfig = { + DefaultDependencies = false; + RequiresMountsFor = "/run/initramfs"; + ConditionFileIsExecutable = [ + "!/run/initramfs/shutdown" + ]; + }; + + serviceConfig = { + Type = "oneshot"; + ProtectSystem = "strict"; + ReadWritePaths = "/run/initramfs"; + ExecStart = "${pkgs.makeInitrdNGTool}/bin/make-initrd-ng ${ramfsContents} /run/initramfs"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix b/nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix new file mode 100644 index 000000000000..b1914a9c4e76 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix @@ -0,0 +1,136 @@ +{ config, lib, pkgs, utils, ... }: + +let + cfg = config.systemd.sysupdate; + + format = pkgs.formats.ini { }; + + definitionsDirectory = utils.systemdUtils.lib.definitions + "sysupdate.d" + format + cfg.transfers; +in +{ + options.systemd.sysupdate = { + + enable = lib.mkEnableOption (lib.mdDoc "systemd-sysupdate") // { + description = lib.mdDoc '' + Atomically update the host OS, container images, portable service + images or other sources. + + If enabled, updates are triggered in regular intervals via a + `systemd.timer` unit. + + Please see + <https://www.freedesktop.org/software/systemd/man/systemd-sysupdate.html> + for more details. + ''; + }; + + timerConfig = utils.systemdUtils.unitOptions.timerOptions.options.timerConfig // { + default = { }; + description = lib.mdDoc '' + The timer configuration for performing the update. + + By default, the upstream configuration is used: + <https://github.com/systemd/systemd/blob/main/units/systemd-sysupdate.timer> + ''; + }; + + reboot = { + enable = lib.mkEnableOption (lib.mdDoc "automatically rebooting after an update") // { + description = lib.mdDoc '' + Whether to automatically reboot after an update. + + If set to `true`, the system will automatically reboot via a + `systemd.timer` unit but only after a new version was installed. + + This uses a unit completely separate from the one performing the + update because it is typically advisable to download updates + regularly while the system is up, but delay reboots until the + appropriate time (i.e. typically at night). + + Set this to `false` if you do not want to reboot after an update. This + is useful when you update a container image or another source where + rebooting is not necessary in order to finalize the update. + ''; + }; + + timerConfig = utils.systemdUtils.unitOptions.timerOptions.options.timerConfig // { + default = { }; + description = lib.mdDoc '' + The timer configuration for rebooting after an update. + + By default, the upstream configuration is used: + <https://github.com/systemd/systemd/blob/main/units/systemd-sysupdate-reboot.timer> + ''; + }; + }; + + transfers = lib.mkOption { + type = with lib.types; attrsOf format.type; + default = { }; + example = { + "10-uki.conf" = { + Transfer = { + ProtectVersion = "%A"; + }; + + Source = { + Type = "url-file"; + Path = "https://download.example.com/"; + MatchPattern = "nixos_@v.efi.xz"; + }; + + Target = { + Type = "regular-file"; + Path = "/EFI/Linux"; + PathRelativeTo = "boot"; + MatchPattern = '' + nixos_@v+@l-@d.efi"; \ + nixos_@v+@l.efi \ + nixos_@v.efi + ''; + Mode = "0444"; + TriesLeft = 3; + TriesDone = 0; + InstancesMax = 2; + }; + }; + }; + description = lib.mdDoc '' + Specify transfers as a set of the names of the transfer files as the + key and the configuration as its value. The configuration can use all + upstream options. See + <https://www.freedesktop.org/software/systemd/man/sysupdate.d.html> + for all available options. + ''; + }; + + }; + + config = lib.mkIf cfg.enable { + + systemd.additionalUpstreamSystemUnits = [ + "systemd-sysupdate.service" + "systemd-sysupdate.timer" + "systemd-sysupdate-reboot.service" + "systemd-sysupdate-reboot.timer" + ]; + + systemd.timers = { + "systemd-sysupdate" = { + wantedBy = [ "timers.target" ]; + timerConfig = cfg.timerConfig; + }; + "systemd-sysupdate-reboot" = lib.mkIf cfg.reboot.enable { + wantedBy = [ "timers.target" ]; + timerConfig = cfg.reboot.timerConfig; + }; + }; + + environment.etc."sysupdate.d".source = definitionsDirectory; + }; + + meta.maintainers = with lib.maintainers; [ nikstur ]; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/tmpfiles.nix b/nixpkgs/nixos/modules/system/boot/systemd/tmpfiles.nix new file mode 100644 index 000000000000..f7ef45aab3c9 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/tmpfiles.nix @@ -0,0 +1,225 @@ +{ config, lib, pkgs, utils, ... }: + +with lib; + +let + cfg = config.systemd.tmpfiles; + systemd = config.systemd.package; +in +{ + options = { + systemd.tmpfiles.rules = mkOption { + type = types.listOf types.str; + default = []; + example = [ "d /tmp 1777 root root 10d" ]; + description = lib.mdDoc '' + Rules for creation, deletion and cleaning of volatile and temporary files + automatically. See + {manpage}`tmpfiles.d(5)` + for the exact format. + ''; + }; + + systemd.tmpfiles.settings = mkOption { + description = lib.mdDoc '' + Declare systemd-tmpfiles rules to create, delete, and clean up volatile + and temporary files and directories. + + Even though the service is called `*tmp*files` you can also create + persistent files. + ''; + example = { + "10-mypackage" = { + "/var/lib/my-service/statefolder".d = { + mode = "0755"; + user = "root"; + group = "root"; + }; + }; + }; + default = {}; + type = types.attrsOf (types.attrsOf (types.attrsOf (types.submodule ({ name, config, ... }: { + options.type = mkOption { + type = types.str; + default = name; + example = "d"; + description = lib.mdDoc '' + The type of operation to perform on the file. + + The type consists of a single letter and optionally one or more + modifier characters. + + Please see the upstream documentation for the available types and + more details: + <https://www.freedesktop.org/software/systemd/man/tmpfiles.d> + ''; + }; + options.mode = mkOption { + type = types.str; + default = "-"; + example = "0755"; + description = lib.mdDoc '' + The file access mode to use when creating this file or directory. + ''; + }; + options.user = mkOption { + type = types.str; + default = "-"; + example = "root"; + description = lib.mdDoc '' + The user of the file. + + This may either be a numeric ID or a user/group name. + + If omitted or when set to `"-"`, the user and group of the user who + invokes systemd-tmpfiles is used. + ''; + }; + options.group = mkOption { + type = types.str; + default = "-"; + example = "root"; + description = lib.mdDoc '' + The group of the file. + + This may either be a numeric ID or a user/group name. + + If omitted or when set to `"-"`, the user and group of the user who + invokes systemd-tmpfiles is used. + ''; + }; + options.age = mkOption { + type = types.str; + default = "-"; + example = "10d"; + description = lib.mdDoc '' + Delete a file when it reaches a certain age. + + If a file or directory is older than the current time minus the age + field, it is deleted. + + If set to `"-"` no automatic clean-up is done. + ''; + }; + options.argument = mkOption { + type = types.str; + default = ""; + example = ""; + description = lib.mdDoc '' + An argument whose meaning depends on the type of operation. + + Please see the upstream documentation for the meaning of this + parameter in different situations: + <https://www.freedesktop.org/software/systemd/man/tmpfiles.d> + ''; + }; + })))); + }; + + systemd.tmpfiles.packages = mkOption { + type = types.listOf types.package; + default = []; + example = literalExpression "[ pkgs.lvm2 ]"; + apply = map getLib; + description = lib.mdDoc '' + List of packages containing {command}`systemd-tmpfiles` rules. + + All files ending in .conf found in + {file}`«pkg»/lib/tmpfiles.d` + will be included. + If this folder does not exist or does not contain any files an error will be returned instead. + + If a {file}`lib` output is available, rules are searched there and only there. + If there is no {file}`lib` output it will fall back to {file}`out` + and if that does not exist either, the default output will be used. + ''; + }; + }; + + config = { + systemd.additionalUpstreamSystemUnits = [ + "systemd-tmpfiles-clean.service" + "systemd-tmpfiles-clean.timer" + "systemd-tmpfiles-setup.service" + "systemd-tmpfiles-setup-dev.service" + ]; + + systemd.additionalUpstreamUserUnits = [ + "systemd-tmpfiles-clean.service" + "systemd-tmpfiles-clean.timer" + "systemd-tmpfiles-setup.service" + ]; + + environment.etc = { + "tmpfiles.d".source = (pkgs.symlinkJoin { + name = "tmpfiles.d"; + paths = map (p: p + "/lib/tmpfiles.d") cfg.packages; + postBuild = '' + for i in $(cat $pathsPath); do + (test -d "$i" && test $(ls "$i"/*.conf | wc -l) -ge 1) || ( + echo "ERROR: The path '$i' from systemd.tmpfiles.packages contains no *.conf files." + exit 1 + ) + done + '' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) '' + rm -f $out/${removePrefix "tmpfiles.d/" name} + '') config.system.build.etc.passthru.targets; + }) + "/*"; + }; + + systemd.tmpfiles.packages = [ + # Default tmpfiles rules provided by systemd + (pkgs.runCommand "systemd-default-tmpfiles" {} '' + mkdir -p $out/lib/tmpfiles.d + cd $out/lib/tmpfiles.d + + # home.conf creates /srv (which we don't want), and /home, which + # is handled by NixOS anyway. + # ln -s "${systemd}/example/tmpfiles.d/home.conf" + ln -s "${systemd}/example/tmpfiles.d/journal-nocow.conf" + ln -s "${systemd}/example/tmpfiles.d/portables.conf" + ln -s "${systemd}/example/tmpfiles.d/static-nodes-permissions.conf" + ln -s "${systemd}/example/tmpfiles.d/systemd.conf" + ln -s "${systemd}/example/tmpfiles.d/systemd-nologin.conf" + ln -s "${systemd}/example/tmpfiles.d/systemd-nspawn.conf" + ln -s "${systemd}/example/tmpfiles.d/systemd-tmp.conf" + ln -s "${systemd}/example/tmpfiles.d/tmp.conf" + ln -s "${systemd}/example/tmpfiles.d/var.conf" + ln -s "${systemd}/example/tmpfiles.d/x11.conf" + '') + # User-specified tmpfiles rules + (pkgs.writeTextFile { + name = "nixos-tmpfiles.d"; + destination = "/lib/tmpfiles.d/00-nixos.conf"; + text = '' + # This file is created automatically and should not be modified. + # Please change the option ‘systemd.tmpfiles.rules’ instead. + + ${concatStringsSep "\n" cfg.rules} + ''; + }) + ] ++ (mapAttrsToList (name: paths: + pkgs.writeTextDir "lib/tmpfiles.d/${name}.conf" (concatStrings (mapAttrsToList (path: types: + concatStrings (mapAttrsToList (_type: entry: '' + '${entry.type}' '${path}' '${entry.mode}' '${entry.user}' '${entry.group}' '${entry.age}' ${entry.argument} + '') types) + ) paths )) + ) cfg.settings); + + systemd.tmpfiles.rules = [ + "d /nix/var 0755 root root - -" + "L+ /nix/var/nix/gcroots/booted-system 0755 root root - /run/booted-system" + "d /run/lock 0755 root root - -" + "d /var/db 0755 root root - -" + "L /etc/mtab - - - - ../proc/mounts" + "L /var/lock - - - - ../run/lock" + # Boot-time cleanup + "R! /etc/group.lock - - - - -" + "R! /etc/passwd.lock - - - - -" + "R! /etc/shadow.lock - - - - -" + "R! /etc/mtab* - - - - -" + "R! /nix/var/nix/gcroots/tmp - - - - -" + "R! /nix/var/nix/temproots - - - - -" + ]; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/user.nix b/nixpkgs/nixos/modules/system/boot/systemd/user.nix new file mode 100644 index 000000000000..64dc19633eca --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/user.nix @@ -0,0 +1,238 @@ +{ config, lib, pkgs, utils, ... }: +with utils; +with systemdUtils.unitOptions; +with lib; + +let + cfg = config.systemd.user; + + systemd = config.systemd.package; + + inherit + (systemdUtils.lib) + makeUnit + generateUnits + targetToUnit + serviceToUnit + sliceToUnit + socketToUnit + timerToUnit + pathToUnit; + + upstreamUserUnits = [ + "app.slice" + "background.slice" + "basic.target" + "bluetooth.target" + "default.target" + "exit.target" + "graphical-session-pre.target" + "graphical-session.target" + "paths.target" + "printer.target" + "session.slice" + "shutdown.target" + "smartcard.target" + "sockets.target" + "sound.target" + "systemd-exit.service" + "timers.target" + "xdg-desktop-autostart.target" + ] ++ config.systemd.additionalUpstreamUserUnits; + + writeTmpfiles = { rules, user ? null }: + let + suffix = optionalString (user != null) "-${user}"; + in + pkgs.writeTextFile { + name = "nixos-user-tmpfiles.d${suffix}"; + destination = "/etc/xdg/user-tmpfiles.d/00-nixos${suffix}.conf"; + text = '' + # This file is created automatically and should not be modified. + # Please change the options ‘systemd.user.tmpfiles’ instead. + ${concatStringsSep "\n" rules} + ''; + }; +in { + options = { + systemd.user.extraConfig = mkOption { + default = ""; + type = types.lines; + example = "DefaultCPUAccounting=yes"; + description = lib.mdDoc '' + Extra config options for systemd user instances. See {manpage}`systemd-user.conf(5)` for + available options. + ''; + }; + + systemd.user.units = mkOption { + description = lib.mdDoc "Definition of systemd per-user units."; + default = {}; + type = systemdUtils.types.units; + }; + + systemd.user.paths = mkOption { + default = {}; + type = systemdUtils.types.paths; + description = lib.mdDoc "Definition of systemd per-user path units."; + }; + + systemd.user.services = mkOption { + default = {}; + type = systemdUtils.types.services; + description = lib.mdDoc "Definition of systemd per-user service units."; + }; + + systemd.user.slices = mkOption { + default = {}; + type = systemdUtils.types.slices; + description = lib.mdDoc "Definition of systemd per-user slice units."; + }; + + systemd.user.sockets = mkOption { + default = {}; + type = systemdUtils.types.sockets; + description = lib.mdDoc "Definition of systemd per-user socket units."; + }; + + systemd.user.targets = mkOption { + default = {}; + type = systemdUtils.types.targets; + description = lib.mdDoc "Definition of systemd per-user target units."; + }; + + systemd.user.timers = mkOption { + default = {}; + type = systemdUtils.types.timers; + description = lib.mdDoc "Definition of systemd per-user timer units."; + }; + + systemd.user.tmpfiles = { + rules = mkOption { + type = types.listOf types.str; + default = []; + example = [ "D %C - - - 7d" ]; + description = lib.mdDoc '' + Global user rules for creation, deletion and cleaning of volatile and + temporary files automatically. See + {manpage}`tmpfiles.d(5)` + for the exact format. + ''; + }; + + users = mkOption { + description = mdDoc '' + Per-user rules for creation, deletion and cleaning of volatile and + temporary files automatically. + ''; + default = {}; + type = types.attrsOf (types.submodule { + options = { + rules = mkOption { + type = types.listOf types.str; + default = []; + example = [ "D %C - - - 7d" ]; + description = mdDoc '' + Per-user rules for creation, deletion and cleaning of volatile and + temporary files automatically. See + {manpage}`tmpfiles.d(5)` + for the exact format. + ''; + }; + }; + }); + }; + }; + + systemd.additionalUpstreamUserUnits = mkOption { + default = []; + type = types.listOf types.str; + example = []; + description = lib.mdDoc '' + Additional units shipped with systemd that should be enabled for per-user systemd instances. + ''; + internal = true; + }; + }; + + config = { + systemd.additionalUpstreamSystemUnits = [ + "user.slice" + ]; + + environment.etc = { + "systemd/user".source = generateUnits { + type = "user"; + inherit (cfg) units; + upstreamUnits = upstreamUserUnits; + upstreamWants = []; + }; + + "systemd/user.conf".text = '' + [Manager] + ${cfg.extraConfig} + ''; + }; + + systemd.user.units = + 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; + + # Generate timer units for all services that have a ‘startAt’ value. + systemd.user.timers = + mapAttrs (name: service: { + wantedBy = ["timers.target"]; + timerConfig.OnCalendar = service.startAt; + }) + (filterAttrs (name: service: service.startAt != []) cfg.services); + + # Provide the systemd-user PAM service, required to run systemd + # user instances. + security.pam.services.systemd-user = + { # Ensure that pam_systemd gets included. This is special-cased + # in systemd to provide XDG_RUNTIME_DIR. + startSession = true; + # Disable pam_mount in systemd-user to prevent it from being called + # multiple times during login, because it will prevent pam_mount from + # unmounting the previously mounted volumes. + pamMount = false; + }; + + # Some overrides to upstream units. + systemd.services."user@".restartIfChanged = false; + systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions. + + # enable systemd user tmpfiles + systemd.user.services.systemd-tmpfiles-setup.wantedBy = + optional + (cfg.tmpfiles.rules != [] || any (cfg': cfg'.rules != []) (attrValues cfg.tmpfiles.users)) + "basic.target"; + + # /run/current-system/sw/etc/xdg is in systemd's $XDG_CONFIG_DIRS so we can + # write the tmpfiles.d rules for everyone there + environment.systemPackages = + optional + (cfg.tmpfiles.rules != []) + (writeTmpfiles { inherit (cfg.tmpfiles) rules; }); + + # /etc/profiles/per-user/$USER/etc/xdg is in systemd's $XDG_CONFIG_DIRS so + # we can write a single user's tmpfiles.d rules there + users.users = + mapAttrs + (user: cfg': { + packages = optional (cfg'.rules != []) (writeTmpfiles { + inherit (cfg') rules; + inherit user; + }); + }) + cfg.tmpfiles.users; + + system.userActivationScripts.tmpfiles = '' + ${config.systemd.package}/bin/systemd-tmpfiles --user --create --remove + ''; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/systemd/userdbd.nix b/nixpkgs/nixos/modules/system/boot/systemd/userdbd.nix new file mode 100644 index 000000000000..e7f6d42341c4 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/systemd/userdbd.nix @@ -0,0 +1,18 @@ +{ config, lib, ... }: + +let + cfg = config.services.userdbd; +in +{ + options.services.userdbd.enable = lib.mkEnableOption (lib.mdDoc '' + the systemd JSON user/group record lookup service + ''); + config = lib.mkIf cfg.enable { + systemd.additionalUpstreamSystemUnits = [ + "systemd-userdbd.socket" + "systemd-userdbd.service" + ]; + + systemd.sockets.systemd-userdbd.wantedBy = [ "sockets.target" ]; + }; +} |