From 8dd369f524411d971e03d8bc5e1abcb73c55f3db Mon Sep 17 00:00:00 2001 From: WilliButz Date: Sun, 28 Apr 2024 18:31:09 +0200 Subject: [PATCH] nixos/systemd-tmpfiles: add initrd support This adds support for declaring tmpfiles rules exclusively for the systemd initrd. Configuration is possible through the new option `boot.initrd.systemd.tmpfiles.settings` that shares the same interface as `systemd.tmpfiles.settings`. I did intentionally not replicate the `rules` interface here, given that the settings attribute set is more versatile than the list of strings used for `rules`. This should also make it unnecessary to implement the workaround from 1a68e21d474c5d6005812459c9bce28168625384 again. A self-contained `tmpfiles.d` directory is generated from the new initrd settings and it is added to the initrd as a content path at `/etc/tmpfiles.d`. The stage-1 `systemd-tmpfiles-setup.service` is now altered to no longer operate under the `/sysroot` prefix, because the `/sysroot` hierarchy cannot be expected to be available when the default upstream service is started. To handle files under `/sysroot` a slightly altered version of the upstream default service is introduced. This new unit `systemd-tmpfiles-setup-sysroot.service` operates only under the `/sysroot` prefix and it is ordered between `initrd-fs.target` and the nixos activation. Config related to tmpfiles was moved from initrd.nix to tmpfiles.nix. --- nixos/modules/system/boot/systemd.nix | 1 - nixos/modules/system/boot/systemd/initrd.nix | 4 - .../modules/system/boot/systemd/tmpfiles.nix | 278 +++++++++++------- 3 files changed, 179 insertions(+), 104 deletions(-) diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 85e9b0a68b46..3fa19c7ecf1e 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 9ddfcf6c1ba6..b853cb687c1b 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" @@ -506,8 +504,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 ded13728017d..fab7549ca912 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 = [ @@ -232,11 +259,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 = [ @@ -254,5 +277,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); + }; + }; }; }