nixos/ssh: use correct executable for grep in ssh-askpass-wrapper (#373746)
[NixPkgs.git] / nixos / modules / system / boot / systemd / tmpfiles.nix
blob10d58dc0c706ed76c7da16de187fe20ddf22da25
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.systemd.tmpfiles;
7   initrdCfg = config.boot.initrd.systemd.tmpfiles;
8   systemd = config.systemd.package;
10   attrsWith' = placeholder: elemType: types.attrsWith {
11     inherit elemType placeholder;
12   };
14   settingsOption = {
15     description = ''
16       Declare systemd-tmpfiles rules to create, delete, and clean up volatile
17       and temporary files and directories.
19       Even though the service is called `*tmp*files` you can also create
20       persistent files.
21     '';
22     example = {
23       "10-mypackage" = {
24         "/var/lib/my-service/statefolder".d = {
25           mode = "0755";
26           user = "root";
27           group = "root";
28         };
29       };
30     };
31     default = {};
32     type = attrsWith' "config-name" (attrsWith' "tmpfiles-type" (attrsWith' "path" (types.submodule ({ name, config, ... }: {
33       options.type = mkOption {
34         type = types.str;
35         default = name;
36         example = "d";
37         description = ''
38           The type of operation to perform on the file.
40           The type consists of a single letter and optionally one or more
41           modifier characters.
43           Please see the upstream documentation for the available types and
44           more details:
45           <https://www.freedesktop.org/software/systemd/man/tmpfiles.d>
46         '';
47       };
48       options.mode = mkOption {
49         type = types.str;
50         default = "-";
51         example = "0755";
52         description = ''
53           The file access mode to use when creating this file or directory.
54         '';
55       };
56       options.user = mkOption {
57         type = types.str;
58         default = "-";
59         example = "root";
60         description = ''
61           The user of the file.
63           This may either be a numeric ID or a user/group name.
65           If omitted or when set to `"-"`, the user and group of the user who
66           invokes systemd-tmpfiles is used.
67         '';
68       };
69       options.group = mkOption {
70         type = types.str;
71         default = "-";
72         example = "root";
73         description = ''
74           The group of the file.
76           This may either be a numeric ID or a user/group name.
78           If omitted or when set to `"-"`, the user and group of the user who
79           invokes systemd-tmpfiles is used.
80         '';
81       };
82       options.age = mkOption {
83         type = types.str;
84         default = "-";
85         example = "10d";
86         description = ''
87           Delete a file when it reaches a certain age.
89           If a file or directory is older than the current time minus the age
90           field, it is deleted.
92           If set to `"-"` no automatic clean-up is done.
93         '';
94       };
95       options.argument = mkOption {
96         type = types.str;
97         default = "";
98         example = "";
99         description = ''
100           An argument whose meaning depends on the type of operation.
102           Please see the upstream documentation for the meaning of this
103           parameter in different situations:
104           <https://www.freedesktop.org/software/systemd/man/tmpfiles.d>
105         '';
106       };
107     }))));
108   };
110   # generates a single entry for a tmpfiles.d rule
111   settingsEntryToRule = path: entry: ''
112     '${entry.type}' '${path}' '${entry.mode}' '${entry.user}' '${entry.group}' '${entry.age}' ${entry.argument}
113   '';
115   # generates a list of tmpfiles.d rules from the attrs (paths) under tmpfiles.settings.<name>
116   pathsToRules = mapAttrsToList (path: types:
117     concatStrings (
118       mapAttrsToList (_type: settingsEntryToRule path) types
119     )
120   );
122   mkRuleFileContent = paths: concatStrings (pathsToRules paths);
125   options = {
126     systemd.tmpfiles.rules = mkOption {
127       type = types.listOf types.str;
128       default = [];
129       example = [ "d /tmp 1777 root root 10d" ];
130       description = ''
131         Rules for creation, deletion and cleaning of volatile and temporary files
132         automatically. See
133         {manpage}`tmpfiles.d(5)`
134         for the exact format.
135       '';
136     };
138     systemd.tmpfiles.settings = mkOption settingsOption;
140     boot.initrd.systemd.tmpfiles.settings = mkOption (settingsOption // {
141       description = ''
142         Similar to {option}`systemd.tmpfiles.settings` but the rules are
143         only applied by systemd-tmpfiles before `initrd-switch-root.target`.
145         See {manpage}`bootup(7)`.
146       '';
147     });
149     systemd.tmpfiles.packages = mkOption {
150       type = types.listOf types.package;
151       default = [];
152       example = literalExpression "[ pkgs.lvm2 ]";
153       apply = map getLib;
154       description = ''
155         List of packages containing {command}`systemd-tmpfiles` rules.
157         All files ending in .conf found in
158         {file}`«pkg»/lib/tmpfiles.d`
159         will be included.
160         If this folder does not exist or does not contain any files an error will be returned instead.
162         If a {file}`lib` output is available, rules are searched there and only there.
163         If there is no {file}`lib` output it will fall back to {file}`out`
164         and if that does not exist either, the default output will be used.
165       '';
166     };
167   };
169   config = {
170     warnings =
171       let
172         paths = lib.filter (path:
173           path != null && lib.hasPrefix "/etc/tmpfiles.d/" path
174         ) (map (path: path.target) config.boot.initrd.systemd.storePaths);
175       in
176       lib.optional (lib.length paths > 0) (lib.concatStringsSep " " [
177         "Files inside /etc/tmpfiles.d in the initrd need to be created with"
178         "boot.initrd.systemd.tmpfiles.settings."
179         "Creating them by hand using boot.initrd.systemd.contents or"
180         "boot.initrd.systemd.storePaths will lead to errors in the future."
181         "Found these problematic files: ${lib.concatStringsSep ", " paths}"
182       ]);
184     systemd.additionalUpstreamSystemUnits = [
185       "systemd-tmpfiles-clean.service"
186       "systemd-tmpfiles-clean.timer"
187       "systemd-tmpfiles-setup-dev-early.service"
188       "systemd-tmpfiles-setup-dev.service"
189       "systemd-tmpfiles-setup.service"
190     ];
192     systemd.additionalUpstreamUserUnits = [
193       "systemd-tmpfiles-clean.service"
194       "systemd-tmpfiles-clean.timer"
195       "systemd-tmpfiles-setup.service"
196     ];
198     # Allow systemd-tmpfiles to be restarted by switch-to-configuration. This
199     # service is not pulled into the normal boot process. It only exists for
200     # switch-to-configuration.
201     #
202     # This needs to be a separate unit because it does not execute
203     # systemd-tmpfiles with `--boot` as that is supposed to only be executed
204     # once at boot time.
205     #
206     # Keep this aligned with the upstream `systemd-tmpfiles-setup.service` unit.
207     systemd.services."systemd-tmpfiles-resetup" = {
208       description = "Re-setup tmpfiles on a system that is already running.";
210       requiredBy = [ "sysinit-reactivation.target" ];
211       after = [ "local-fs.target" "systemd-sysusers.service" "systemd-journald.service" ];
212       before = [ "sysinit-reactivation.target" "shutdown.target" ];
213       conflicts = [ "shutdown.target" ];
214       restartTriggers = [ config.environment.etc."tmpfiles.d".source ];
216       unitConfig.DefaultDependencies = false;
218       serviceConfig = {
219         Type = "oneshot";
220         RemainAfterExit = true;
221         ExecStart = "systemd-tmpfiles --create --remove --exclude-prefix=/dev";
222         SuccessExitStatus = "DATAERR CANTCREAT";
223         ImportCredential = [
224           "tmpfiles.*"
225           "loging.motd"
226           "login.issue"
227           "network.hosts"
228           "ssh.authorized_keys.root"
229         ];
230       };
231     };
233     environment.etc = {
234       "tmpfiles.d".source = (pkgs.symlinkJoin {
235         name = "tmpfiles.d";
236         paths = map (p: p + "/lib/tmpfiles.d") cfg.packages;
237         postBuild = ''
238           for i in $(cat $pathsPath); do
239             (test -d "$i" && test $(ls "$i"/*.conf | wc -l) -ge 1) || (
240               echo "ERROR: The path '$i' from systemd.tmpfiles.packages contains no *.conf files."
241               exit 1
242             )
243           done
244         '' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) ''
245           rm -f $out/${removePrefix "tmpfiles.d/" name}
246         '') config.system.build.etc.passthru.targets;
247       }) + "/*";
248       "mtab" = {
249         mode = "direct-symlink";
250         source = "/proc/mounts";
251       };
252     };
254     systemd.tmpfiles.packages = [
255       # Default tmpfiles rules provided by systemd
256       (pkgs.runCommand "systemd-default-tmpfiles" {} ''
257         mkdir -p $out/lib/tmpfiles.d
258         cd $out/lib/tmpfiles.d
260         ln -s "${systemd}/example/tmpfiles.d/home.conf"
261         ln -s "${systemd}/example/tmpfiles.d/journal-nocow.conf"
262         ln -s "${systemd}/example/tmpfiles.d/portables.conf"
263         ln -s "${systemd}/example/tmpfiles.d/static-nodes-permissions.conf"
264         ln -s "${systemd}/example/tmpfiles.d/systemd.conf"
265         ln -s "${systemd}/example/tmpfiles.d/systemd-nologin.conf"
266         ln -s "${systemd}/example/tmpfiles.d/systemd-nspawn.conf"
267         ln -s "${systemd}/example/tmpfiles.d/systemd-tmp.conf"
268         ln -s "${systemd}/example/tmpfiles.d/tmp.conf"
269         ln -s "${systemd}/example/tmpfiles.d/var.conf"
270         ln -s "${systemd}/example/tmpfiles.d/x11.conf"
271       '')
272       # User-specified tmpfiles rules
273       (pkgs.writeTextFile {
274         name = "nixos-tmpfiles.d";
275         destination = "/lib/tmpfiles.d/00-nixos.conf";
276         text = ''
277           # This file is created automatically and should not be modified.
278           # Please change the option ‘systemd.tmpfiles.rules’ instead.
280           ${concatStringsSep "\n" cfg.rules}
281         '';
282       })
283     ] ++ (mapAttrsToList (name: paths:
284       pkgs.writeTextDir "lib/tmpfiles.d/${name}.conf" (mkRuleFileContent paths)
285     ) cfg.settings);
287     systemd.tmpfiles.rules = [
288       "d  /run/lock                          0755 root root - -"
289       "d  /var/db                            0755 root root - -"
290       "L  /var/lock                          -    -    -    - ../run/lock"
291     ] ++ lib.optionals config.nix.enable [
292       "d  /nix/var                           0755 root root - -"
293       "L+ /nix/var/nix/gcroots/booted-system 0755 root root - /run/booted-system"
294     ]
295     # Boot-time cleanup
296     ++ [
297       "R! /etc/group.lock                    -    -    -    - -"
298       "R! /etc/passwd.lock                   -    -    -    - -"
299       "R! /etc/shadow.lock                   -    -    -    - -"
300     ] ++ lib.optionals config.nix.enable [
301       "R! /nix/var/nix/gcroots/tmp           -    -    -    - -"
302       "R! /nix/var/nix/temproots             -    -    -    - -"
303     ];
305     boot.initrd.systemd = {
306       additionalUpstreamUnits = [
307         "systemd-tmpfiles-setup-dev-early.service"
308         "systemd-tmpfiles-setup-dev.service"
309         "systemd-tmpfiles-setup.service"
310       ];
312       # override to exclude the prefix /sysroot, because it is not necessarily set up when the unit starts
313       services.systemd-tmpfiles-setup.serviceConfig = {
314         ExecStart = [
315           ""
316           "systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev --exclude-prefix=/sysroot"
317         ];
318       };
320       # sets up files under the prefix /sysroot, after the hierarchy is available and before nixos activation
321       services.systemd-tmpfiles-setup-sysroot = {
322         description = "Create Volatile Files and Directories in the Real Root";
323         after = [ "initrd-fs.target" ];
324         before = [
325           "initrd-nixos-activation.service"
326           "shutdown.target" "initrd-switch-root.target"
327         ];
328         conflicts = [ "shutdown.target" "initrd-switch-root.target" ];
329         wantedBy = [ "initrd.target" ];
330         serviceConfig = {
331           Type = "oneshot";
332           RemainAfterExit = true;
333           ExecStart = "systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev --prefix=/sysroot";
334           SuccessExitStatus = [ "DATAERR CANTCREAT" ];
335           ImportCredential = [
336             "tmpfiles.*"
337             "login.motd"
338             "login.issue"
339             "network.hosts"
340             "ssh.authorized_keys.root"
341           ];
342         };
343         unitConfig = {
344           DefaultDependencies = false;
345           RefuseManualStop = true;
346         };
348       };
350       contents."/etc/tmpfiles.d" = mkIf (initrdCfg.settings != { }) {
351         source = pkgs.linkFarm "initrd-tmpfiles.d" (
352           mapAttrsToList
353             (name: paths: {
354               name = "${name}.conf";
355               path = pkgs.writeText "${name}.conf" (mkRuleFileContent paths);
356             }
357             )
358             initrdCfg.settings);
359       };
360     };
361   };