diff --git a/nixos/modules/system/boot/systemd/user.nix b/nixos/modules/system/boot/systemd/user.nix index 46d66fe4e688..c91849bd2694 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 6f056de2ed5c..fa4b8b87194c 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 000000000000..bf29b4b57be3 --- /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 ]") + ''; +})