Unverified Commit 8dd369f5 authored by WilliButz's avatar WilliButz
Browse files

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 1a68e21d 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.
parent 7c8b2a22
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -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"
+0 −4
Original line number Diff line number Diff line
@@ -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" ];
+179 −99
Original line number Diff line number Diff line
{ config, lib, pkgs, utils, ... }:
{ config, lib, pkgs, ... }:

with lib;

let
  cfg = config.systemd.tmpfiles;
  initrdCfg = config.boot.initrd.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 = ''
        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 {
  settingsOption = {
    description = ''
      Declare systemd-tmpfiles rules to create, delete, and clean up volatile
      and temporary files and directories.
@@ -116,6 +103,45 @@ in
    }))));
  };

  # 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.<name>
  pathsToRules = mapAttrsToList (path: types:
    concatStrings (
      mapAttrsToList (_type: settingsEntryToRule path) types
    )
  );

  mkRuleFileContent = paths: concatStrings (pathsToRules paths);
in
{
  options = {
    systemd.tmpfiles.rules = mkOption {
      type = types.listOf types.str;
      default = [];
      example = [ "d /tmp 1777 root root 10d" ];
      description = ''
        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 settingsOption;

    boot.initrd.systemd.tmpfiles.settings = mkOption (settingsOption // {
      description = ''
        Similar to {option}`systemd.tmpfiles.settings` but the rules are
        only applied by systemd-tmpfiles before `initrd-switch-root.target`.

        See {manpage}`bootup(7)`.
      '';
    });

    systemd.tmpfiles.packages = mkOption {
      type = types.listOf types.package;
      default = [];
@@ -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);
      };
    };
  };
}