about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorschnusch <schnusch@users.noreply.github.com>2022-12-23 21:22:49 +0100
committerschnusch <schnusch@users.noreply.github.com>2023-01-17 12:19:40 +0100
commit34ad8447c3d8bc4609289ce0d7686491d2a54c38 (patch)
tree59c6a01f12b633f75420ca203a5c3838b38268b6 /nixos
parent2054bb5de5d9cb6da9253b8716066d0f272c5ef2 (diff)
nixos: systemd: add systemd.user.tmpfiles
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/system/boot/systemd/user.nix75
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/systemd-user-tmpfiles-rules.nix35
3 files changed, 111 insertions, 0 deletions
diff --git a/nixos/modules/system/boot/systemd/user.nix b/nixos/modules/system/boot/systemd/user.nix
index 46d66fe4e688c..c91849bd26943 100644
--- a/nixos/modules/system/boot/systemd/user.nix
+++ b/nixos/modules/system/boot/systemd/user.nix
@@ -39,6 +39,20 @@ let
     "timers.target"
     "xdg-desktop-autostart.target"
   ] ++ config.systemd.additionalUpstreamUserUnits;
+
+  writeTmpfiles = { rules, user ? null }:
+    let
+      suffix = if user == null then "" else "-${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 {
@@ -93,6 +107,42 @@ in {
       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.
+        '';
+        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;
@@ -154,5 +204,30 @@ in {
     # 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;
   };
 }
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 6f056de2ed5cc..fa4b8b87194cd 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -642,6 +642,7 @@ in {
   systemd-portabled = handleTest ./systemd-portabled.nix {};
   systemd-shutdown = handleTest ./systemd-shutdown.nix {};
   systemd-timesyncd = handleTest ./systemd-timesyncd.nix {};
+  systemd-user-tmpfiles-rules = handleTest ./systemd-user-tmpfiles-rules.nix {};
   systemd-misc = handleTest ./systemd-misc.nix {};
   tandoor-recipes = handleTest ./tandoor-recipes.nix {};
   taskserver = handleTest ./taskserver.nix {};
diff --git a/nixos/tests/systemd-user-tmpfiles-rules.nix b/nixos/tests/systemd-user-tmpfiles-rules.nix
new file mode 100644
index 0000000000000..bf29b4b57be3e
--- /dev/null
+++ b/nixos/tests/systemd-user-tmpfiles-rules.nix
@@ -0,0 +1,35 @@
+import ./make-test-python.nix ({ lib, ... }: {
+  name = "systemd-user-tmpfiles-rules";
+
+  meta = with lib.maintainers; {
+    maintainers = [ schnusch ];
+  };
+
+  nodes.machine = { ... }: {
+    users.users = {
+      alice.isNormalUser = true;
+      bob.isNormalUser = true;
+    };
+
+    systemd.user.tmpfiles = {
+      rules = [
+        "d %h/user_tmpfiles_created"
+      ];
+      users.alice.rules = [
+        "d %h/only_alice"
+      ];
+    };
+  };
+
+  testScript = { ... }: ''
+    machine.succeed("loginctl enable-linger alice bob")
+
+    machine.wait_until_succeeds("systemctl --user --machine=alice@ is-active systemd-tmpfiles-setup.service")
+    machine.succeed("[ -d ~alice/user_tmpfiles_created ]")
+    machine.succeed("[ -d ~alice/only_alice ]")
+
+    machine.wait_until_succeeds("systemctl --user --machine=bob@ is-active systemd-tmpfiles-setup.service")
+    machine.succeed("[ -d ~bob/user_tmpfiles_created ]")
+    machine.succeed("[ ! -e ~bob/only_alice ]")
+  '';
+})