summaryrefslogtreecommitdiff
path: root/find/exec.c
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-12-28 21:37:38 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-12-28 21:37:38 +0000
commitd7fdaf4d3a9795a294e93cad6f7d8238ba3754a6 (patch)
tree9bff05ea75330b7d8e2ade183cdcd1e4282f27d9 /find/exec.c
downloadfindutils-tarball-master.tar.gz
Diffstat (limited to 'find/exec.c')
-rw-r--r--find/exec.c390
1 files changed, 390 insertions, 0 deletions
diff --git a/find/exec.c b/find/exec.c
new file mode 100644
index 0000000..7b6ecf6
--- /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/>.
+*/
+
+/* config.h must be included first. */
+#include <config.h>
+
+/* system headers. */
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+
+/* gnulib headers */
+#include "cloexec.h"
+#include "dirname.h"
+#include "error.h"
+#include "gettext.h"
+#include "save-cwd.h"
+#include "xalloc.h"
+
+/* findutils headers */
+#include "buildcmd.h"
+#include "defs.h"
+#include "fdleak.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
+
+
+/* Initialize exec->wd_for_exec.
+
+ We save in exec->wd_for_exec the directory whose path relative to
+ cwd_df is dir.
+ */
+static bool
+initialize_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 = initialize_wd_for_exec (execp, state.cwd_dir_fd, dir);
+ free (dir);
+ return result;
+ }
+ else
+ {
+ return initialize_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 *buf = NULL;
+ const 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 struct 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. */
+ if (execp->state.todo)
+ 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 (local)
+ free_cwd (execp->wd_for_exec);
+ }
+ if (buf)
+ {
+ assert (local);
+ free (buf);
+ }
+ 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%s%s"),
+ (wd->desc < 0 && wd->name) ? ": " : "",
+ (wd->desc < 0 && wd->name) ? wd->name : "");
+ 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) */
+ }
+
+}