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