From 3f226f42bfc7c11c5d1230d0a72201b684499bd5 Mon Sep 17 00:00:00 2001 From: Leslie P. Polzer Date: Mon, 24 Mar 2008 00:50:52 +0000 Subject: [PATCH] Merge Leslie Polzer's SOC 2007 changes implementing updatedb in C. To: address@hidden 2008-03-24 Leslie Polzer Merge Leslie Polzer's updatedb enhancements to xargs which were produced as part of the Google Summer of Code 2007. * locate/updatedb.c: New file, implementing updatedb. * po/POTFILES.in: Add updatedb.c. * locate/updatedb.sh: No longer used. * locate/Makefile.am (updatedb_SOURCES): updatedb is now a C program, not a shell script. (_FIND): New macro; the name of the installed find binary (_FRCODE): Likewise for frcode. (_BIGRAM): Likewise for bigram. (_CODE): Likewise for code. (INCLUDES): Add extra includes for building updatedb (FIND, FRCODE, etc.) (updatedb): Now built by automake-generated rules, not by processing updatedb.sh. * locate/pipeline.c: New file, implementing run_pipeline, a function which launches a set of processes connected via pipes. * locate/locatedb.h: Include , . (run_pipeline): Declare this function. * import-gnulib.config (modules): Add euidaccess, tempname, strsep. --- import-gnulib.config | 4 + locate/Makefile.am | 65 ++-- locate/locatedb.h | 5 + locate/pipeline.c | 443 +++++++++++++++++++++ locate/updatedb.c | 1076 ++++++++++++++++++++++++++++++++++++++++++++++++++ locate/updatedb.sh | 183 +-------- po/POTFILES.in | 1 + 7 files changed, 1588 insertions(+), 189 deletions(-) create mode 100644 locate/pipeline.c create mode 100644 locate/updatedb.c diff --git a/import-gnulib.config b/import-gnulib.config index 287c2d8..1089692 100644 --- a/import-gnulib.config +++ b/import-gnulib.config @@ -36,6 +36,7 @@ closein closeout dirname error +euidaccess fchdir fcntl fdl @@ -74,11 +75,13 @@ stpcpy strcasestr strdup strftime +strsep strtol strtoul strtoull strtoumax sys_stat +tempname timespec verify version-etc @@ -92,3 +95,4 @@ xstrtol xstrtoumax yesno ' + diff --git a/locate/Makefile.am b/locate/Makefile.am index fd9f1f5..b95505b 100644 --- a/locate/Makefile.am +++ b/locate/Makefile.am @@ -7,42 +7,59 @@ AM_INSTALLCHECK_STD_OPTIONS_EXEMPT = \ frcode$(EXEEXT) \ code$(EXEEXT) \ bigram$(EXEEXT) -bin_PROGRAMS = locate +bin_PROGRAMS = locate updatedb libexec_PROGRAMS = frcode code bigram -bin_SCRIPTS = updatedb +#bin_SCRIPTS = updatedb man_MANS = locate.1 updatedb.1 locatedb.5 BUILT_SOURCES = dblocation.texi -EXTRA_DIST = locatedb.h updatedb.sh $(man_MANS) +EXTRA_DIST = locatedb.h $(man_MANS) CLEANFILES = updatedb dblocation.texi locate_SOURCES = locate.c word_io.c code_SOURCES = code.c word_io.c +updatedb_SOURCES = updatedb.c pipeline.c + +_FIND=`echo find | sed '$(transform)'` +_FRCODE=`echo frcode | sed '$(transform)'` +_BIGRAM=`echo bigram | sed '$(transform)'` +_CODE=`echo code | sed '$(transform)'` + +INCLUDES = -I$(top_srcdir)/lib -I../gnulib/lib -I$(top_srcdir)/gnulib/lib -I../intl \ + -DLOCATE_DB=\"$(LOCATE_DB)\" \ + -DLOCALEDIR=\"$(localedir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DBINDIR=\"$(bindir)\" \ + -DVERSION=\"@address@hidden" \ + -DSORT_SUPPORTS_Z=\"$(SORT_SUPPORTS_Z)\" \ + -DFIND=\"$(_FIND)\" \ + -DFRCODE=\"$(_FRCODE)\" \ + -DBIGRAM=\"$(_BIGRAM)\" \ + -DCODE=\"$(_CODE)\" -INCLUDES = -I$(top_srcdir)/lib -I../gnulib/lib -I$(top_srcdir)/gnulib/lib -I../intl -DLOCATE_DB=\"$(LOCATE_DB)\" -DLOCALEDIR=\"$(localedir)\" LDADD = ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ $(PROGRAMS) $(LIBPROGRAMS): ../lib/libfind.a ../gnulib/lib/libgnulib.a -updatedb: updatedb.sh - rm -f $@ - find=`echo find|sed '$(transform)'`; \ - frcode=`echo frcode|sed '$(transform)'`; \ - bigram=`echo bigram|sed '$(transform)'`; \ - code=`echo code|sed '$(transform)'`; \ - sed \ - -e "s,@""bindir""@,$(bindir)," \ - -e "s,@""libexecdir""@,$(libexecdir)," \ - -e "s,@""LOCATE_DB""@,$(LOCATE_DB)," \ - -e "s,@""VERSION""@,@VERSION@," \ - -e "s,@""PACKAGE_NAME""@,@PACKAGE_NAME@," \ - -e "s,@""find""@,$${find}," \ - -e "s,@""frcode""@,$${frcode}," \ - -e "s,@""bigram""@,$${bigram}," \ - -e "s,@""code""@,$${code}," \ - -e "s,@""SORT""@,$(SORT)," \ - -e "s,@""SORT_SUPPORTS_Z""@,$(SORT_SUPPORTS_Z)," \ - $(srcdir)/updatedb.sh > $@ - chmod +x $@ +#updatedb: updatedb.sh +# rm -f $@ +# find=`echo find|sed '$(transform)'`; \ +# frcode=`echo frcode|sed '$(transform)'`; \ +# bigram=`echo bigram|sed '$(transform)'`; \ +# code=`echo code|sed '$(transform)'`; \ +# sed \ +# -e "s,@""bindir""@,$(bindir)," \ +# -e "s,@""libexecdir""@,$(libexecdir)," \ +# -e "s,@""LOCATE_DB""@,$(LOCATE_DB)," \ +# -e "s,@""VERSION""@,@VERSION@," \ +# -e "s,@""PACKAGE_NAME""@,@PACKAGE_NAME@," \ +# -e "s,@""find""@,$${find}," \ +# -e "s,@""frcode""@,$${frcode}," \ +# -e "s,@""bigram""@,$${bigram}," \ +# -e "s,@""code""@,$${code}," \ +# -e "s,@""SORT""@,$(SORT)," \ +# -e "s,@""SORT_SUPPORTS_Z""@,$(SORT_SUPPORTS_Z)," \ +# $(srcdir)/updatedb.sh > $@ +# chmod +x $@ install-data-hook: $(top_srcdir)/build-aux/mkinstalldirs $(DESTDIR)$(localstatedir) diff --git a/locate/locatedb.h b/locate/locatedb.h index a4a7289..cdc8669 100644 --- a/locate/locatedb.h +++ b/locate/locatedb.h @@ -18,6 +18,9 @@ #ifndef _LOCATEDB_H #define _LOCATEDB_H 1 +#include +#include + /* The magic string at the start of a locate database, to make sure it's in the right format. The 02 is the database format version number. This string has the same format as a database entry, but you can't @@ -74,6 +77,8 @@ int getword (FILE *fp, const char *filename, bool putword (FILE *fp, int word, GetwordEndianState endian_state_flag); +int run_pipeline (char * const **args, int do_setuid, uid_t uid, + int *exit_status, int inputfd, int outputfd); #define SLOCATE_DB_MAGIC_LEN 2 diff --git a/locate/pipeline.c b/locate/pipeline.c new file mode 100644 index 0000000..eff1d57 --- /dev/null +++ b/locate/pipeline.c @@ -0,0 +1,443 @@ +/* + pipeline.c -- run a command pipeline + + Copyright (C) 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. + + Written by James Youngman. +*/ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* SIZE_MAX */ + +#include "error.h" +#include "locatedb.h" + + + +struct command +{ + int is_running; + pid_t pid; + int exit_status; + const char **args; +}; + + +static int +mark_close_on_exec (int fd, int silent_errors) +{ + long flags; + + flags = fcntl (fd, F_GETFD); + flags |= FD_CLOEXEC; + if (fcntl (fd, F_SETFD, flags) < 0) + { + if (!silent_errors) + error (0, errno, "fcntl"); + return 0; /* failed. */ + } + return 1; +} + + +static void +xclose (int *fd) +{ + assert (0 == close (*fd)); + *fd = -1; +} + +/* Invoke a command. Return the PID of the command we started, + * or zero if we were unable to run the command. + * + * Many of the parameters are obvious and are described in an inline + * comment. However, the following are more complex: + * + * If fork() fails, we call the function FAILURE_CALLBACK, passing it + * the argument OPAQUE_CONTEXT. The caller can use this feature to + * limit retries, arrange for pausing between retries, and so on. + * If FAILURE_CALLBACK is NULL, no retries will be attempted. + * + * If fork() succeeded, the PID of the child is returned. If the + * exec succeeded, *CHILD_ERRNO_VALUE is set to 0. Otherwise, that + * location is set to the errno value which explains why exec failed. + */ +static pid_t +start_command (int silent_errors, /* turns off error messages */ + int use_path, /* search $PATH for the command */ + char * const * args, /* arguments (including name of binary) */ + int do_setuid, /* whether to change the uid */ + uid_t uid, /* uid to change to */ + int stdin_fd, /* standard input */ + int stdout_fd, /* standard output */ + int *child_errno_value, /* See above */ + int (*failure_callback)(void*), /* See above */ + void *opaque_context) /* See above */ +{ + int saved_errno; + int pipefd[2]; + int (*fn_exec)(const char *, char *const[]); + + assert (args); + assert (args[0]); + + if (use_path) + fn_exec = execvp; + else + fn_exec = execv; + + /* Create a pipe for transmitting the value of errno back from + * a child which has failed to exec() into the parent. Both the + * parent and child are the same binary at the point this happens, + * so there is no need to worry about data representation issues. + */ + if (0 != pipe (pipefd)) + { + if (!silent_errors) + error (0, errno, "pipe"); + return 0; /* failed. */ + } + /* Set the write end of the pipe to close-on-exec */ + mark_close_on_exec (pipefd[1], silent_errors); + + for (;;) + { + pid_t child_pid; + + child_pid = fork (); + if (0 == child_pid) + { + /* We are the child. */ + xclose (&pipefd[0]); /* close read end of pipe */ + + /* Do the redirections. Close the old file descriptors first, + * in case we are very close to a file descriptor resource limit. + */ + close (STDIN_FILENO); + if (dup (stdin_fd) >= 0) + { + xclose (&stdin_fd); + close (STDOUT_FILENO); + if (dup (stdout_fd) >= 0) + { + xclose (&stdout_fd); + + /* Run the indicated command. */ + if (do_setuid) + { + //fprintf(stderr, "%s: setuid to %d requested.\n", args[0], uid); + if (setuid (uid) == -1) + { + //fprintf(stderr, "%s: but failed.\n", args[0]); + error (0, errno, "failed to set UID to %d", uid); + } + else + { + //fprintf(stderr, "%s: and was successful; about to exec with uid %d.\n", args[0], getuid()); + execvp (args[0], args); + } + } + execvp (args[0], args); + saved_errno = errno; + } + else + { + saved_errno = errno; + } + } + else + { + saved_errno = errno; + } + + /* If we get to here, the exec failed. Tell the parent why + * by sending them our errno value. + */ + write (pipefd[1], &saved_errno, sizeof (saved_errno)); + _exit (1); + } + else if (child_pid > 0) + { + /* We are the parent. */ + int nbytes; + + /* Close the copies of the child's file descriptors that the + * parent has. Child 1 will only be able to exit when its input + * pipe has no remaining writers, so we have to ensure that the + * parent doesn't hold the write end open. + */ + + close (pipefd[1]); /* close write end of pipe */ + nbytes = read (pipefd[0], &saved_errno, sizeof (saved_errno)); + if (nbytes == sizeof (saved_errno)) + { + /* The exec failed. We know why, too. */ + if (child_errno_value) + *child_errno_value = saved_errno; + if (!silent_errors) + error (0, saved_errno, args[0]); + return child_pid; + } + else + { + /* We could not read data from the pipe. That means that it is + * closed. We marked the write end of the pipe as FD_CLOEXEC, + * so the exec must have succeeded. + */ + *child_errno_value = 0; + return child_pid; + } + } + else + { + /* fork() failed; we are the parent, there is no child. */ + saved_errno = errno; + + if (failure_callback) + { + /* Call the failure callback. It decides if we should retry. + * Its context pointer allows it to figure out how many times + * it retried so far (if it needs to know). + */ + if (!failure_callback (opaque_context)) + { + /* Caller does not want us to retry. */ + errno = saved_errno; + return 0; + } + } + else + { + /* No failure callback. Hence no retry. */ + if (!silent_errors) + error (0, saved_errno, "fork"); + errno = saved_errno; + return 0; + } + /* If we get to here, the failure callback indicated we should retry. */ + } + /* We get to here if we are going to retry. */ + } +} + + +/* Retry strategy function that always declines to perform a retry. */ +static int +alwaysfail (void* context) +{ + return 0; +} + +/* Returns nonzero if the indicated status value denotes a failure */ +static int +childfailed (int status) +{ + return !WIFEXITED(status) || WEXITSTATUS(status); +} + +/* Count the length of an argument list */ +static unsigned int +countargs (char * const *v[]) +{ + unsigned int n = 0; + while (*v) + { + v++; + n++; + } + return n; +} + +/* Send a (normally) fatal signal to a list of (child) processes */ +static void +stopkids (const pid_t * pids, unsigned int n) +{ + int i; + + for (i=0; i= N. + * The exit status for each child is placed into exit_status[]. + * + * If DO_SETUID is true, attempt to change the uid to UID for this command. + * + * INPUT and OUTPUT are the file descriptors for the stdandard input of the first + * command in the pipeline and the standard output of the last, respectively. + * + * Note: We use the clumsy DO_SETUID to avoid messing around with setuid() when + * there's no need to. + */ +int +run_pipeline (char * const **args, int do_setuid, uid_t uid, + int *exit_status, int input, int output) +{ + int debugflag = 0; + unsigned int i, j; + int n; + pid_t *child_pids; + int result = 1; + int *input_fds, *output_fds; + size_t sizmax = (size_t)~0uL; + + n = countargs (args); + assert (args); + assert (exit_status); + assert (input >= 0); + assert (output >= 0); + + assert (n > 0); + assert ((SIZE_MAX / sizeof(pid_t)) > n); + assert ((SIZE_MAX / (2*sizeof (int))) > n); + + child_pids = malloc (sizeof (pid_t) * n); + input_fds = malloc (sizeof (int) * n); + output_fds = malloc (sizeof (int) * n); + + for (i=0; i= 0); + assert (arglist); + assert (arglist[0]); + + if (debugflag) + { + fprintf (stderr, "starting %s\n", arglist[0]); + } + child_pids[i] = start_command (0, 0, arglist, + do_setuid, uid, + input_fds[i], output_fds[i], + &child_errno, alwaysfail, NULL); + xclose (&input_fds[i]); + xclose (&output_fds[i]); + + if (0 == child_pids[i]) + { + /* Oh dear. Failed. */ + stopkids (child_pids, 1+i); + result = 0; + } + } + if (debugflag) + { + fprintf (stderr, "started all commands.\n"); + } + + input_fds [0] = output_fds[n-1] = -1; + + /* OK, we launched all the children. Wait for them to finish. + * We actually launched (1+i) processes (in the success case, of + * course n==1+i). + */ + for (j=0u; j 0) + { + pid = waitpid (child_pids[j], &exit_status[j], 0); + assert(pid != 0); + assert(pid > 0); + assert (child_pids[j] == pid); + if (childfailed (exit_status[j])) + result = 0; + } + } + } + + for (i=0u; i= 0) + xclose (&input_fds[i]); + if (output_fds[i] >= 0) + xclose (&output_fds[i]); + } + + free (child_pids); + free (input_fds); + free (output_fds); + + return result; +} + diff --git a/locate/updatedb.c b/locate/updatedb.c new file mode 100644 index 0000000..aa7ebf6 --- /dev/null +++ b/locate/updatedb.c @@ -0,0 +1,1076 @@ +/* + updatedb -- build a locate pathname database + + Copyright (C) 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. + + csh original by James Woods; sh conversion by David MacKenzie. + Rewrite in C by Leslie P. Polzer and James Youngman. + Based on a proposal by James Youngman. + +*/ + +#define _GNU_SOURCE + +#ifndef DEBUG +#define STR(a) do{}while (0); +#else +#define STR(a) fprintf(stderr, "STR: %s=%s\n", #a, a); +#endif + +#include + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +#ifdef HAVE_LOCALE_H +#include +#endif + +#if ENABLE_NLS +# include +# define _(Text) gettext (Text) +#else +# define _(Text) Text +#define textdomain(Domain) +#define bindtextdomain(Package, Directory) +#endif +#ifdef gettext_noop +# define N_(String) gettext_noop (String) +#else +/* We used to use (String) instead of just String, but apparently ISO C + * doesn't allow this (at least, that's what HP said when someone reported + * this as a compiler bug). This is HP case number 1205608192. See + * also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11250 (which references + * ANSI 3.5.7p14-15). The Intel icc compiler also rejects constructs + * like: static const char buf[] = ("string"); + */ +# define N_(String) String +#endif + +#include "quote.h" +#include "quotearg.h" +#include "printquoted.h" +#include "gnulib-version.h" + +#include "../gnulib/lib/error.h" +#include "../gnulib/lib/idcache.c" +#include "../gnulib/lib/string.h" +#include "../gnulib/lib/tempname.h" +#include "tempname.h" +#include "findutils-version.h" +#include "locatedb.h" + +/* + * TODO: + * + * * install signal handlers for cleanup + * + * * support spaces in paths (e.g. prunepaths) + * - use char** for those + * - support 'prunepath'-style command line args + * + */ + +typedef enum { OLD, LOCATE02, SLOCATE } DBFORMAT; + + + +struct map +{ + char *key; + char *value; +}; + + +struct config +{ + char *findoptions; + char *shell; + char *searchpaths; + char *netpaths; + char *prunepaths; + char *pruneregex; + char *prunefs; + char *locate_db; + DBFORMAT dbformat; + char *localuser; + char *netuser; + char *libexecdir; + char *bindir; + char *changeto; + int quiet; + + char *find; + char *frcode; + char *bigram; + char *code; +}; + +static struct config CFG = +{ + "", /* findoptions */ + "/bin/sh", /* shell */ + "/", /* searchpaths */ + "", /* netpaths */ + "/tmp /usr/tmp /var/tmp /afs /amd /sfs /proc", /* prunepaths */ + "", /* pruneregex */ + "nfs NFS proc afs proc smbfs autofs iso9660 ncpfs coda devpts ftpfs devfs mfs sysfs shfs", /* prunefs */ + LOCATE_DB, /* locate_db */ + LOCATE02, /* dbformat */ + "", /* localuser */ + "daemon", /* netuser */ + LIBEXECDIR, /* libexecdir */ + BINDIR, /* bindir */ + "/", /* changeto */ + 0, /* quiet */ + + FIND, + FRCODE, + BIGRAM, + CODE +}; + +static char** temps; +static int ntemps = 0; + +static void +add_temp_file (const char* tempfile) +{ + ++ntemps; + temps = realloc (temps, (ntemps)*sizeof(char*)); + temps[ntemps-1] = strdup (tempfile); +} + +static void +cleanup_temp_files (void) +{ + int i; + for (i=0; i= 0) + { + saved_errno = errno; + if (0 != unlink(tempname_template)) + { + close(fd); + errno = saved_errno; + return -1; + } + } + return fd; +} + + +int +prunepaths_has_trailing_slashes (const char* prunepaths) +{ + char* paths = strdup (prunepaths); + + while (paths) + { + char* path = strsep (&paths, " "); /* FIXME doesn't work for paths with spaces */ + + if (path[strlen(path)-1] == '/') + return 1; + + } + + free (paths); + + return 0; +} + + +char* +strcat_alloc (const char *string1, ...) +{ + int l; + va_list args; + char *s; + char *concat; + char *ptr; + + + if (!string1) + return NULL; + + l = 1 + strlen (string1); + va_start (args, string1); + s = va_arg (args, char*); + while (s) + { + l += strlen (s); + s = va_arg (args, char*); + } + va_end (args); + + concat = malloc (l); + ptr = concat; + + ptr = stpcpy (ptr, string1); + va_start (args, string1); + s = va_arg (args, char*); + while (s) + { + ptr = stpcpy (ptr, s); + s = va_arg (args, char*); + } + va_end (args); + + return concat; +} + + +static void +is_regular_executable (const char *file) +{ + struct stat sbuf; + + if (!file) + return; + + if (stat (file, &sbuf) == -1) + error (1, errno, _("couldn't stat %s"), + quotearg_n_style (0, locale_quoting_style, file)); + + if (!S_ISREG (sbuf.st_mode)) + error (1, 0, _("%s is not a regular file"), quotearg_n_style (0, locale_quoting_style, file)); + + if (!euidaccess (file, X_OK) == 0) + error (1, 0, _("no executable permission for %s"), quotearg_n_style (0, locale_quoting_style, file)); + + return; +} + + +static void +check_executables (const char *file, ...) +{ + va_list args; + char *vf = NULL; + + is_regular_executable (file); + + va_start (args, file); + while (vf = va_arg (args, char*)) + is_regular_executable (vf); + va_end (args); +} + + +static void +usage (FILE* out) +{ + fprintf (out, "%s", + _("Usage: updatedb [--findoptions='-option1 -option2...']\n" + "\t[--localpaths='dir1 dir2...'] [--netpaths='dir1 dir2...']\n" + "\t[--prunepaths='dir1 dir2...'] [--prunefs='fs1 fs2...']\n" + "\t[--output=dbfile] [--changecwd=dir]\n" + "\t[--netuser=user] [--localuser=user]\n" + "\t[--old-format] [--quiet] [--version] [--help]\n" + "\n" + "Report bugs to .\n")); +} + + +static void +parse_options (int* ac, char*** av) +{ + enum option_ids /* return values for getopt_long */ + { + OPT_FINDOPTIONS, + OPT_SHELL, + OPT_SEARCHPATHS, + OPT_NETPATHS, + OPT_PRUNEPATHS, + OPT_PRUNEREGEX, + OPT_PRUNEFS, + OPT_LOCATE_DB, + OPT_LOCALUSER, + OPT_NETUSER, + OPT_LIBEXECDIR, + OPT_BINDIR, + OPT_CHANGECWD, + OPT_DBFORMAT, + OPT_OLDFORMAT + }; + + static struct option const longopts[] = + { + {"findoptions", required_argument, NULL, OPT_FINDOPTIONS}, + {"shell", required_argument, NULL, OPT_SHELL}, + {"localpaths", required_argument, NULL, OPT_SEARCHPATHS}, + {"netpaths", required_argument, NULL, OPT_NETPATHS}, + {"prunepaths", required_argument, NULL, OPT_PRUNEPATHS}, + {"pruneregex", required_argument, NULL, OPT_PRUNEREGEX}, + {"prunefs", required_argument, NULL, OPT_PRUNEFS}, + {"output", required_argument, NULL, OPT_LOCATE_DB}, + {"localuser", required_argument, NULL, OPT_LOCALUSER}, + {"netuser", required_argument, NULL, OPT_NETUSER}, + {"libexecdir", required_argument, NULL, OPT_LIBEXECDIR}, + {"bindir", required_argument, NULL, OPT_BINDIR}, + {"changecwd", required_argument, NULL, OPT_CHANGECWD}, + {"dbformat", required_argument, NULL, OPT_DBFORMAT}, + {"old-format", no_argument, NULL, OPT_OLDFORMAT}, + + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {"quiet", no_argument, NULL, 'q'}, + + {NULL, no_argument, NULL, 0} + }; + + int optc; + + /* environment processing */ + +#define FROM_ENV(var, env) \ + if (getenv (#env)) \ + CFG.var = strdup (getenv (#env)); + + + FROM_ENV (findoptions, FINDOPTIONS); + FROM_ENV (shell, SHELL); + FROM_ENV (searchpaths, SEARCHPATHS); + FROM_ENV (netpaths, NETPATHS); + FROM_ENV (prunepaths, PRUNEPATHS); + FROM_ENV (pruneregex, PRUNEREGEX); + FROM_ENV (prunefs, PRUNEFS); + FROM_ENV (locate_db, LOCATE_DB); + FROM_ENV (localuser, LOCALUSER); + FROM_ENV (netuser, NETUSER); + FROM_ENV (libexecdir, LIBEXECDIR); + FROM_ENV (bindir, BINDIR); + FROM_ENV (changeto, CHANGETO); + + + /* command-line processing */ + + while ((optc = getopt_long (*ac, *av, "h", longopts, (int *) 0)) != -1) + switch (optc) + { + case 'h': + usage (stdout); + exit (EXIT_SUCCESS); + break; + + case 'v': + display_findutils_version("updatedb"); + exit(EXIT_SUCCESS); + break; + + case 'q': + CFG.quiet = 1; /* XXX this doesn't have any effect right now, + but will be useful in the future. */ + break; + + case OPT_FINDOPTIONS: + CFG.findoptions = optarg; + break; + + case OPT_SHELL: + CFG.shell = optarg; + break; + + case OPT_SEARCHPATHS: + CFG.searchpaths = optarg; + break; + + case OPT_NETPATHS: + CFG.netpaths = optarg; + break; + + case OPT_PRUNEPATHS: + if (prunepaths_has_trailing_slashes (optarg)) + { + fprintf (stderr, _("pruned paths must not contain trailing slashes\n")); + exit (EXIT_FAILURE); + } + break; + + case OPT_PRUNEREGEX: + CFG.pruneregex = optarg; + break; + + case OPT_PRUNEFS: + CFG.prunefs = optarg; + break; + + case OPT_LOCATE_DB: + CFG.locate_db = optarg; + break; + + case OPT_LOCALUSER: + CFG.localuser = optarg; + break; + + case OPT_NETUSER: + CFG.netuser = optarg; + break; + + case OPT_LIBEXECDIR: + CFG.shell = optarg; + break; + + case OPT_BINDIR: + CFG.bindir = optarg; + break; + + case OPT_CHANGECWD: + CFG.changeto = optarg; + break; + + case OPT_DBFORMAT: + if (strcasecmp ("locate02", optarg) == 0) + { + CFG.dbformat = LOCATE02; + } + else if (strcasecmp ("slocate", optarg) == 0) + { + CFG.dbformat = SLOCATE; + } + else if (strcasecmp ("old", optarg) == 0) + { + CFG.dbformat = OLD; + } + else + { + fprintf (stderr, _("Unsupported database format %s.\n" + "Supported formats are: LOCATE02 SLOCATE OLD\n"), + quotearg_n_style (0, locale_quoting_style, optarg)); + exit (EXIT_FAILURE); + } + break; + + case OPT_OLDFORMAT: + CFG.dbformat = OLD; + break; + + default: + usage (stderr); + exit (EXIT_FAILURE); + } + + STR (CFG.bindir); + STR (CFG.libexecdir); + + CFG.find = strcat_alloc (CFG.bindir, "/" FIND, 0); + CFG.frcode = strcat_alloc (CFG.libexecdir, "/" FRCODE, 0); + CFG.bigram = strcat_alloc (CFG.libexecdir, "/" BIGRAM, 0); + CFG.code = strcat_alloc (CFG.libexecdir, "/" CODE, 0); +} + + +/* cmd represents a set of arguments that we will eventually pass to + * an exec function. We use this prepresentation for conveniently + * appending extra arguments. + * + * We don't have a separate filename and argv[0]. The argv[0] + * value we use should be the actual filename of the executable. + * + * The argument vector is allocated (and extended) by us, but we + * assume that the actual arguments are owned by somebody else. + */ +struct cmd +{ + char ** args; + int nargs; +}; + +/* Add an argument to a command. */ +static void +addarg (struct cmd* ctl, const char *arg) +{ + ctl->args = realloc (ctl->args, (ctl->nargs+1)*sizeof(char*)); + ctl->args[ctl->nargs] = (char*)arg; + ++ctl->nargs; +} + +/* Create a new command. To begin with, there is not even an executable name. */ +static struct cmd* +newcmd (void) +{ + struct cmd *ctl = malloc(sizeof(struct cmd)); + ctl->args = NULL; + ctl->nargs = 0; + return ctl; +} + +/* Delete a command structure. */ +static void +delcmd (struct cmd *p) +{ + /* Assume we do not own the individual arguments. */ + free (p->args); + free (p); +} + +/* Copy arguments out of one cmd into another */ +static void +appendargs (struct cmd *ctl, struct cmd* extra) +{ + int i; + for (i=0; inargs; ++i) + { + addarg (ctl, extra->args[i]); + } +} + + +/* Split a string on spaces and append each element of the result to + * the arguments of a command. + */ +static void +split_and_append_args (struct cmd* ctl, const char *s) +{ + if (strlen(s)) + { + char *p = strdup(s); + if (s) + { + char *tmp; + while (tmp = strsep (&p, " ")) + { + addarg (ctl, tmp); + } + } + free(p); + } +} + +/* Build a find command to search a specific list of directories + * taking into a accoutn a set of prune conditions. + */ +static struct cmd* +build_find_cmd (int null_terminate, + const char* executable, + const char* paths, + const char* options, + struct cmd* prune_paths) +{ + struct cmd *p = newcmd(); + int n; + + addarg (p, executable); + n = p->nargs; + split_and_append_args (p, paths); + if (n == p->nargs) + { + delcmd (p); + return NULL; + } + split_and_append_args (p, options); + addarg (p, "("); + appendargs (p, prune_paths); + addarg (p, ")"); + addarg (p, "-prune"); + addarg (p, "-o"); + if (null_terminate) + addarg (p, "-print0"); + else + addarg (p, "-print"); + return p; +} + + +/* Given a list of paths a list of filesystems to + * avoid, build a set of find tests which will + * prune the search tree appropriately. + */ +struct cmd* +build_prune_test_list(const char *c_prunepaths, + const char *c_prunefs) +{ + char* prune_items; + char *s, *tmp; + struct cmd* p; + int prune_item_added = 0; + + p = newcmd(); + + STR (c_prunepaths); + s = prune_items = strdup (c_prunepaths); + while (tmp = strsep (&s, " ")) + { + STR (tmp); + if (prune_item_added) + addarg (p, "-o"); + addarg (p, "-type"); + addarg (p, "d"); + addarg (p, "-wholename"); + addarg (p, strdup(tmp)); + prune_item_added = 1; + } + free(prune_items); + + /* prunefs */ + STR (c_prunefs); + s = prune_items = strdup (c_prunefs); + while (tmp = strsep (&s, " ")) + { + STR (tmp); + if (prune_item_added) + addarg (p, "-o"); + addarg (p, "-fstype"); + addarg (p, strdup(tmp)); + prune_item_added = 1; + } + free (prune_items); + return p; +} + + +/* Analyse the exit status of a program. If it exited unsuccessfully, + * print a suitable error message. + */ +static int +diagnose (const char *command, + int status) +{ + if (WIFEXITED(status)) + { + int rv = WEXITSTATUS(status); + if (rv) + { + error(0, 0, + _("%s returned exit status %d"), + command, rv); + } + return rv; + } + else if (WIFSIGNALED(status)) + { + int rv = 128+WTERMSIG(status); + error(0, 0, + _("%s was killed by signal %d"), + command, WTERMSIG(status)); + return rv; + } + else + { + error(0, 0, + _("%s was killed by signal %d"), + command, WTERMSIG(status)); + return -1; + } +} + +/* Diagnose the exit statuses of the elements of a pipeline. */ +static int +diagnose_pipeline (char *const **commands, + const int *statusinfo) +{ + int worst = 0; + while (*commands) + { + int status = diagnose (commands[0][0], *statusinfo); + if (status > worst) + worst = status; + ++commands; + ++statusinfo; + } + return worst; +} + + +/* + * Encode a file list with frcode, producing a LOCATE02 format (or an slocate format) + * locate database. + */ +static int +encode_with_frcode (int null_terminate, int file_list_fd, int output_fd) +{ + enum { NCOMMANDS = 3 }; + char * const * frcode_pipeline[NCOMMANDS]; + struct cmd* command[NCOMMANDS]; + int status[NCOMMANDS]; + int i; + + + i = 0; + assert(i+1 < NCOMMANDS); + command[i] = newcmd(); + addarg (command[i], "sort"); + addarg (command[i], "-f"); + if (null_terminate) + addarg (command[i], "-z"); + + + assert(i+1 < NCOMMANDS); + command[++i] = newcmd(); + addarg (command[i], CFG.frcode); + if (null_terminate) + addarg (command[i], "-0"); + + if (SLOCATE == CFG.dbformat) + { + addarg (command[i], "-S"); + addarg (command[i], "1"); + } + + assert(i+1 < NCOMMANDS); + command[++i] = NULL; + + for (i=0; iargs; + } + else + { + frcode_pipeline[i] = NULL; + } + } + + run_pipeline (frcode_pipeline, 0, 0, status, file_list_fd, output_fd); + return diagnose_pipeline (frcode_pipeline, status); +} + + +/* + * Encode a file list with bigram and code, producing an old format + * locate database. + */ +static int +encode_with_bigram(int null_terminate, int file_list_fd, int output_fd) +{ + char bigram_template[] = "/tmp/updatedb-bigrams.XXXXXX"; + unsigned int i; + int rv, saved_errno; + enum { NCOMMANDS = 7 }; + char * const * bigram_pipeline[NCOMMANDS]; + struct cmd* command[NCOMMANDS]; + int status[NCOMMANDS]; + int bigram_fd; + + assert (0 == null_terminate); + + if (0 != lseek (file_list_fd, 0, SEEK_SET)) + error (1, errno, _("lseek failed on file list")); + + i = 0; + assert(0 < NCOMMANDS); + command[i] = newcmd(); + addarg (command[i], CFG.bigram); + + assert(i+1 < NCOMMANDS); + command[++i] = newcmd(); + addarg (command[i], "sort"); + + assert(i+1 < NCOMMANDS); + command[++i] = newcmd(); + addarg (command[i], "uniq"); + addarg (command[i], "-c"); + + assert(i+1 < NCOMMANDS); + command[++i] = newcmd(); + addarg (command[i], "sort"); + addarg (command[i], "-n"); + addarg (command[i], "-r"); + + assert(i+1 < NCOMMANDS); + command[++i] = newcmd(); + addarg (command[i], "awk"); + addarg (command[i], "{ if (NR <= 128) print $2 }"); + + assert(i+1 < NCOMMANDS); + command[++i] = newcmd(); + addarg (command[i], "tr"); + addarg (command[i], "-d"); + addarg (command[i], "\\012"); + + assert(i+1 < NCOMMANDS); + command[++i] = NULL; + assert(i == NCOMMANDS-1); + + for (i=0; iargs; + } + else + { + bigram_pipeline[i] = NULL; + } + status[i] = 0; + } + + /* Create the bigram file. */ + bigram_fd = gen_tempname (bigram_template, GT_FILE); + if (bigram_fd < 0) + { + saved_errno = errno; + error(0, errno, _("failed to create temporary file")); + return 1; + } + + /* Compute and save the bigram list. */ + run_pipeline (bigram_pipeline, 0, 0, status, file_list_fd, bigram_fd); + if (diagnose_pipeline (bigram_pipeline, status)) + { + saved_errno = errno; + unlink (bigram_template); + errno = saved_errno; + return 1; + } + + /* Clean up the pipeline */ + for (i=0; iargs; + + command[++i] = newcmd (); + addarg (command[i], CFG.code); + addarg (command[i], bigram_template); + addarg (command[i], NULL); + bigram_pipeline[i] = command[i]->args; + + bigram_pipeline[++i] = NULL; + + run_pipeline(bigram_pipeline, 0, 0, status, file_list_fd, output_fd); + rv = 0; + if (diagnose_pipeline (bigram_pipeline, status)) + rv = 1; + + if (0 != unlink (bigram_template)) + { + error(0, errno, _("failed to delete %s"), bigram_template); + rv = 1; + } + return rv; +} + + +/* small specialization of run_pipeline */ +static int +run_pipeline_as_user (char * const **args, const char* user, + int *exit_status, int input, int output) +{ + int do_setuid = 0; + uid_t uid, *puid; + + uid = geteuid (); + if (user[0]) + { + puid = getuidbyname (user); + if (NULL == puid) + { + error (1, errno, _("could not get UID for user %s"), + quotearg_n_style (0, locale_quoting_style, user)); + /*NOTREACHED*/ + } + else + { + do_setuid = (*puid != uid); + uid = *puid; + } + } + return run_pipeline (args, do_setuid, uid, exit_status, input, output); +} + + +int +main (int argc, char **argv) +{ + char * const * db_pipeline[] = {NULL, NULL, NULL}; + int tmpfile_fd, output_fd, saved_errno; + size_t dbfilename_len; + int status[3]; + char *tmpdb, *codefile; + struct cmd *sort=NULL, *local_find=NULL, *net_find=NULL; + struct cmd *prune_paths=NULL; + int (*encoder)(int nullterminate, int input, int output); + int null_terminate; + + +#ifdef HAVE_SETLOCALE + setlocale (LC_ALL, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + parse_options (&argc, &argv); + + STR (CFG.find); + STR (CFG.frcode); + STR (CFG.bigram); + STR (CFG.code); + + check_executables (CFG.find, CFG.frcode, CFG.bigram, CFG.code, 0); + + prune_paths = newcmd(); + +#if 0 + prune_old_temporaries(); + setup_signal_handlers(); /* remove $LOCATE_DB.n on HUP & TERM */ +#endif + + chdir (CFG.changeto); + + + if (CFG.dbformat == OLD) + { + encoder = encode_with_bigram; + null_terminate = 0; + } + else + { + encoder = encode_with_frcode; +#ifdef SORT_SUPPORTS_Z + null_terminate = 1; +#else + null_terminate = 0; +#endif + } + + prune_paths = build_prune_test_list (CFG.prunepaths, CFG.prunefs); + local_find = build_find_cmd (null_terminate, CFG.find, CFG.searchpaths, CFG.findoptions, prune_paths); + net_find = build_find_cmd (null_terminate, CFG.find, CFG.netpaths, CFG.findoptions, prune_paths); + + atexit (cleanup_temp_files); + tmpfile_fd = scratchfile(); + if (tmpfile_fd < 0) + error(1, errno, _("failed to create temporary file")); + + if (local_find) + { + addarg (local_find, NULL); + db_pipeline[0] = local_find->args; + db_pipeline[1] = NULL; + + run_pipeline_as_user (db_pipeline, CFG.localuser, status, STDIN_FILENO, tmpfile_fd); + + if (diagnose_pipeline (db_pipeline, status)) + return 1; + } + if (net_find) + { + addarg (net_find, NULL); + db_pipeline[0] = net_find->args; + db_pipeline[1] = NULL; + run_pipeline_as_user (db_pipeline, CFG.netuser, status, STDIN_FILENO, tmpfile_fd); + + if (diagnose_pipeline (db_pipeline, status)) + return 1; + } + /* rewind output file to use it as input */ + if (0 != lseek(tmpfile_fd, 0, SEEK_SET)) + error (1, errno, _("lseek failed on temporary file")); + + dbfilename_len = 1+strlen (CFG.locate_db)+2; + tmpdb = malloc (dbfilename_len); + sprintf (tmpdb, "%s.n", CFG.locate_db); + + output_fd = open (tmpdb, O_WRONLY|O_CREAT, 0644); + if (output_fd < 0) + error (1, errno, CFG.locate_db); + + if (0 != encoder (null_terminate, tmpfile_fd, output_fd)) + { + unlink (tmpdb); + return 1; + } + else + { + /* Delete the old database, but don't fail if it + * didn't exist anyway. + */ + if (0 != unlink (CFG.locate_db)) + { + saved_errno = errno; + if (ENOENT != errno) + { + unlink (tmpdb); + error (1, saved_errno, CFG.locate_db); + } + } + if (0 != rename (tmpdb, CFG.locate_db)) + { + saved_errno = errno; + unlink (tmpdb); + error (1, saved_errno, tmpdb); + } + } + free (tmpdb); + return 0; +} + diff --git a/locate/updatedb.sh b/locate/updatedb.sh index 9f4e4d9..2c572ca 100644 --- a/locate/updatedb.sh +++ b/locate/updatedb.sh @@ -1,22 +1,5 @@ #! /bin/sh -# updatedb -- build a locate pathname database -# Copyright (C) 1994, 1996, 1997, 2000, 2001, 2003, 2004, 2005, 2006 -# Free Software Foundation, Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# csh original by James Woods; sh conversion by David MacKenzie. + #exec 2> /tmp/updatedb-trace.txt #set -x @@ -174,108 +157,48 @@ done test -z "$PRUNEREGEX" && PRUNEREGEX=`echo $PRUNEPATHS|sed -e 's,^,\\\(^,' -e 's, ,$\\\)\\\|\\\(^,g' -e 's,$,$\\\),'` -# The database file to build. -: address@hidden@} - -# Directory to hold intermediate files. -if test -d /var/tmp; then - : ${TMPDIR=/var/tmp} -elif test -d /usr/tmp; then - : ${TMPDIR=/usr/tmp} -else - : ${TMPDIR=/tmp} -fi -export TMPDIR - -# The user to search network directories as. -: ${NETUSER=daemon} - -# The directory containing the subprograms. -if test -n "$LIBEXECDIR" ; then - : LIBEXECDIR already set, do nothing -else - : address@hidden@} -fi - -# The directory containing find. -if test -n "$BINDIR" ; then - : BINDIR already set, do nothing -else - : address@hidden@} -fi - -# The names of the utilities to run to build the database. -: ${find:=${BINDIR}/@address@hidden -: ${frcode:=${LIBEXECDIR}/@address@hidden -: ${bigram:=${LIBEXECDIR}/@address@hidden -: ${code:=${LIBEXECDIR}/@address@hidden - -checkbinary () { - if test -x "$1" ; then - : ok + if test -n "$PRUNEFS"; then + # findify prunefs + prunefs_exp=`echo $PRUNEFS |sed -e 's/\([^ ][^ ]*\)/-o -fstype \1/g' \ + -e 's/-o //' -e 's/$/ -o/'` else - eval echo "updatedb needs to be able to execute $1, but cannot." >&2 - exit 1 + prunefs_exp='' fi -} - -for binary in $find $frcode $bigram $code -do - checkbinary $binary -done - - -PATH=/bin:/usr/bin:${BINDIR}; export PATH - -: ${PRUNEFS="nfs NFS proc afs proc smbfs autofs iso9660 ncpfs coda devpts ftpfs devfs mfs sysfs shfs"} -if test -n "$PRUNEFS"; then -prunefs_exp=`echo $PRUNEFS |sed -e 's/\([^ ][^ ]*\)/-o -fstype \1/g' \ - -e 's/-o //' -e 's/$/ -o/'` -else - prunefs_exp='' -fi # Make and code the file list. # Sort case insensitively for users' convenience. -rm -f $LOCATE_DB.n -trap 'rm -f $LOCATE_DB.n; exit' HUP TERM - -if test $old = no; then -# LOCATE02 or slocate format if { -cd "$changeto" -if test -n "$SEARCHPATHS"; then + cd "$changeto" + if test -n "$SEARCHPATHS"; then if [ "$LOCALUSER" != "" ]; then - # : A1 su $LOCALUSER `select_shell $LOCALUSER` -c \ "$find $SEARCHPATHS $FINDOPTIONS \ \\( $prunefs_exp \ -type d -regex '$PRUNEREGEX' \\) -prune -o $print_option" else - # : A2 $find $SEARCHPATHS $FINDOPTIONS \ \( $prunefs_exp \ -type d -regex "$PRUNEREGEX" \) -prune -o $print_option fi -fi + fi # SEARCHPATHS -if test -n "$NETPATHS"; then -myuid=`getuid` -if [ "$myuid" = 0 ]; then - # : A3 + if test -n "$NETPATHS"; then + myuid=`getuid` + if [ "$myuid" = 0 ]; then su $NETUSER `select_shell $NETUSER` -c \ "$find $NETPATHS $FINDOPTIONS \\( -type d -regex '$PRUNEREGEX' -prune \\) -o $print_option" || exit $? else - # : A4 $find $NETPATHS $FINDOPTIONS \( -type d -regex "$PRUNEREGEX" -prune \) -o $print_option || exit $? fi -fi + fi # NETPATHS + } | $sort -f | $frcode $frcode_options > $LOCATE_DB.n + then : OK so far true @@ -289,82 +212,12 @@ fi # To avoid breaking locate while this script is running, put the # results in a temp file, then rename it atomically. if test -s $LOCATE_DB.n; then - chmod 644 ${LOCATE_DB}.n - mv ${LOCATE_DB}.n $LOCATE_DB + rm -f $LOCATE_DB + mv $LOCATE_DB.n $LOCATE_DB + chmod 644 $LOCATE_DB else echo "updatedb: new database would be empty" >&2 rm -f $LOCATE_DB.n fi -else # old - -if ! bigrams=`mktemp -t updatedbXXXXXXXXX`; then - echo mktemp failed >&2 - exit 1 -fi - -if ! filelist=`mktemp -t updatedbXXXXXXXXX`; then - echo mktemp failed >&2 - exit 1 -fi - -rm -f $LOCATE_DB.n -trap 'rm -f $bigrams $filelist $LOCATE_DB.n; exit' HUP TERM - -# Alphabetize subdirectories before file entries using tr. James Woods says: -# "to get everything in monotonic collating sequence, to avoid some -# breakage i'll have to think about." -{ -cd "$changeto" -if test -n "$SEARCHPATHS"; then - if [ "$LOCALUSER" != "" ]; then - # : A5 - su $LOCALUSER `select_shell $LOCALUSER` -c \ - "$find $SEARCHPATHS $FINDOPTIONS \ - \( $prunefs_exp \ - -type d -regex '$PRUNEREGEX' \) -prune -o $print_option" || exit $? - else - # : A6 - $find $SEARCHPATHS $FINDOPTIONS \ - \( $prunefs_exp \ - -type d -regex "$PRUNEREGEX" \) -prune -o $print_option || exit $? - fi -fi - -if test -n "$NETPATHS"; then - myuid=`getuid` - if [ "$myuid" = 0 ]; then - # : A7 - su $NETUSER `select_shell $NETUSER` -c \ - "$find $NETPATHS $FINDOPTIONS \\( -type d -regex '$PRUNEREGEX' -prune \\) -o $print_option" || - exit $? - else - # : A8 - $find $NETPATHS $FINDOPTIONS \( -type d -regex "$PRUNEREGEX" -prune \) -o $print_option || - exit $? - fi -fi -} | tr / '\001' | $sort -f | tr '\001' / > $filelist - -# Compute the (at most 128) most common bigrams in the file list. -$bigram $bigram_opts < $filelist | sort | uniq -c | sort -nr | - awk '{ if (NR <= 128) print $2 }' | tr -d '\012' > $bigrams - -# Code the file list. -$code $bigrams < $filelist > $LOCATE_DB.n - -rm -f $bigrams $filelist - -# To reduce the chances of breaking locate while this script is running, -# put the results in a temp file, then rename it atomically. -if test -s $LOCATE_DB.n; then - chmod 644 ${LOCATE_DB}.n - mv ${LOCATE_DB}.n $LOCATE_DB -else - echo "updatedb: new database would be empty" >&2 - rm -f $LOCATE_DB.n -fi - -fi - exit 0 diff --git a/po/POTFILES.in b/po/POTFILES.in index 0afb2c7..3491eb8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -37,4 +37,5 @@ locate/code.c locate/frcode.c locate/locate.c locate/word_io.c +locate/updatedb.c xargs/xargs.c -- 1.5.3.8