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 int conf_block_size = 32;
72 bool use_debug = false;
74 /* Parse a STR, store the parsed boolean value to DEST;
75 return 0 if OK, -1 on error. */
77 parse_bool(bool *dest, const char *str)
79 if (strcmp(str, "0") == 0 || strcmp(str, "no") == 0) {
83 if (strcmp(str, "1") == 0 || strcmp(str, "yes") == 0) {
90 /* String list handling */
92 /* Add values from space-separated VAL to VAR and LIST */
94 var_add_values(vector<string> *list, const char *val)
99 while (isspace((unsigned char)*val))
106 while (*val != 0 && !isspace((unsigned char)*val));
107 list->emplace_back(start, val - start);
111 /* Finish variable LIST, sort its contents, remove duplicates */
113 var_finish(vector<string> *list)
115 sort(list->begin(), list->end());
116 auto new_end = unique(list->begin(), list->end());
117 list->erase(new_end, list->end());
120 /* UPDATEDB_CONF parsing */
122 /* UPDATEDB_CONF (locked) */
123 static FILE *uc_file;
124 /* Line number at token start; type matches error_at_line () */
125 static unsigned uc_line;
126 /* Current line number; type matches error_at_line () */
127 static unsigned uc_current_line;
128 /* Last string returned by uc_lex */
129 static string uc_lex_buf;
139 UCT_PRUNE_BIND_MOUNTS,
145 /* Return next token from uc_file; for UCT_IDENTIFIER, UCT_QUOTED or keywords,
146 store the data to uc_lex_buf (valid until next call). */
153 uc_line = uc_current_line;
155 c = getc_unlocked(uc_file);
158 } while (c != '\n' && isspace((unsigned char)c));
162 c = getc_unlocked(uc_file);
175 while ((c = getc_unlocked(uc_file)) != '"') {
176 if (c == EOF || c == '\n') {
177 fprintf(stderr, "%s:%u: missing closing `\"'\n",
178 UPDATEDB_CONF, uc_line);
181 uc_lex_buf.push_back(c);
187 if (!isalpha((unsigned char)c) && c != '_')
190 uc_lex_buf.push_back(c);
191 c = getc_unlocked(uc_file);
192 } while (c != EOF && (isalnum((unsigned char)c) || c == '_'));
194 if (uc_lex_buf == "PRUNE_BIND_MOUNTS")
195 return UCT_PRUNE_BIND_MOUNTS;
196 if (uc_lex_buf == "PRUNEFS")
198 if (uc_lex_buf == "PRUNENAMES")
199 return UCT_PRUNENAMES;
200 if (uc_lex_buf == "PRUNEPATHS")
201 return UCT_PRUNEPATHS;
202 return UCT_IDENTIFIER;
207 /* Parse /etc/updatedb.conf. Exit on I/O or syntax error. */
209 parse_updatedb_conf(void)
211 bool had_prune_bind_mounts, had_prunefs, had_prunenames, had_prunepaths;
213 uc_file = fopen(UPDATEDB_CONF, "r");
214 if (uc_file == NULL) {
215 if (errno != ENOENT) {
216 perror(UPDATEDB_CONF);
223 had_prune_bind_mounts = false;
225 had_prunenames = false;
226 had_prunepaths = false;
229 int var_token, token;
239 case UCT_PRUNE_BIND_MOUNTS:
240 had_var = &had_prune_bind_mounts;
244 had_var = &had_prunefs;
248 had_var = &had_prunenames;
252 had_var = &had_prunepaths;
256 fprintf(stderr, "%s:%u: unknown variable: `%s'\n",
257 UPDATEDB_CONF, uc_line, uc_lex_buf.c_str());
261 fprintf(stderr, "%s:%u: variable name expected\n",
262 UPDATEDB_CONF, uc_line);
265 if (*had_var != false) {
266 fprintf(stderr, "%s:%u: variable `%s' was already defined\n",
267 UPDATEDB_CONF, uc_line, uc_lex_buf.c_str());
273 if (token != UCT_EQUAL) {
274 fprintf(stderr, "%s:%u: `=' expected after variable name\n",
275 UPDATEDB_CONF, uc_line);
279 if (token != UCT_QUOTED) {
280 fprintf(stderr, "%s:%u: value in quotes expected after `='\n",
281 UPDATEDB_CONF, uc_line);
284 if (var_token == UCT_PRUNE_BIND_MOUNTS) {
285 if (parse_bool(&conf_prune_bind_mounts, uc_lex_buf.c_str()) != 0) {
286 fprintf(stderr, "%s:%u: invalid value `%s' of PRUNE_BIND_MOUNTS\n",
287 UPDATEDB_CONF, uc_line, uc_lex_buf.c_str());
290 } else if (var_token == UCT_PRUNEFS)
291 var_add_values(&conf_prunefs, uc_lex_buf.c_str());
292 else if (var_token == UCT_PRUNENAMES)
293 var_add_values(&conf_prunenames, uc_lex_buf.c_str());
294 else if (var_token == UCT_PRUNEPATHS)
295 var_add_values(&conf_prunepaths, uc_lex_buf.c_str());
299 if (token != UCT_EOL && token != UCT_EOF) {
300 fprintf(stderr, "%s:%u: unexpected data after variable value\n",
301 UPDATEDB_CONF, uc_line);
305 while (token != UCT_EOL) {
306 if (token == UCT_EOF)
312 if (ferror(uc_file)) {
313 perror(UPDATEDB_CONF);
316 funlockfile(uc_file);
320 /* Command-line argument parsing */
322 /* Output --help text */
326 printf(_("Usage: updatedb [OPTION]...\n"
327 "Update a plocate database.\n"
329 " -f, --add-prunefs FS omit also FS (space-separated)\n"
330 " -n, --add-prunenames NAMES omit also NAMES (space-separated)\n"
331 " -e, --add-prunepaths PATHS omit also PATHS (space-separated)\n"
332 " --add-single-prunepath PATH omit also PATH\n"
333 " -U, --database-root PATH the subtree to store in "
334 "database (default \"/\")\n"
335 " -h, --help print this help\n"
336 " -o, --output FILE database to update (default\n"
338 " -b, --block-size SIZE number of filenames to store\n"
339 " in each block (default 32)\n"
340 " --prune-bind-mounts FLAG omit bind mounts (default "
342 " --prunefs FS filesystems to omit from "
344 " --prunenames NAMES directory names to omit from "
346 " --prunepaths PATHS paths to omit from database\n"
347 " -l, --require-visibility FLAG check visibility before "
349 " (default \"yes\")\n"
350 " -v, --verbose print paths of files as they "
352 " -V, --version print version information\n"
354 "The configuration defaults to values read from\n"
356 DBFILE, UPDATEDB_CONF);
358 "Report bugs to %s.\n"),
362 /* Prepend current working directory to PATH;
363 return resulting path */
365 prepend_cwd(const string &path)
369 buf.resize(BUFSIZ); /* Not PATH_MAX because it is not defined on some platforms. */
371 buf.resize(buf.size() * 1.5);
372 while ((res = getcwd(buf.data(), buf.size())) == NULL && errno == ERANGE);
374 fprintf(stderr, "%s: %s: can not get current working directory\n",
375 program_invocation_name, strerror(errno));
378 buf.resize(strlen(buf.data()));
379 return buf + '/' + path;
382 /* Parse ARGC, ARGV. Exit on error or --help, --version. */
384 parse_arguments(int argc, char *argv[])
386 enum { OPT_DEBUG_PRUNING = CHAR_MAX + 1,
387 OPT_ADD_SINGLE_PRUNEPATH = CHAR_MAX + 2 };
389 static const struct option options[] = {
390 { "add-prunefs", required_argument, NULL, 'f' },
391 { "add-prunenames", required_argument, NULL, 'n' },
392 { "add-prunepaths", required_argument, NULL, 'e' },
393 { "add-single-prunepath", required_argument, NULL, OPT_ADD_SINGLE_PRUNEPATH },
394 { "database-root", required_argument, NULL, 'U' },
395 { "debug-pruning", no_argument, NULL, OPT_DEBUG_PRUNING },
396 { "help", no_argument, NULL, 'h' },
397 { "output", required_argument, NULL, 'o' },
398 { "prune-bind-mounts", required_argument, NULL, 'B' },
399 { "prunefs", required_argument, NULL, 'F' },
400 { "prunenames", required_argument, NULL, 'N' },
401 { "prunepaths", required_argument, NULL, 'P' },
402 { "require-visibility", required_argument, NULL, 'l' },
403 { "verbose", no_argument, NULL, 'v' },
404 { "version", no_argument, NULL, 'V' },
405 { "block-size", required_argument, 0, 'b' },
406 { "debug", no_argument, 0, 'D' }, // Not documented.
410 bool prunefs_changed, prunenames_changed, prunepaths_changed;
411 bool got_prune_bind_mounts, got_visibility;
413 prunefs_changed = false;
414 prunenames_changed = false;
415 prunepaths_changed = false;
416 got_prune_bind_mounts = false;
417 got_visibility = false;
421 opt = getopt_long(argc, argv, "U:Ve:f:hl:n:o:vb:D", options, &idx);
430 if (got_prune_bind_mounts != false) {
431 fprintf(stderr, "%s: --%s would override earlier command-line argument\n",
432 program_invocation_name, "prune-bind-mounts");
435 got_prune_bind_mounts = true;
436 if (parse_bool(&conf_prune_bind_mounts, optarg) != 0) {
437 fprintf(stderr, "%s: invalid value %s of --%s\n",
438 program_invocation_name, optarg, "prune-bind-mounts");
444 if (prunefs_changed != false) {
445 fprintf(stderr, "%s: --%s would override earlier command-line argument\n",
446 program_invocation_name, "prunefs");
449 prunefs_changed = true;
450 conf_prunefs.clear();
451 var_add_values(&conf_prunefs, optarg);
455 if (prunenames_changed != false) {
456 fprintf(stderr, "%s: --%s would override earlier command-line argument\n",
457 program_invocation_name, "prunenames");
460 prunenames_changed = true;
461 conf_prunenames.clear();
462 var_add_values(&conf_prunenames, optarg);
466 if (prunepaths_changed != false) {
467 fprintf(stderr, "%s: --%s would override earlier command-line argument\n",
468 program_invocation_name, "prunepaths");
471 prunepaths_changed = true;
472 conf_prunepaths.clear();
473 var_add_values(&conf_prunepaths, optarg);
477 if (conf_scan_root != NULL) {
478 fprintf(stderr, "%s: --%s specified twice\n",
479 program_invocation_name, "database-root");
482 conf_scan_root = realpath(optarg, nullptr);
483 if (conf_scan_root == NULL) {
484 fprintf(stderr, "%s: %s: invalid value `%s' of --%s\n",
485 program_invocation_name, strerror(errno), optarg, "database-root");
491 puts("updatedb (" PACKAGE_NAME ") " PACKAGE_VERSION);
492 puts(_("Copyright (C) 2007 Red Hat, Inc. All rights reserved.\n"
493 "This software is distributed under the GPL v.2.\n"
495 "This program is provided with NO WARRANTY, to the extent "
496 "permitted by law."));
500 prunepaths_changed = true;
501 var_add_values(&conf_prunepaths, optarg);
504 case OPT_ADD_SINGLE_PRUNEPATH:
505 prunepaths_changed = true;
506 conf_prunepaths.push_back(optarg);
510 prunefs_changed = true;
511 var_add_values(&conf_prunefs, optarg);
519 if (got_visibility != false) {
520 fprintf(stderr, "%s: --%s specified twice\n",
521 program_invocation_name, "require-visibility");
525 got_visibility = true;
526 if (parse_bool(&conf_check_visibility, optarg) != 0) {
527 fprintf(stderr, "%s: invalid value `%s' of --%s",
528 program_invocation_name, optarg, "require-visibility");
534 prunenames_changed = true;
535 var_add_values(&conf_prunenames, optarg);
539 if (!conf_output.empty()) {
540 fprintf(stderr, "%s: --%s specified twice",
541 program_invocation_name, "output");
544 conf_output = optarg;
552 conf_block_size = atoi(optarg);
559 case OPT_DEBUG_PRUNING:
560 conf_debug_pruning = true;
568 if (optind != argc) {
569 fprintf(stderr, "%s: unexpected operand on command line",
570 program_invocation_name);
573 if (conf_scan_root == NULL) {
574 static char root[] = "/";
576 conf_scan_root = root;
578 if (conf_output.empty())
579 conf_output = DBFILE;
580 if (conf_output[0] != '/')
581 conf_output = prepend_cwd(conf_output);
584 /* Conversion of configuration for main code */
586 /* Store a string list to OBSTACK */
588 gen_conf_block_string_list(string *obstack,
589 const vector<string> *strings)
591 for (const string &str : *strings) {
598 /* Generate conf_block */
604 #define CONST(S) conf_block.append(S, sizeof(S))
605 /* conf_check_visibility value is stored in the header */
606 CONST("prune_bind_mounts");
607 /* Add two NUL bytes after the value */
608 conf_block.append(conf_prune_bind_mounts != false ? "1\0" : "0\0", 3);
610 gen_conf_block_string_list(&conf_block, &conf_prunefs);
612 gen_conf_block_string_list(&conf_block, &conf_prunenames);
614 gen_conf_block_string_list(&conf_block, &conf_prunepaths);
615 /* scan_root is contained directly in the header */
616 /* conf_output, conf_verbose are not relevant */
620 /* Parse /etc/updatedb.conf and command-line arguments ARGC, ARGV.
621 Exit on error or --help, --version. */
622 void conf_prepare(int argc, char *argv[])
624 parse_updatedb_conf();
625 parse_arguments(argc, argv);
626 for (string &str : conf_prunefs) {
627 /* Assuming filesystem names are ASCII-only */
631 /* Finish the variable only after converting filesystem names to upper case
632 to avoid keeping duplicates that originally differed in case and to sort
634 var_finish(&conf_prunefs);
635 var_finish(&conf_prunenames);
636 var_finish(&conf_prunepaths);
638 string_list_dir_path_sort(&conf_prunepaths);
640 if (conf_debug_pruning) {
641 /* This is debuging output, don't mark anything for translation */
642 fprintf(stderr, "conf_block:\n");
643 for (char c : conf_block) {
644 if (isascii((unsigned char)c) && isprint((unsigned char)c) && c != '\\')
647 fprintf(stderr, "\\%03o", (unsigned)(unsigned char)c);
652 fprintf(stderr, "\n-----------------------\n");