1 /* updatedb configuration.
3 Copyright (C) 2005, 2007, 2008 Red Hat, Inc. All rights reserved.
4 This copyrighted material is made available to anyone wishing to use, modify,
5 copy, or redistribute it subject to the terms and conditions of the GNU General
8 This program is distributed in the hope that it will be useful, but WITHOUT ANY
9 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
10 PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License along with
13 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
14 Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 Author: Miloslav Trmac <mitr@redhat.com>
18 plocate modifications: Copyright (C) 2020 Steinar H. Gunderson.
19 plocate parts and modifications are licensed under the GPLv2 or, at your option,
40 /* true if locate(1) should check whether files are visible before reporting
42 bool conf_check_visibility = true;
44 /* Filesystems to skip, converted to uppercase and sorted by name */
45 vector<string> conf_prunefs;
47 /* Directory names to skip, sorted by name */
48 vector<string> conf_prunenames;
50 /* Paths to skip, sorted by name using dir_path_cmp () */
51 vector<string> conf_prunepaths;
53 /* true if bind mounts should be skipped */
54 bool conf_prune_bind_mounts; /* = false; */
56 /* true if pruning debug output was requested */
57 bool conf_debug_pruning; /* = false; */
59 /* Root of the directory tree to store in the database (canonical) */
60 char *conf_scan_root; /* = NULL; */
62 /* Absolute (not necessarily canonical) path to the database */
65 /* 1 if file names should be written to stdout as they are found */
66 bool conf_verbose; /* = false; */
68 /* Configuration representation for the database configuration block */
71 /* Absolute (not necessarily canonical) path to the config file */
72 string conf_configfile_str;
73 const char *conf_configfile;
75 int conf_block_size = 32;
76 bool use_debug = false;
78 /* Parse a STR, store the parsed boolean value to DEST;
79 return 0 if OK, -1 on error. */
81 parse_bool(bool *dest, const char *str)
83 if (strcmp(str, "0") == 0 || strcmp(str, "no") == 0) {
87 if (strcmp(str, "1") == 0 || strcmp(str, "yes") == 0) {
94 /* String list handling */
96 /* Add values from space-separated VAL to VAR and LIST */
98 var_add_values(vector<string> *list, const char *val)
103 while (isspace((unsigned char)*val))
110 while (*val != 0 && !isspace((unsigned char)*val));
111 list->emplace_back(start, val - start);
115 /* Finish variable LIST, sort its contents, remove duplicates */
117 var_finish(vector<string> *list)
119 sort(list->begin(), list->end());
120 auto new_end = unique(list->begin(), list->end());
121 list->erase(new_end, list->end());
124 /* UPDATEDB_CONF parsing */
126 /* UPDATEDB_CONF (locked) */
127 static FILE *uc_file;
128 /* Line number at token start; type matches error_at_line () */
129 static unsigned uc_line;
130 /* Current line number; type matches error_at_line () */
131 static unsigned uc_current_line;
132 /* Last string returned by uc_lex */
133 static string uc_lex_buf;
143 UCT_PRUNE_BIND_MOUNTS,
149 /* Return next token from uc_file; for UCT_IDENTIFIER, UCT_QUOTED or keywords,
150 store the data to uc_lex_buf (valid until next call). */
157 uc_line = uc_current_line;
159 c = getc_unlocked(uc_file);
162 } while (c != '\n' && isspace((unsigned char)c));
166 c = getc_unlocked(uc_file);
179 while ((c = getc_unlocked(uc_file)) != '"') {
180 if (c == EOF || c == '\n') {
181 fprintf(stderr, "%s:%u: missing closing `\"'\n",
182 UPDATEDB_CONF, uc_line);
185 uc_lex_buf.push_back(c);
191 if (!isalpha((unsigned char)c) && c != '_')
194 uc_lex_buf.push_back(c);
195 c = getc_unlocked(uc_file);
196 } while (c != EOF && (isalnum((unsigned char)c) || c == '_'));
198 if (uc_lex_buf == "PRUNE_BIND_MOUNTS")
199 return UCT_PRUNE_BIND_MOUNTS;
200 if (uc_lex_buf == "PRUNEFS")
202 if (uc_lex_buf == "PRUNENAMES")
203 return UCT_PRUNENAMES;
204 if (uc_lex_buf == "PRUNEPATHS")
205 return UCT_PRUNEPATHS;
206 return UCT_IDENTIFIER;
211 /* Parse /etc/updatedb.conf. Exit on I/O or syntax error. */
213 parse_updatedb_conf(void)
215 bool had_prune_bind_mounts, had_prunefs, had_prunenames, had_prunepaths;
217 uc_file = fopen(UPDATEDB_CONF, "r");
218 if (uc_file == NULL) {
219 if (errno != ENOENT) {
220 perror(UPDATEDB_CONF);
227 had_prune_bind_mounts = false;
229 had_prunenames = false;
230 had_prunepaths = false;
233 int var_token, token;
243 case UCT_PRUNE_BIND_MOUNTS:
244 had_var = &had_prune_bind_mounts;
248 had_var = &had_prunefs;
252 had_var = &had_prunenames;
256 had_var = &had_prunepaths;
260 fprintf(stderr, "%s:%u: unknown variable: `%s'\n",
261 UPDATEDB_CONF, uc_line, uc_lex_buf.c_str());
265 fprintf(stderr, "%s:%u: variable name expected\n",
266 UPDATEDB_CONF, uc_line);
269 if (*had_var != false) {
270 fprintf(stderr, "%s:%u: variable `%s' was already defined\n",
271 UPDATEDB_CONF, uc_line, uc_lex_buf.c_str());
277 if (token != UCT_EQUAL) {
278 fprintf(stderr, "%s:%u: `=' expected after variable name\n",
279 UPDATEDB_CONF, uc_line);
283 if (token != UCT_QUOTED) {
284 fprintf(stderr, "%s:%u: value in quotes expected after `='\n",
285 UPDATEDB_CONF, uc_line);
288 if (var_token == UCT_PRUNE_BIND_MOUNTS) {
289 if (parse_bool(&conf_prune_bind_mounts, uc_lex_buf.c_str()) != 0) {
290 fprintf(stderr, "%s:%u: invalid value `%s' of PRUNE_BIND_MOUNTS\n",
291 UPDATEDB_CONF, uc_line, uc_lex_buf.c_str());
294 } else if (var_token == UCT_PRUNEFS)
295 var_add_values(&conf_prunefs, uc_lex_buf.c_str());
296 else if (var_token == UCT_PRUNENAMES)
297 var_add_values(&conf_prunenames, uc_lex_buf.c_str());
298 else if (var_token == UCT_PRUNEPATHS)
299 var_add_values(&conf_prunepaths, uc_lex_buf.c_str());
303 if (token != UCT_EOL && token != UCT_EOF) {
304 fprintf(stderr, "%s:%u: unexpected data after variable value\n",
305 UPDATEDB_CONF, uc_line);
309 while (token != UCT_EOL) {
310 if (token == UCT_EOF)
316 if (ferror(uc_file)) {
317 perror(UPDATEDB_CONF);
320 funlockfile(uc_file);
324 /* Command-line argument parsing */
326 /* Output --help text */
330 printf(_("Usage: updatedb [OPTION]...\n"
331 "Update a plocate database.\n"
333 " -f, --add-prunefs FS omit also FS (space-separated)\n"
334 " -n, --add-prunenames NAMES omit also NAMES (space-separated)\n"
335 " -e, --add-prunepaths PATHS omit also PATHS (space-separated)\n"
336 " --add-single-prunepath PATH omit also PATH\n"
337 " -U, --database-root PATH the subtree to store in "
338 "database (default \"/\")\n"
339 " -h, --help print this help\n"
340 " -o, --output FILE database to update (default\n"
342 " -b, --block-size SIZE number of filenames to store\n"
343 " in each block (default 32)\n"
344 " --prune-bind-mounts FLAG omit bind mounts (default "
346 " --prunefs FS filesystems to omit from "
348 " --prunenames NAMES directory names to omit from "
350 " --prunepaths PATHS paths to omit from database\n"
351 " -l, --require-visibility FLAG check visibility before "
353 " (default \"yes\")\n"
354 " -v, --verbose print paths of files as they "
356 " -V, --version print version information\n"
358 "The configuration defaults to values read from\n"
360 DBFILE, UPDATEDB_CONF);
362 "Report bugs to %s.\n"),
366 /* Prepend current working directory to PATH;
367 return resulting path */
369 prepend_cwd(const string &path)
373 buf.resize(BUFSIZ); /* Not PATH_MAX because it is not defined on some platforms. */
375 buf.resize(buf.size() * 1.5);
376 while ((res = getcwd(buf.data(), buf.size())) == NULL && errno == ERANGE);
378 fprintf(stderr, "%s: %s: can not get current working directory\n",
379 program_invocation_name, strerror(errno));
382 buf.resize(strlen(buf.data()));
383 return buf + '/' + path;
386 /* Parse ARGC, ARGV. Exit on error or --help, --version. */
388 parse_arguments(int argc, char *argv[])
390 enum { OPT_DEBUG_PRUNING = CHAR_MAX + 1,
391 OPT_ADD_SINGLE_PRUNEPATH = CHAR_MAX + 2 };
393 static const struct option options[] = {
394 { "add-prunefs", required_argument, NULL, 'f' },
395 { "add-prunenames", required_argument, NULL, 'n' },
396 { "add-prunepaths", required_argument, NULL, 'e' },
397 { "add-single-prunepath", required_argument, NULL, OPT_ADD_SINGLE_PRUNEPATH },
398 { "database-root", required_argument, NULL, 'U' },
399 { "debug-pruning", no_argument, NULL, OPT_DEBUG_PRUNING },
400 { "help", no_argument, NULL, 'h' },
401 { "output", required_argument, NULL, 'o' },
402 { "prune-bind-mounts", required_argument, NULL, 'B' },
403 { "prunefs", required_argument, NULL, 'F' },
404 { "prunenames", required_argument, NULL, 'N' },
405 { "prunepaths", required_argument, NULL, 'P' },
406 { "require-visibility", required_argument, NULL, 'l' },
407 { "verbose", no_argument, NULL, 'v' },
408 { "version", no_argument, NULL, 'V' },
409 { "block-size", required_argument, 0, 'b' },
410 { "debug", no_argument, 0, 'D' }, // Not documented.
414 bool prunefs_changed, prunenames_changed, prunepaths_changed;
415 bool got_prune_bind_mounts, got_visibility;
417 prunefs_changed = false;
418 prunenames_changed = false;
419 prunepaths_changed = false;
420 got_prune_bind_mounts = false;
421 got_visibility = false;
425 opt = getopt_long(argc, argv, "U:Ve:f:hl:n:o:vb:D", options, &idx);
434 if (got_prune_bind_mounts != false) {
435 fprintf(stderr, "%s: --%s would override earlier command-line argument\n",
436 program_invocation_name, "prune-bind-mounts");
439 got_prune_bind_mounts = true;
440 if (parse_bool(&conf_prune_bind_mounts, optarg) != 0) {
441 fprintf(stderr, "%s: invalid value %s of --%s\n",
442 program_invocation_name, optarg, "prune-bind-mounts");
448 if (prunefs_changed != false) {
449 fprintf(stderr, "%s: --%s would override earlier command-line argument\n",
450 program_invocation_name, "prunefs");
453 prunefs_changed = true;
454 conf_prunefs.clear();
455 var_add_values(&conf_prunefs, optarg);
459 if (prunenames_changed != false) {
460 fprintf(stderr, "%s: --%s would override earlier command-line argument\n",
461 program_invocation_name, "prunenames");
464 prunenames_changed = true;
465 conf_prunenames.clear();
466 var_add_values(&conf_prunenames, optarg);
470 if (prunepaths_changed != false) {
471 fprintf(stderr, "%s: --%s would override earlier command-line argument\n",
472 program_invocation_name, "prunepaths");
475 prunepaths_changed = true;
476 conf_prunepaths.clear();
477 var_add_values(&conf_prunepaths, optarg);
481 if (conf_scan_root != NULL) {
482 fprintf(stderr, "%s: --%s specified twice\n",
483 program_invocation_name, "database-root");
486 conf_scan_root = realpath(optarg, nullptr);
487 if (conf_scan_root == NULL) {
488 fprintf(stderr, "%s: %s: invalid value `%s' of --%s\n",
489 program_invocation_name, strerror(errno), optarg, "database-root");
495 puts("updatedb (" PACKAGE_NAME ") " PACKAGE_VERSION);
496 puts(_("Copyright (C) 2007 Red Hat, Inc. All rights reserved.\n"
497 "This software is distributed under the GPL v.2.\n"
499 "This program is provided with NO WARRANTY, to the extent "
500 "permitted by law."));
504 prunepaths_changed = true;
505 var_add_values(&conf_prunepaths, optarg);
508 case OPT_ADD_SINGLE_PRUNEPATH:
509 prunepaths_changed = true;
510 conf_prunepaths.push_back(optarg);
514 prunefs_changed = true;
515 var_add_values(&conf_prunefs, optarg);
523 if (got_visibility != false) {
524 fprintf(stderr, "%s: --%s specified twice\n",
525 program_invocation_name, "require-visibility");
529 got_visibility = true;
530 if (parse_bool(&conf_check_visibility, optarg) != 0) {
531 fprintf(stderr, "%s: invalid value `%s' of --%s",
532 program_invocation_name, optarg, "require-visibility");
538 prunenames_changed = true;
539 var_add_values(&conf_prunenames, optarg);
543 if (!conf_output.empty()) {
544 fprintf(stderr, "%s: --%s specified twice",
545 program_invocation_name, "output");
548 conf_output = optarg;
556 conf_block_size = atoi(optarg);
563 case OPT_DEBUG_PRUNING:
564 conf_debug_pruning = true;
572 if (optind != argc) {
573 fprintf(stderr, "%s: unexpected operand on command line",
574 program_invocation_name);
577 if (conf_scan_root == NULL) {
578 static char root[] = "/";
580 conf_scan_root = root;
582 if (conf_output.empty())
583 conf_output = DBFILE;
584 if (conf_output[0] != '/')
585 conf_output = prepend_cwd(conf_output);
588 /* Conversion of configuration for main code */
590 /* Store a string list to OBSTACK */
592 gen_conf_block_string_list(string *obstack,
593 const vector<string> *strings)
595 for (const string &str : *strings) {
602 /* Generate conf_block */
608 #define CONST(S) conf_block.append(S, sizeof(S))
609 /* conf_check_visibility value is stored in the header */
610 CONST("prune_bind_mounts");
611 /* Add two NUL bytes after the value */
612 conf_block.append(conf_prune_bind_mounts != false ? "1\0" : "0\0", 3);
614 gen_conf_block_string_list(&conf_block, &conf_prunefs);
616 gen_conf_block_string_list(&conf_block, &conf_prunenames);
618 gen_conf_block_string_list(&conf_block, &conf_prunepaths);
619 /* scan_root is contained directly in the header */
620 /* conf_output, conf_verbose are not relevant */
624 /* Parse /etc/updatedb.conf and command-line arguments ARGC, ARGV.
625 Exit on error or --help, --version. */
626 void conf_prepare(int argc, char *argv[])
628 parse_updatedb_conf();
629 parse_arguments(argc, argv);
630 for (string &str : conf_prunefs) {
631 /* Assuming filesystem names are ASCII-only */
635 /* Finish the variable only after converting filesystem names to upper case
636 to avoid keeping duplicates that originally differed in case and to sort
638 var_finish(&conf_prunefs);
639 var_finish(&conf_prunenames);
640 var_finish(&conf_prunepaths);
642 string_list_dir_path_sort(&conf_prunepaths);
644 if (conf_debug_pruning) {
645 /* This is debuging output, don't mark anything for translation */
646 fprintf(stderr, "conf_block:\n");
647 for (char c : conf_block) {
648 if (isascii((unsigned char)c) && isprint((unsigned char)c) && c != '\\')
651 fprintf(stderr, "\\%03o", (unsigned)(unsigned char)c);
656 fprintf(stderr, "\n-----------------------\n");