1{ config, lib, pkgs, utils, ... }:
2
3with lib;
4
5let
6 cfg = config.systemd.tmpfiles;
7 systemd = config.systemd.package;
8in
9{
10 options = {
11 systemd.tmpfiles.rules = mkOption {
12 type = types.listOf types.str;
13 default = [];
14 example = [ "d /tmp 1777 root root 10d" ];
15 description = lib.mdDoc ''
16 Rules for creation, deletion and cleaning of volatile and temporary files
17 automatically. See
18 {manpage}`tmpfiles.d(5)`
19 for the exact format.
20 '';
21 };
22
23 systemd.tmpfiles.settings = mkOption {
24 description = lib.mdDoc ''
25 Declare systemd-tmpfiles rules to create, delete, and clean up volatile
26 and temporary files and directories.
27
28 Even though the service is called `*tmp*files` you can also create
29 persistent files.
30 '';
31 example = {
32 "10-mypackage" = {
33 "/var/lib/my-service/statefolder".d = {
34 mode = "0755";
35 user = "root";
36 group = "root";
37 };
38 };
39 };
40 default = {};
41 type = types.attrsOf (types.attrsOf (types.attrsOf (types.submodule ({ name, config, ... }: {
42 options.type = mkOption {
43 type = types.str;
44 default = name;
45 example = "d";
46 description = lib.mdDoc ''
47 The type of operation to perform on the file.
48
49 The type consists of a single letter and optionally one or more
50 modifier characters.
51
52 Please see the upstream documentation for the available types and
53 more details:
54 <https://www.freedesktop.org/software/systemd/man/tmpfiles.d>
55 '';
56 };
57 options.mode = mkOption {
58 type = types.str;
59 default = "-";
60 example = "0755";
61 description = lib.mdDoc ''
62 The file access mode to use when creating this file or directory.
63 '';
64 };
65 options.user = mkOption {
66 type = types.str;
67 default = "-";
68 example = "root";
69 description = lib.mdDoc ''
70 The user of the file.
71
72 This may either be a numeric ID or a user/group name.
73
74 If omitted or when set to `"-"`, the user and group of the user who
75 invokes systemd-tmpfiles is used.
76 '';
77 };
78 options.group = mkOption {
79 type = types.str;
80 default = "-";
81 example = "root";
82 description = lib.mdDoc ''
83 The group of the file.
84
85 This may either be a numeric ID or a user/group name.
86
87 If omitted or when set to `"-"`, the user and group of the user who
88 invokes systemd-tmpfiles is used.
89 '';
90 };
91 options.age = mkOption {
92 type = types.str;
93 default = "-";
94 example = "10d";
95 description = lib.mdDoc ''
96 Delete a file when it reaches a certain age.
97
98 If a file or directory is older than the current time minus the age
99 field, it is deleted.
100
101 If set to `"-"` no automatic clean-up is done.
102 '';
103 };
104 options.argument = mkOption {
105 type = types.str;
106 default = "";
107 example = "";
108 description = lib.mdDoc ''
109 An argument whose meaning depends on the type of operation.
110
111 Please see the upstream documentation for the meaning of this
112 parameter in different situations:
113 <https://www.freedesktop.org/software/systemd/man/tmpfiles.d>
114 '';
115 };
116 }))));
117 };
118
119 systemd.tmpfiles.packages = mkOption {
120 type = types.listOf types.package;
121 default = [];
122 example = literalExpression "[ pkgs.lvm2 ]";
123 apply = map getLib;
124 description = lib.mdDoc ''
125 List of packages containing {command}`systemd-tmpfiles` rules.
126
127 All files ending in .conf found in
128 {file}`«pkg»/lib/tmpfiles.d`
129 will be included.
130 If this folder does not exist or does not contain any files an error will be returned instead.
131
132 If a {file}`lib` output is available, rules are searched there and only there.
133 If there is no {file}`lib` output it will fall back to {file}`out`
134 and if that does not exist either, the default output will be used.
135 '';
136 };
137 };
138
139 config = {
140 systemd.additionalUpstreamSystemUnits = [
141 "systemd-tmpfiles-clean.service"
142 "systemd-tmpfiles-clean.timer"
143 "systemd-tmpfiles-setup.service"
144 "systemd-tmpfiles-setup-dev.service"
145 ];
146
147 systemd.additionalUpstreamUserUnits = [
148 "systemd-tmpfiles-clean.service"
149 "systemd-tmpfiles-clean.timer"
150 "systemd-tmpfiles-setup.service"
151 ];
152
153 environment.etc = {
154 "tmpfiles.d".source = (pkgs.symlinkJoin {
155 name = "tmpfiles.d";
156 paths = map (p: p + "/lib/tmpfiles.d") cfg.packages;
157 postBuild = ''
158 for i in $(cat $pathsPath); do
159 (test -d "$i" && test $(ls "$i"/*.conf | wc -l) -ge 1) || (
160 echo "ERROR: The path '$i' from systemd.tmpfiles.packages contains no *.conf files."
161 exit 1
162 )
163 done
164 '' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) ''
165 rm -f $out/${removePrefix "tmpfiles.d/" name}
166 '') config.system.build.etc.passthru.targets;
167 }) + "/*";
168 };
169
170 systemd.tmpfiles.packages = [
171 # Default tmpfiles rules provided by systemd
172 (pkgs.runCommand "systemd-default-tmpfiles" {} ''
173 mkdir -p $out/lib/tmpfiles.d
174 cd $out/lib/tmpfiles.d
175
176 ln -s "${systemd}/example/tmpfiles.d/home.conf"
177 ln -s "${systemd}/example/tmpfiles.d/journal-nocow.conf"
178 ln -s "${systemd}/example/tmpfiles.d/portables.conf"
179 ln -s "${systemd}/example/tmpfiles.d/static-nodes-permissions.conf"
180 ln -s "${systemd}/example/tmpfiles.d/systemd.conf"
181 ln -s "${systemd}/example/tmpfiles.d/systemd-nologin.conf"
182 ln -s "${systemd}/example/tmpfiles.d/systemd-nspawn.conf"
183 ln -s "${systemd}/example/tmpfiles.d/systemd-tmp.conf"
184 ln -s "${systemd}/example/tmpfiles.d/tmp.conf"
185 ln -s "${systemd}/example/tmpfiles.d/var.conf"
186 ln -s "${systemd}/example/tmpfiles.d/x11.conf"
187 '')
188 # User-specified tmpfiles rules
189 (pkgs.writeTextFile {
190 name = "nixos-tmpfiles.d";
191 destination = "/lib/tmpfiles.d/00-nixos.conf";
192 text = ''
193 # This file is created automatically and should not be modified.
194 # Please change the option ‘systemd.tmpfiles.rules’ instead.
195
196 ${concatStringsSep "\n" cfg.rules}
197 '';
198 })
199 ] ++ (mapAttrsToList (name: paths:
200 pkgs.writeTextDir "lib/tmpfiles.d/${name}.conf" (concatStrings (mapAttrsToList (path: types:
201 concatStrings (mapAttrsToList (_type: entry: ''
202 '${entry.type}' '${path}' '${entry.mode}' '${entry.user}' '${entry.group}' '${entry.age}' ${entry.argument}
203 '') types)
204 ) paths ))
205 ) cfg.settings);
206
207 systemd.tmpfiles.rules = [
208 "d /nix/var 0755 root root - -"
209 "L+ /nix/var/nix/gcroots/booted-system 0755 root root - /run/booted-system"
210 "d /run/lock 0755 root root - -"
211 "d /var/db 0755 root root - -"
212 "L /etc/mtab - - - - ../proc/mounts"
213 "L /var/lock - - - - ../run/lock"
214 # Boot-time cleanup
215 "R! /etc/group.lock - - - - -"
216 "R! /etc/passwd.lock - - - - -"
217 "R! /etc/shadow.lock - - - - -"
218 "R! /etc/mtab* - - - - -"
219 "R! /nix/var/nix/gcroots/tmp - - - - -"
220 "R! /nix/var/nix/temproots - - - - -"
221 ];
222 };
223}