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