diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 9e7387e40379..bb6fcc1d38ce 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -53,7 +53,6 @@ let "debug-shell.service" # Udev. - "systemd-tmpfiles-setup-dev-early.service" "systemd-udevd-control.socket" "systemd-udevd-kernel.socket" "systemd-udevd.service" diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 4abe379be5d0..fa144951b208 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -67,8 +67,6 @@ let "systemd-poweroff.service" "systemd-reboot.service" "systemd-sysctl.service" - "systemd-tmpfiles-setup-dev.service" - "systemd-tmpfiles-setup.service" "timers.target" "tpm2.target" "umount.target" @@ -518,8 +516,6 @@ in { (v: let n = escapeSystemdPath v.where; in nameValuePair "${n}.automount" (automountToUnit 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" ]; diff --git a/nixos/modules/system/boot/systemd/tmpfiles.nix b/nixos/modules/system/boot/systemd/tmpfiles.nix index af37fb07d29b..f609da314c86 100644 --- a/nixos/modules/system/boot/systemd/tmpfiles.nix +++ b/nixos/modules/system/boot/systemd/tmpfiles.nix @@ -1,10 +1,121 @@ -{ config, lib, pkgs, utils, ... }: +{ config, lib, pkgs, ... }: with lib; let cfg = config.systemd.tmpfiles; + initrdCfg = config.boot.initrd.systemd.tmpfiles; systemd = config.systemd.package; + + settingsOption = { + description = '' + 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 = '' + 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: + + ''; + }; + options.mode = mkOption { + type = types.str; + default = "-"; + example = "0755"; + description = '' + The file access mode to use when creating this file or directory. + ''; + }; + options.user = mkOption { + type = types.str; + default = "-"; + example = "root"; + description = '' + 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 = '' + 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 = '' + 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 = '' + An argument whose meaning depends on the type of operation. + + Please see the upstream documentation for the meaning of this + parameter in different situations: + + ''; + }; + })))); + }; + + # generates a single entry for a tmpfiles.d rule + settingsEntryToRule = path: entry: '' + '${entry.type}' '${path}' '${entry.mode}' '${entry.user}' '${entry.group}' '${entry.age}' ${entry.argument} + ''; + + # generates a list of tmpfiles.d rules from the attrs (paths) under tmpfiles.settings. + pathsToRules = mapAttrsToList (path: types: + concatStrings ( + mapAttrsToList (_type: settingsEntryToRule path) types + ) + ); + + mkRuleFileContent = paths: concatStrings (pathsToRules paths); in { options = { @@ -20,101 +131,16 @@ in ''; }; - systemd.tmpfiles.settings = mkOption { + systemd.tmpfiles.settings = mkOption settingsOption; + + boot.initrd.systemd.tmpfiles.settings = mkOption (settingsOption // { description = '' - Declare systemd-tmpfiles rules to create, delete, and clean up volatile - and temporary files and directories. + Similar to {option}`systemd.tmpfiles.settings` but the rules are + only applied by systemd-tmpfiles before `initrd-switch-root.target`. - Even though the service is called `*tmp*files` you can also create - persistent files. + See {manpage}`bootup(7)`. ''; - 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 = '' - 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: - - ''; - }; - options.mode = mkOption { - type = types.str; - default = "-"; - example = "0755"; - description = '' - The file access mode to use when creating this file or directory. - ''; - }; - options.user = mkOption { - type = types.str; - default = "-"; - example = "root"; - description = '' - 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 = '' - 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 = '' - 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 = '' - An argument whose meaning depends on the type of operation. - - Please see the upstream documentation for the meaning of this - parameter in different situations: - - ''; - }; - })))); - }; + }); systemd.tmpfiles.packages = mkOption { type = types.listOf types.package; @@ -140,8 +166,9 @@ in systemd.additionalUpstreamSystemUnits = [ "systemd-tmpfiles-clean.service" "systemd-tmpfiles-clean.timer" - "systemd-tmpfiles-setup.service" + "systemd-tmpfiles-setup-dev-early.service" "systemd-tmpfiles-setup-dev.service" + "systemd-tmpfiles-setup.service" ]; systemd.additionalUpstreamUserUnits = [ @@ -236,11 +263,7 @@ in ''; }) ] ++ (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 )) + pkgs.writeTextDir "lib/tmpfiles.d/${name}.conf" (mkRuleFileContent paths) ) cfg.settings); systemd.tmpfiles.rules = [ @@ -256,5 +279,62 @@ in "R! /nix/var/nix/gcroots/tmp - - - - -" "R! /nix/var/nix/temproots - - - - -" ]; + + boot.initrd.systemd = { + additionalUpstreamUnits = [ + "systemd-tmpfiles-setup-dev-early.service" + "systemd-tmpfiles-setup-dev.service" + "systemd-tmpfiles-setup.service" + ]; + + # override to exclude the prefix /sysroot, because it is not necessarily set up when the unit starts + services.systemd-tmpfiles-setup.serviceConfig = { + ExecStart = [ + "" + "systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev --exclude-prefix=/sysroot" + ]; + }; + + # sets up files under the prefix /sysroot, after the hierarchy is available and before nixos activation + services.systemd-tmpfiles-setup-sysroot = { + description = "Create Volatile Files and Directories in the Real Root"; + after = [ "initrd-fs.target" ]; + before = [ + "initrd-nixos-activation.service" + "shutdown.target" "initrd-switch-root.target" + ]; + conflicts = [ "shutdown.target" "initrd-switch-root.target" ]; + wantedBy = [ "initrd.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev --prefix=/sysroot"; + SuccessExitStatus = [ "DATAERR CANTCREAT" ]; + ImportCredential = [ + "tmpfiles.*" + "login.motd" + "login.issue" + "network.hosts" + "ssh.authorized_keys.root" + ]; + }; + unitConfig = { + DefaultDependencies = false; + RefuseManualStop = true; + }; + + }; + + contents."/etc/tmpfiles.d" = mkIf (initrdCfg.settings != { }) { + source = pkgs.linkFarm "initrd-tmpfiles.d" ( + mapAttrsToList + (name: paths: { + name = "${name}.conf"; + path = pkgs.writeText "${name}.conf" (mkRuleFileContent paths); + } + ) + initrdCfg.settings); + }; + }; }; }