diff options
author | James Youngman <jay@gnu.org> | 2011-06-12 20:28:41 +0100 |
---|---|---|
committer | James Youngman <jay@gnu.org> | 2011-06-12 20:28:41 +0100 |
commit | 9044591cabd10454e77df781799f1eee0745679e (patch) | |
tree | 7a406e74b31feac0c8914689256b39a66e26cc4b | |
parent | fa1150599babca913a1d5108cece758bd7071342 (diff) | |
download | findutils-9044591cabd10454e77df781799f1eee0745679e.tar.gz |
Split exec-related code into a new file, exec.c
* find/exec.c: New file.
(initialise_wd_for_exec): Move out of pred.c
(record_exec_dir): Likewise.
(impl_pred_exec): Likewise.
(prep_child_for_exec): Likewise.
(launch): Likewise.
* find/pred.c: These functions are moved out of this file.
* find/defs.h: impl_pred_exec is no longer static in pred.c, do
add an external declaration for it. Move the declaration of
launch to here also.
* find/Makefile.am (libfindtools_a_SOURCES): Add exec.c.
* po/POTFILES.in: Add find/exec.c.
Signed-off-by: James Youngman <jay@gnu.org>
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | find/Makefile.am | 2 | ||||
-rw-r--r-- | find/defs.h | 6 | ||||
-rw-r--r-- | find/exec.c | 390 | ||||
-rw-r--r-- | find/pred.c | 335 | ||||
-rw-r--r-- | po/POTFILES.in | 1 |
6 files changed, 410 insertions, 338 deletions
@@ -1,5 +1,19 @@ 2011-06-12 James Youngman <jay@gnu.org> + Split exec-related code into a new file, exec.c + * find/exec.c: New file. + (initialise_wd_for_exec): Move out of pred.c + (record_exec_dir): Likewise. + (impl_pred_exec): Likewise. + (prep_child_for_exec): Likewise. + (launch): Likewise. + * find/pred.c: These functions are moved out of this file. + * find/defs.h: impl_pred_exec is no longer static in pred.c, so + add an external declaration for it. Move the declaration of + launch to here also. + * find/Makefile.am (libfindtools_a_SOURCES): Add exec.c. + * po/POTFILES.in: Add find/exec.c. + Eliminate .x-sc_prohibit_empty_lines_at_EOF and .x-sc_bindtextdomain * cfg.mk: Move definition of which files we should not check to exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF, instead diff --git a/find/Makefile.am b/find/Makefile.am index ba18731e..bfb50ce1 100644 --- a/find/Makefile.am +++ b/find/Makefile.am @@ -5,7 +5,7 @@ localedir = $(datadir)/locale # regexprops_SOURCES = regexprops.c noinst_LIBRARIES = libfindtools.a -libfindtools_a_SOURCES = finddata.c fstype.c parser.c pred.c tree.c util.c sharefile.c +libfindtools_a_SOURCES = finddata.c fstype.c parser.c pred.c exec.c tree.c util.c sharefile.c # We always build two versions of find, one with fts, one without. diff --git a/find/defs.h b/find/defs.h index 0801ad2d..17425923 100644 --- a/find/defs.h +++ b/find/defs.h @@ -464,11 +464,9 @@ PREDICATEFUNCTION pred_context; int launch (struct buildcmd_control *ctl, void *usercontext, int argc, char **argv); - char *find_pred_name (PRED_FUNC pred_func); - void print_predicate (FILE *fp, const struct predicate *p); void print_tree (FILE*, struct predicate *node, int indent); void print_list (FILE*, struct predicate *node); @@ -669,6 +667,10 @@ struct state bool already_issued_stat_error_msg; }; +/* exec.c */ +bool impl_pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr); +int launch (struct buildcmd_control *ctl, void *usercontext, int argc, char **argv); + /* finddata.c */ extern struct options options; extern struct state state; diff --git a/find/exec.c b/find/exec.c new file mode 100644 index 00000000..db4c0297 --- /dev/null +++ b/find/exec.c @@ -0,0 +1,390 @@ +/* exec.c -- Implementation of -exec, -execdir, -ok, -okdir. + Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2003, + 2004, 2005, 2006, 2007, 2008, 2009, + 2010 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 <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +/* Normal POSIX headers (some of which are replaced by gnulib) */ +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <signal.h> +#include <sys/wait.h> + + +/* gnulib headers */ +#include "error.h" +#include "dirname.h" +#include "xalloc.h" +#include "cloexec.h" +#include "save-cwd.h" + +/* findutils headers */ +#include "buildcmd.h" +#include "fdleak.h" +#include "defs.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(Text) gettext (Text) +#else +# define _(Text) Text +#endif +#ifdef gettext_noop +# define N_(String) gettext_noop (String) +#else +/* See locate.c for explanation as to why not use (String) */ +# define N_(String) String +#endif + + +#if defined SIGCLD && !defined SIGCHLD +# define SIGCHLD SIGCLD +#endif + + + + + +/* Initialise exec->wd_for_exec. + + We save in exec->wd_for_exec the directory whose path relative to + cwd_df is dir. + */ +static bool +initialise_wd_for_exec (struct exec_val *execp, int cwd_fd, const char *dir) +{ + execp->wd_for_exec = xmalloc (sizeof (*execp->wd_for_exec)); + execp->wd_for_exec->name = NULL; + execp->wd_for_exec->desc = openat (cwd_fd, dir, O_RDONLY); + if (execp->wd_for_exec->desc < 0) + return false; + set_cloexec_flag (execp->wd_for_exec->desc, true); + return true; +} + + +static bool +record_exec_dir (struct exec_val *execp) +{ + if (!execp->state.todo) + { + /* working directory not already known, so must be a *dir variant, + and this must be the first arg we added. However, this may + be -execdir foo {} \; (i.e. not multiple). */ + assert (!execp->state.todo); + + /* Record the WD. If we're using -L or fts chooses to do so for + any other reason, state.cwd_dir_fd may in fact not be the + directory containing the target file. When this happens, + rel_path will contain directory components (since it is the + path from state.cwd_dir_fd to the target file). + + We deal with this by extracting any directory part and using + that to adjust what goes into execp->wd_for_exec. + */ + if (strchr (state.rel_pathname, '/')) + { + char *dir = mdir_name (state.rel_pathname); + bool result = initialise_wd_for_exec (execp, state.cwd_dir_fd, dir); + free (dir); + return result; + } + else + { + return initialise_wd_for_exec (execp, state.cwd_dir_fd, "."); + } + } + return true; +} + + + + +bool +impl_pred_exec (const char *pathname, + struct stat *stat_buf, + struct predicate *pred_ptr) +{ + struct exec_val *execp = &pred_ptr->args.exec_vec; + char *target; + bool result; + const bool local = is_exec_in_local_dir (pred_ptr->pred_func); + char *prefix; + size_t pfxlen; + + (void) stat_buf; + if (local) + { + /* For -execdir/-okdir predicates, the parser did not fill in + the wd_for_exec member of sturct exec_val. So for those + predicates, we do so now. + */ + if (!record_exec_dir (execp)) + { + error (EXIT_FAILURE, errno, + _("Failed to save working directory in order to " + "run a command on %s"), + safely_quote_err_filename (0, pathname)); + /*NOTREACHED*/ + } + target = base_name (state.rel_pathname); + if ('/' == target[0]) + { + /* find / execdir ls -d {} \; */ + prefix = NULL; + pfxlen = 0; + } + else + { + prefix = "./"; + pfxlen = 2u; + } + } + else + { + /* For the others (-exec, -ok), the parser should + have set wd_for_exec to initial_wd, indicating + that the exec should take place from find's initial + working directory. + */ + assert (execp->wd_for_exec == initial_wd); + target = pathname; + prefix = NULL; + pfxlen = 0u; + } + + if (execp->multiple) + { + /* Push the argument onto the current list. + * The command may or may not be run at this point, + * depending on the command line length limits. + */ + bc_push_arg (&execp->ctl, + &execp->state, + target, strlen (target)+1, + prefix, pfxlen, + 0); + + /* remember that there are pending execdirs. */ + state.execdirs_outstanding = true; + + /* POSIX: If the primary expression is punctuated by a plus + * sign, the primary shall always evaluate as true + */ + result = true; + } + else + { + int i; + + for (i=0; i<execp->num_args; ++i) + { + bc_do_insert (&execp->ctl, + &execp->state, + execp->replace_vec[i], + strlen (execp->replace_vec[i]), + prefix, pfxlen, + target, strlen (target), + 0); + } + + /* Actually invoke the command. */ + bc_do_exec (&execp->ctl, &execp->state); + if (WIFEXITED(execp->last_child_status)) + { + if (0 == WEXITSTATUS(execp->last_child_status)) + result = true; /* The child succeeded. */ + else + result = false; + } + else + { + result = false; + } + } + if (target != pathname) + { + assert (local); + free (target); + } + return result; +} + + + +/* 1) fork to get a child; parent remembers the child pid + 2) child execs the command requested + 3) parent waits for child; checks for proper pid of child + + Possible returns: + + ret errno status(h) status(l) + + pid x signal# 0177 stopped + pid x exit arg 0 term by _exit + pid x 0 signal # term by signal + -1 EINTR parent got signal + -1 other some other kind of error + + Return true only if the pid matches, status(l) is + zero, and the exit arg (status high) is 0. + Otherwise return false, possibly printing an error message. */ +static bool +prep_child_for_exec (bool close_stdin, const struct saved_cwd *wd) +{ + bool ok = true; + if (close_stdin) + { + const char inputfile[] = "/dev/null"; + + if (close (0) < 0) + { + error (0, errno, _("Cannot close standard input")); + ok = false; + } + else + { + if (open (inputfile, O_RDONLY +#if defined O_LARGEFILE + |O_LARGEFILE +#endif + ) < 0) + { + /* This is not entirely fatal, since + * executing the child with a closed + * stdin is almost as good as executing it + * with its stdin attached to /dev/null. + */ + error (0, errno, "%s", safely_quote_err_filename (0, inputfile)); + /* do not set ok=false, it is OK to continue anyway. */ + } + } + } + + /* Even if DebugSearch is set, don't announce our change of + * directory, since we're not going to emit a subsequent + * announcement of a call to stat() anyway, as we're about to exec + * something. + */ + if (0 != restore_cwd (wd)) + { + error (0, errno, _("Failed to change directory")); + ok = false; + } + return ok; +} + + +int +launch (struct buildcmd_control *ctl, void *usercontext, int argc, char **argv) +{ + pid_t child_pid; + static int first_time = 1; + struct exec_val *execp = usercontext; + + /* Make sure output of command doesn't get mixed with find output. */ + fflush (stdout); + fflush (stderr); + + /* Make sure to listen for the kids. */ + if (first_time) + { + first_time = 0; + signal (SIGCHLD, SIG_DFL); + } + + child_pid = fork (); + if (child_pid == -1) + error (EXIT_FAILURE, errno, _("cannot fork")); + if (child_pid == 0) + { + /* We are the child. */ + assert (NULL != execp->wd_for_exec); + if (!prep_child_for_exec (execp->close_stdin, execp->wd_for_exec)) + { + _exit (1); + } + else + { + if (fd_leak_check_is_enabled ()) + { + complain_about_leaky_fds (); + } + } + + if (bc_args_exceed_testing_limit (argv)) + errno = E2BIG; + else + execvp (argv[0], argv); + /* TODO: use a pipe to pass back the errno value, like xargs does */ + error (0, errno, "%s", + safely_quote_err_filename (0, argv[0])); + _exit (1); + } + + while (waitpid (child_pid, &(execp->last_child_status), 0) == (pid_t) -1) + { + if (errno != EINTR) + { + error (0, errno, _("error waiting for %s"), + safely_quote_err_filename (0, argv[0])); + state.exit_status = 1; + return 0; /* FAIL */ + } + } + + if (WIFSIGNALED (execp->last_child_status)) + { + error (0, 0, _("%s terminated by signal %d"), + quotearg_n_style (0, options.err_quoting_style, argv[0]), + WTERMSIG (execp->last_child_status)); + + if (execp->multiple) + { + /* -exec \; just returns false if the invoked command fails. + * -exec {} + returns true if the invoked command fails, but + * sets the program exit status. + */ + state.exit_status = 1; + } + + return 1; /* OK */ + } + + if (0 == WEXITSTATUS (execp->last_child_status)) + { + return 1; /* OK */ + } + else + { + if (execp->multiple) + { + /* -exec \; just returns false if the invoked command fails. + * -exec {} + returns true if the invoked command fails, but + * sets the program exit status. + */ + state.exit_status = 1; + } + /* The child failed, but this is the exec callback. We + * don't want to run the child again in this case anwyay. + */ + return 1; /* FAIL (but don't try again) */ + } + +} diff --git a/find/pred.c b/find/pred.c index 3b15a6e5..4aaebe24 100644 --- a/find/pred.c +++ b/find/pred.c @@ -20,7 +20,6 @@ #include "defs.h" #include <fnmatch.h> -#include <signal.h> #include <math.h> #include <pwd.h> #include <grp.h> @@ -40,7 +39,6 @@ #include "human.h" #include "filemode.h" #include "printquoted.h" -#include "buildcmd.h" #include "yesno.h" #include "listfile.h" #include "stat-time.h" @@ -48,10 +46,7 @@ #include "dircallback.h" #include "error.h" #include "verify.h" -#include "fdleak.h" #include "areadlink.h" -#include "cloexec.h" -#include "save-cwd.h" #include <selinux/selinux.h> @@ -68,11 +63,6 @@ # define N_(String) String #endif -#if defined SIGCLD && !defined SIGCHLD -# define SIGCHLD SIGCLD -#endif - - #ifdef CLOSEDIR_VOID /* Fake a return value. */ #define CLOSEDIR(d) (closedir (d), 0) @@ -425,166 +415,6 @@ pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ } -/* Initialise exec->wd_for_exec. - - We save in exec->wd_for_exec the directory whose path relative to - cwd_df is dir. - */ -static bool -initialise_wd_for_exec (struct exec_val *execp, int cwd_fd, const char *dir) -{ - execp->wd_for_exec = xmalloc (sizeof (*execp->wd_for_exec)); - execp->wd_for_exec->name = NULL; - execp->wd_for_exec->desc = openat (cwd_fd, dir, O_RDONLY); - if (execp->wd_for_exec->desc < 0) - return false; - set_cloexec_flag (execp->wd_for_exec->desc, true); - return true; -} - - -static bool -record_exec_dir (struct exec_val *execp) -{ - if (!execp->state.todo) - { - /* Record the WD. If we're using -L or fts chooses to do so for - any other reason, state.cwd_dir_fd may in fact not be the - directory containing the target file. When this happens, - rel_path will contain directory components (since it is the - path from state.cwd_dir_fd to the target file). - - We deal with this by extracting any directory part and using - that to adjust what goes into execp->wd_for_exec. - */ - if (strchr (state.rel_pathname, '/')) - { - char *dir = mdir_name (state.rel_pathname); - bool result = initialise_wd_for_exec (execp, state.cwd_dir_fd, dir); - free (dir); - return result; - } - else - { - return initialise_wd_for_exec (execp, state.cwd_dir_fd, "."); - } - } - return true; -} - - -static bool -impl_pred_exec (const char *pathname, - struct stat *stat_buf, - struct predicate *pred_ptr) -{ - struct exec_val *execp = &pred_ptr->args.exec_vec; - char *buf = NULL; - const char *target; - bool result; - const bool local = is_exec_in_local_dir (pred_ptr->pred_func); - const char *prefix; - size_t pfxlen; - - (void) stat_buf; - if (local) - { - /* For -execdir/-okdir predicates, the parser did not fill in - the wd_for_exec member of sturct exec_val. So for those - predicates, we do so now. - */ - if (!record_exec_dir (execp)) - { - error (EXIT_FAILURE, errno, - _("Failed to save working directory in order to " - "run a command on %s"), - safely_quote_err_filename (0, pathname)); - /*NOTREACHED*/ - } - target = buf = base_name (state.rel_pathname); - if ('/' == target[0]) - { - /* find / execdir ls -d {} \; */ - prefix = NULL; - pfxlen = 0; - } - else - { - prefix = "./"; - pfxlen = 2u; - } - } - else - { - /* For the others (-exec, -ok), the parser should - have set wd_for_exec to initial_wd, indicating - that the exec should take place from find's initial - working directory. - */ - assert (execp->wd_for_exec == initial_wd); - target = pathname; - prefix = NULL; - pfxlen = 0u; - } - - if (execp->multiple) - { - /* Push the argument onto the current list. - * The command may or may not be run at this point, - * depending on the command line length limits. - */ - bc_push_arg (&execp->ctl, - &execp->state, - target, strlen (target)+1, - prefix, pfxlen, - 0); - - /* remember that there are pending execdirs. */ - state.execdirs_outstanding = true; - - /* POSIX: If the primary expression is punctuated by a plus - * sign, the primary shall always evaluate as true - */ - result = true; - } - else - { - int i; - - for (i=0; i<execp->num_args; ++i) - { - bc_do_insert (&execp->ctl, - &execp->state, - execp->replace_vec[i], - strlen (execp->replace_vec[i]), - prefix, pfxlen, - target, strlen (target), - 0); - } - - /* Actually invoke the command. */ - bc_do_exec (&execp->ctl, &execp->state); - if (WIFEXITED(execp->last_child_status)) - { - if (0 == WEXITSTATUS(execp->last_child_status)) - result = true; /* The child succeeded. */ - else - result = false; - } - else - { - result = false; - } - } - if (buf) - { - assert (local); - free (buf); - } - return result; -} - - bool pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) { @@ -1915,172 +1745,7 @@ pred_context (const char *pathname, struct stat *stat_buf, } -/* 1) fork to get a child; parent remembers the child pid - 2) child execs the command requested - 3) parent waits for child; checks for proper pid of child - - Possible returns: - - ret errno status(h) status(l) - - pid x signal# 0177 stopped - pid x exit arg 0 term by _exit - pid x 0 signal # term by signal - -1 EINTR parent got signal - -1 other some other kind of error - - Return true only if the pid matches, status(l) is - zero, and the exit arg (status high) is 0. - Otherwise return false, possibly printing an error message. */ - - -static bool -prep_child_for_exec (bool close_stdin, const struct saved_cwd *wd) -{ - bool ok = true; - if (close_stdin) - { - const char inputfile[] = "/dev/null"; - - if (close (0) < 0) - { - error (0, errno, _("Cannot close standard input")); - ok = false; - } - else - { - if (open (inputfile, O_RDONLY -#if defined O_LARGEFILE - |O_LARGEFILE -#endif - ) < 0) - { - /* This is not entirely fatal, since - * executing the child with a closed - * stdin is almost as good as executing it - * with its stdin attached to /dev/null. - */ - error (0, errno, "%s", safely_quote_err_filename (0, inputfile)); - /* do not set ok=false, it is OK to continue anyway. */ - } - } - } - - /* Even if DebugSearch is set, don't announce our change of - * directory, since we're not going to emit a subsequent - * announcement of a call to stat() anyway, as we're about to exec - * something. - */ - if (0 != restore_cwd (wd)) - { - error (0, errno, _("Failed to change directory")); - ok = false; - } - return ok; -} - - - - - - -int -launch (struct buildcmd_control *ctl, void *usercontext, int argc, char **argv) -{ - pid_t child_pid; - static int first_time = 1; - struct exec_val *execp = usercontext; - - /* Make sure output of command doesn't get mixed with find output. */ - fflush (stdout); - fflush (stderr); - - /* Make sure to listen for the kids. */ - if (first_time) - { - first_time = 0; - signal (SIGCHLD, SIG_DFL); - } - - child_pid = fork (); - if (child_pid == -1) - error (EXIT_FAILURE, errno, _("cannot fork")); - if (child_pid == 0) - { - /* We are the child. */ - assert (NULL != execp->wd_for_exec); - if (!prep_child_for_exec (execp->close_stdin, execp->wd_for_exec)) - { - _exit (1); - } - else - { - if (fd_leak_check_is_enabled ()) - { - complain_about_leaky_fds (); - } - } - - if (bc_args_exceed_testing_limit (argv)) - errno = E2BIG; - else - execvp (argv[0], argv); - /* TODO: use a pipe to pass back the errno value, like xargs does */ - error (0, errno, "%s", - safely_quote_err_filename (0, argv[0])); - _exit (1); - } - - while (waitpid (child_pid, &(execp->last_child_status), 0) == (pid_t) -1) - { - if (errno != EINTR) - { - error (0, errno, _("error waiting for %s"), - safely_quote_err_filename (0, argv[0])); - state.exit_status = 1; - return 0; /* FAIL */ - } - } - - if (WIFSIGNALED (execp->last_child_status)) - { - error (0, 0, _("%s terminated by signal %d"), - quotearg_n_style (0, options.err_quoting_style, argv[0]), - WTERMSIG (execp->last_child_status)); - - if (execp->multiple) - { - /* -exec \; just returns false if the invoked command fails. - * -exec {} + returns true if the invoked command fails, but - * sets the program exit status. - */ - state.exit_status = 1; - } - - return 1; /* OK */ - } - - if (0 == WEXITSTATUS (execp->last_child_status)) - { - return 1; /* OK */ - } - else - { - if (execp->multiple) - { - /* -exec \; just returns false if the invoked command fails. - * -exec {} + returns true if the invoked command fails, but - * sets the program exit status. - */ - state.exit_status = 1; - } - /* The child failed, but this is the exec callback. We - * don't want to run the child again in this case anwyay. - */ - return 1; /* FAIL (but don't try again) */ - } -} static bool diff --git a/po/POTFILES.in b/po/POTFILES.in index 3cc18f2f..d4f8d7e7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -6,6 +6,7 @@ # and this notice are preserved. #Package source files +find/exec.c find/find.c find/fstype.c find/ftsfind.c |