Merge pull request #329823 from ExpidusOS/fix/pkgsllvm/elfutils
[NixPkgs.git] / nixos / modules / misc / locate.nix
blob4692ed15a9567df9817d3dacb380fbd8664956d4
1 { config, lib, pkgs, ... }:
3 let
4   cfg = config.services.locate;
5   isMLocate = lib.hasPrefix "mlocate" cfg.package.name;
6   isPLocate = lib.hasPrefix "plocate" cfg.package.name;
7   isMorPLocate = isMLocate || isPLocate;
8   isFindutils = lib.hasPrefix "findutils" cfg.package.name;
9 in
11   imports = [
12     (lib.mkRenamedOptionModule [ "services" "locate" "period" ] [ "services" "locate" "interval" ])
13     (lib.mkRenamedOptionModule [ "services" "locate" "locate" ] [ "services" "locate" "package" ])
14     (lib.mkRemovedOptionModule [ "services" "locate" "includeStore" ] "Use services.locate.prunePaths")
15   ];
17   options.services.locate = {
18     enable = lib.mkOption {
19       type = lib.types.bool;
20       default = false;
21       description = ''
22         If enabled, NixOS will periodically update the database of
23         files used by the {command}`locate` command.
24       '';
25     };
27     package = lib.mkPackageOption pkgs [ "findutils" "locate" ] {
28       example = "mlocate";
29     };
31     interval = lib.mkOption {
32       type = lib.types.str;
33       default = "02:15";
34       example = "hourly";
35       description = ''
36         Update the locate database at this interval. Updates by
37         default at 2:15 AM every day.
39         The format is described in
40         {manpage}`systemd.time(7)`.
42         To disable automatic updates, set to `"never"`
43         and run {command}`updatedb` manually.
44       '';
45     };
47     extraFlags = lib.mkOption {
48       type = lib.types.listOf lib.types.str;
49       default = [ ];
50       description = ''
51         Extra flags to pass to {command}`updatedb`.
52       '';
53     };
55     output = lib.mkOption {
56       type = lib.types.path;
57       default = "/var/cache/locatedb";
58       description = ''
59         The database file to build.
60       '';
61     };
63     localuser = lib.mkOption {
64       type = lib.types.nullOr lib.types.str;
65       default = "nobody";
66       description = ''
67         The user to search non-network directories as, using
68         {command}`su`.
69       '';
70     };
72     pruneFS = lib.mkOption {
73       type = lib.types.listOf lib.types.str;
74       default = [
75         "afs"
76         "anon_inodefs"
77         "auto"
78         "autofs"
79         "bdev"
80         "binfmt"
81         "binfmt_misc"
82         "ceph"
83         "cgroup"
84         "cgroup2"
85         "cifs"
86         "coda"
87         "configfs"
88         "cramfs"
89         "cpuset"
90         "curlftpfs"
91         "debugfs"
92         "devfs"
93         "devpts"
94         "devtmpfs"
95         "ecryptfs"
96         "eventpollfs"
97         "exofs"
98         "futexfs"
99         "ftpfs"
100         "fuse"
101         "fusectl"
102         "fusesmb"
103         "fuse.ceph"
104         "fuse.glusterfs"
105         "fuse.gvfsd-fuse"
106         "fuse.mfs"
107         "fuse.rclone"
108         "fuse.rozofs"
109         "fuse.sshfs"
110         "gfs"
111         "gfs2"
112         "hostfs"
113         "hugetlbfs"
114         "inotifyfs"
115         "iso9660"
116         "jffs2"
117         "lustre"
118         "lustre_lite"
119         "misc"
120         "mfs"
121         "mqueue"
122         "ncpfs"
123         "nfs"
124         "NFS"
125         "nfs4"
126         "nfsd"
127         "nnpfs"
128         "ocfs"
129         "ocfs2"
130         "pipefs"
131         "proc"
132         "ramfs"
133         "rpc_pipefs"
134         "securityfs"
135         "selinuxfs"
136         "sfs"
137         "shfs"
138         "smbfs"
139         "sockfs"
140         "spufs"
141         "sshfs"
142         "subfs"
143         "supermount"
144         "sysfs"
145         "tmpfs"
146         "tracefs"
147         "ubifs"
148         "udev"
149         "udf"
150         "usbfs"
151         "vboxsf"
152         "vperfctrfs"
153       ];
154       description = ''
155         Which filesystem types to exclude from indexing
156       '';
157     };
159     prunePaths = lib.mkOption {
160       type = lib.types.listOf lib.types.path;
161       default = [
162         "/tmp"
163         "/var/tmp"
164         "/var/cache"
165         "/var/lock"
166         "/var/run"
167         "/var/spool"
168         "/nix/store"
169         "/nix/var/log/nix"
170       ];
171       description = ''
172         Which paths to exclude from indexing
173       '';
174     };
176     pruneNames = lib.mkOption {
177       type = lib.types.listOf lib.types.str;
178       default = lib.optionals (!isFindutils) [ ".bzr" ".cache" ".git" ".hg" ".svn" ];
179       defaultText = lib.literalMD ''
180         `[ ".bzr" ".cache" ".git" ".hg" ".svn" ]`, if
181         supported by the locate implementation (i.e. mlocate or plocate).
182       '';
183       description = ''
184         Directory components which should exclude paths containing them from indexing
185       '';
186     };
188     pruneBindMounts = lib.mkOption {
189       type = lib.types.bool;
190       default = false;
191       description = ''
192         Whether not to index bind mounts
193       '';
194     };
196   };
198   config = lib.mkIf cfg.enable {
199     users.groups = lib.mkMerge [
200       (lib.mkIf isMLocate { mlocate = { }; })
201       (lib.mkIf isPLocate { plocate = { }; })
202     ];
204     security.wrappers =
205       let
206         common = {
207           owner = "root";
208           permissions = "u+rx,g+x,o+x";
209           setgid = true;
210           setuid = false;
211         };
212         mlocate = lib.mkIf isMLocate {
213           group = "mlocate";
214           source = "${cfg.package}/bin/locate";
215         };
216         plocate = lib.mkIf isPLocate {
217           group = "plocate";
218           source = "${cfg.package}/bin/plocate";
219         };
220       in
221       lib.mkIf isMorPLocate {
222         locate = lib.mkMerge [ common mlocate plocate ];
223         plocate = lib.mkIf isPLocate (lib.mkMerge [ common plocate ]);
224       };
226     environment = {
227       # write /etc/updatedb.conf for manual calls to `updatedb`
228       etc."updatedb.conf".text = ''
229         PRUNEFS="${lib.concatStringsSep " " cfg.pruneFS}"
230         PRUNENAMES="${lib.concatStringsSep " " cfg.pruneNames}"
231         PRUNEPATHS="${lib.concatStringsSep " " cfg.prunePaths}"
232         PRUNE_BIND_MOUNTS="${if cfg.pruneBindMounts then "yes" else "no"}"
233       '';
235       systemPackages = [ cfg.package ];
237       variables = lib.mkIf isFindutils {
238         LOCATE_PATH = cfg.output;
239       };
240     };
242     warnings = lib.optional (isMorPLocate && cfg.localuser != null)
243       "mlocate and plocate do not support the services.locate.localuser option. updatedb will run as root. Silence this warning by setting services.locate.localuser = null."
244     ++ lib.optional (isFindutils && cfg.pruneNames != [ ])
245       "findutils locate does not support pruning by directory component"
246     ++ lib.optional (isFindutils && cfg.pruneBindMounts)
247       "findutils locate does not support skipping bind mounts";
249     systemd.services.update-locatedb = {
250       description = "Update Locate Database";
251       path = lib.mkIf (!isMorPLocate) [ pkgs.su ];
253       # mlocate's updatedb takes flags via a configuration file or
254       # on the command line, but not by environment variable.
255       script =
256         if isMorPLocate then
257           let
258             toFlags = x:
259               lib.optional (cfg.${x} != [ ])
260                 "--${lib.toLower x} '${lib.concatStringsSep " " cfg.${x}}'";
261             args = lib.concatLists (map toFlags [ "pruneFS" "pruneNames" "prunePaths" ]);
262           in
263           ''
264             exec ${cfg.package}/bin/updatedb \
265               --output ${toString cfg.output} ${lib.concatStringsSep " " args} \
266               --prune-bind-mounts ${if cfg.pruneBindMounts then "yes" else "no"} \
267               ${lib.concatStringsSep " " cfg.extraFlags}
268           ''
269         else ''
270           exec ${cfg.package}/bin/updatedb \
271             ${lib.optionalString (cfg.localuser != null && !isMorPLocate) "--localuser=${cfg.localuser}"} \
272             --output=${toString cfg.output} ${lib.concatStringsSep " " cfg.extraFlags}
273         '';
274       environment = lib.optionalAttrs (!isMorPLocate) {
275         PRUNEFS = lib.concatStringsSep " " cfg.pruneFS;
276         PRUNEPATHS = lib.concatStringsSep " " cfg.prunePaths;
277         PRUNENAMES = lib.concatStringsSep " " cfg.pruneNames;
278         PRUNE_BIND_MOUNTS = if cfg.pruneBindMounts then "yes" else "no";
279       };
280       serviceConfig = {
281         Nice = 19;
282         IOSchedulingClass = "idle";
283         PrivateTmp = "yes";
284         PrivateNetwork = "yes";
285         NoNewPrivileges = "yes";
286         ReadOnlyPaths = "/";
287         # Use dirOf cfg.output because mlocate creates temporary files next to
288         # the actual database. We could specify and create them as well,
289         # but that would make this quite brittle when they change something.
290         # NOTE: If /var/cache does not exist, this leads to the misleading error message:
291         # update-locatedb.service: Failed at step NAMESPACE spawning …/update-locatedb-start: No such file or directory
292         ReadWritePaths = dirOf cfg.output;
293       };
294     };
296     systemd.timers.update-locatedb = lib.mkIf (cfg.interval != "never") {
297       description = "Update timer for locate database";
298       partOf = [ "update-locatedb.service" ];
299       wantedBy = [ "timers.target" ];
300       timerConfig = {
301         OnCalendar = cfg.interval;
302         Persistent = true;
303       };
304     };
305   };
307   meta.maintainers = with lib.maintainers; [ SuperSandro2000 ];