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