diff --git a/NEWS b/NEWS index 3994c59..1662af2 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,20 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout) * Major changes in release 4.5.0-CVS +** Functional changes + +If xargs fails to run a program because the exec system call finds too +many levels of symbolic links, it exits with status 127 (signifying +that the program cannot be found) rather than 126 (which would signify +that it was found but could not be run). Similarly if the full name +of the executable refers to a directory which does not exist. + +** Enhancements + +If xargs find that exec fails because the argument size limit it +calculated is larger than the system's actual maximum, it now adapts +by passing fewer arguments (as opposed to failing). + ** Performance changes The default optimisation level for find is now -O2 instead of -O0, @@ -21,6 +35,11 @@ operations needed to make a test are. ** Bug Fixes +#22708: Exit status 126 and 127 from the utility invoked from xargs +now makes xargs return 123, meaning that exit status values 126 and +127 now unambigously mean that the utility could not be run or could +not be found, respectively. + #15472: Error messages that print ino_t values are no longer truncated on platforms with 64-bit ino_t. diff --git a/find/defs.h b/find/defs.h index 1708d83..6dd4a30 100644 --- a/find/defs.h +++ b/find/defs.h @@ -462,7 +462,7 @@ PREDICATEFUNCTION pred_xtype; -int launch PARAMS((const struct buildcmd_control *ctl, +int launch PARAMS((struct buildcmd_control *ctl, struct buildcmd_state *buildstate)); diff --git a/find/parser.c b/find/parser.c index 246c4ce..d0264d5 100644 --- a/find/parser.c +++ b/find/parser.c @@ -3125,7 +3125,8 @@ new_insert_exec_ok (const char *action, * export its own environment variables before calling something * else. */ - bcstatus = bc_init_controlinfo(&execp->ctl, 2048u); + bcstatus = bc_init_controlinfo(&execp->ctl, 2048u, + our_pred->args.exec_vec.multiple); switch (bcstatus) { case BC_INIT_ENV_TOO_BIG: diff --git a/find/pred.c b/find/pred.c index d758276..747a3c8 100644 --- a/find/pred.c +++ b/find/pred.c @@ -1842,37 +1842,26 @@ pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ return (pred_type (pathname, &sbuf, pred_ptr)); } -/* 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. */ +struct child_prep_options +{ + boolean close_stdin; + int dir_fd; +}; -static boolean -prep_child_for_exec (boolean close_stdin, int dir_fd) +static int +prep_child_for_exec (void * usercontext) { - boolean ok = true; - if (close_stdin) + const struct exec_val* execp = usercontext; + int ok = 1; + if (execp->close_stdin) { const char inputfile[] = "/dev/null"; if (close(0) < 0) { error(0, errno, _("Cannot close standard input")); - ok = false; + ok = 0; } else { @@ -1888,7 +1877,7 @@ prep_child_for_exec (boolean close_stdin, int dir_fd) * 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. */ + /* do not set ok=0, it is OK to continue anyway. */ } } } @@ -1898,31 +1887,29 @@ prep_child_for_exec (boolean close_stdin, int dir_fd) * announcement of a call to stat() anyway, as we're about to exec * something. */ - if (dir_fd != AT_FDCWD) + if (execp->dir_fd != AT_FDCWD) { - assert (dir_fd >= 0); - if (0 != fchdir(dir_fd)) + assert (execp->dir_fd >= 0); + if (0 != fchdir (execp->dir_fd)) { /* If we cannot execute our command in the correct directory, * we should not execute it at all. */ error(0, errno, _("Failed to change directory")); - ok = false; + ok = 0; } } return ok; } - - - + int -launch (const struct buildcmd_control *ctl, +launch (struct buildcmd_control *ctl, struct buildcmd_state *buildstate) { - int wait_status; + int wait_status, child_err, parent_err; pid_t child_pid; static int first_time = 1; - const struct exec_val *execp = buildstate->usercontext; + struct exec_val *execp = buildstate->usercontext; if (!execp->use_current_dir) { @@ -1945,76 +1932,67 @@ launch (const struct buildcmd_control *ctl, signal (SIGCHLD, SIG_DFL); } - child_pid = fork (); - if (child_pid == -1) - error (1, errno, _("cannot fork")); - if (child_pid == 0) + if (bc_spawn (buildstate->cmd_argv[0], buildstate->cmd_argv, execp, + prep_child_for_exec, + &child_pid, &child_err, &parent_err)) { - /* We are the child. */ - assert (starting_desc >= 0); - if (!prep_child_for_exec(execp->close_stdin, execp->dir_fd)) + bc_clear_args(ctl, buildstate); + + while (waitpid (child_pid, &wait_status, 0) == (pid_t) -1) { - _exit(1); + if (errno != EINTR) + { + error (0, errno, _("error waiting for %s"), + safely_quote_err_filename(0, buildstate->cmd_argv[0])); + state.exit_status = 1; + return 0; /* FAIL */ + } } - - execvp (buildstate->cmd_argv[0], buildstate->cmd_argv); - error (0, errno, "%s", - safely_quote_err_filename(0, buildstate->cmd_argv[0])); - _exit (1); - } - - - /* In parent; set up for next time. */ - bc_clear_args(ctl, buildstate); - - while (waitpid (child_pid, &wait_status, 0) == (pid_t) -1) - { - if (errno != EINTR) + /* If the invoked command fails, + * -exec \; just returns false if the invoked command fails. + * -exec {} + returns true if the invoked command fails, but + * sets the program exit status. + */ + if (WIFSIGNALED (wait_status)) { - error (0, errno, _("error waiting for %s"), - safely_quote_err_filename(0, buildstate->cmd_argv[0])); - state.exit_status = 1; - return 0; /* FAIL */ + error (0, 0, _("%s terminated by signal %d"), + quotearg_n_style(0, options.err_quoting_style, + buildstate->cmd_argv[0]), + WTERMSIG (wait_status)); + + if (execp->multiple) + state.exit_status = 1; + else + return 0; /* FAIL */ } - } - - if (WIFSIGNALED (wait_status)) - { - error (0, 0, _("%s terminated by signal %d"), - quotearg_n_style(0, options.err_quoting_style, - buildstate->cmd_argv[0]), - WTERMSIG (wait_status)); - - if (execp->multiple) + else if (0 == WEXITSTATUS (wait_status)) { - /* -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 */ + } + else + { + /* nonzero child status. */ + if (execp->multiple) + state.exit_status = 1; + else + return 0; /* FAIL */ } - - return 1; /* OK */ - } - - if (0 == WEXITSTATUS (wait_status)) - { - return 1; /* OK */ } else { + assert (child_err || parent_err); + if (child_err) + errno = child_err; + else + errno = parent_err; + error (0, errno, "%s", safely_quote_err_filename(0, buildstate->cmd_argv[0])); 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 0; /* FAIL */ + state.exit_status = 1; + else + return 0; /* FAIL */ } - + return 1; /* Success. */ } diff --git a/find/util.c b/find/util.c index 27db863..e63544a 100644 --- a/find/util.c +++ b/find/util.c @@ -311,7 +311,7 @@ do_complete_pending_execdirs(struct predicate *p, int dir_fd) if (execp->state.todo) { /* There are not-yet-executed arguments. */ - launch (&execp->ctl, &execp->state); + bc_do_exec (&execp->ctl, &execp->state, 1); } } } @@ -359,7 +359,7 @@ complete_pending_execs(struct predicate *p) if (execp->state.todo) { /* There are not-yet-executed arguments. */ - launch (&execp->ctl, &execp->state); + bc_do_exec (&execp->ctl, &execp->state, 1); } } diff --git a/import-gnulib.config b/import-gnulib.config index 287c2d8..9f163e9 100644 --- a/import-gnulib.config +++ b/import-gnulib.config @@ -92,3 +92,4 @@ xstrtol xstrtoumax yesno ' + diff --git a/lib/buildcmd.c b/lib/buildcmd.c index 6c6a9c4..b5afc49 100644 --- a/lib/buildcmd.c +++ b/lib/buildcmd.c @@ -16,29 +16,6 @@ along with this program. If not, see . */ -/* - XXX_SOC: - - One of the aspects of the SOC project is to adapt this module. - This module currently makes an initial guess at two things: - - buildcmd_control->arg_max (The most characters we can fit in) - buildcmd_control->max_arg_count (most args) - - The nature of the SOC task is to adjust these values when exec fails. - Optionally (if we have the time) we can make the software adjust them - when exec succeeds. If we do the latter, we need to ensure we don't - get into some state where we are sitting just below the limit and - keep trying to extend, because that would lead to every other exec - failing. - - If our initial guess is successful, there is no pressing need really to - increase our guess. Indeed, if we are beign called by xargs (as opposed - to find) th user may have specified a limit with "-s" and we should not - exceed it. -*/ - - #include # ifndef PARAMS @@ -50,6 +27,8 @@ # endif #include +#include +#include #if DO_MULTIBYTE @@ -93,6 +72,7 @@ */ /* for sysconf() */ #include +#include /* fcntl, FD_CLOEXEC */ #include @@ -109,6 +89,7 @@ #include #include +#include "wait.h" #include "buildcmd.h" @@ -128,7 +109,7 @@ extern char **environ; Those restrictions do not exist here. */ void -bc_do_insert (const struct buildcmd_control *ctl, +bc_do_insert (struct buildcmd_control *ctl, struct buildcmd_state *state, char *arg, size_t arglen, const char *prefix, size_t pfxlen, @@ -203,29 +184,186 @@ bc_do_insert (const struct buildcmd_control *ctl, initial_args); } -static -void do_exec(const struct buildcmd_control *ctl, - struct buildcmd_state *state) +/* get the length of a zero-terminated string array */ +static unsigned int +get_stringv_len(char** sv) { - /* XXX_SOC: - - Here we are calling the user's function. Currently there is no - way for it to report that the argument list was too long. We - should introduce an externally callable function that allows them - to report this. - - If the callee does report that the exec failed, we need to retry - the exec with a shorter argument list. Once we have reduced the - argument list to the point where the exec can succeed, we need to - preserve the list of arguments we couldn't exec this time. - - This also means that the control argument here probably needs not - to be const (since the limits are in the control arg). - - The caller's only requirement on do_exec is that it should - free up enough room for at least one argument. - */ - (ctl->exec_callback)(ctl, state); + char** p = sv; + + while (*p++); + + return (p - sv - 1); +} + +static int +invoke_exec_callback(struct buildcmd_control *ctl, + struct buildcmd_state *state, + int pos) +{ + int r; + char *saved_arg = state->cmd_argv[pos]; + int saved_argc = state->cmd_argc; + + state->cmd_argv[pos] = NULL; + r = ctl->exec_callback (ctl, state); + + state->cmd_argv[pos] = saved_arg; + state->cmd_argc = saved_argc; + return r; +} + +static int +strategy_on_fail (struct buildcmd_control *ctl, + struct buildcmd_state *state, + int curr_pos) +{ + return curr_pos - 1; +} + + +static void +strategy_on_success (struct buildcmd_control *ctl, + struct buildcmd_state *state, + int curr_pos) +{ + /*nothing*/ +} + +static void +downshift (char **dest, int offset, int count) +{ + int i; + assert (offset >= 0); + for (i=0; icmd_argc; i++) + { + result += strlen (state->cmd_argv[i]); + ++result; + } + state->cmd_argv_chars = result; +} + + +static void +terminate_arglist(struct buildcmd_control *ctl, + struct buildcmd_state *state) +{ + bc_push_arg (ctl, state, + (char *) NULL, 0, + NULL, 0, + false); /* Null terminate the arg list. */ +} + +#define TRACE 0 +static void +dump_args (const struct buildcmd_control *ctl, + const struct buildcmd_state *state, + int marker) +{ +#if TRACE + int i=0; + fprintf (stderr, "pos=%d, cmd_argc=%d\n", + marker, state->cmd_argc); + for (i=0; icmd_argc; i++) + { + const char *label = "*"; + if (i < ctl->initial_argc) + label = "i"; + fprintf (stderr, + "%02d: %s%s %s\n", + i, label, + (i == marker)? ">" : " ", + state->cmd_argv[i] ? + state->cmd_argv[i] : "(null)"); + } +#endif +} + +void +bc_do_exec(struct buildcmd_control *ctl, + struct buildcmd_state *state, + int all) +{ + int pos; +#if TRACE + fprintf (stderr, "==> initial_argc=%d, cmd_initial_argv_chars=%lu\n", + ctl->initial_argc, + (unsigned long) state->cmd_initial_argv_chars); +#endif + terminate_arglist (ctl, state); + while (state->cmd_argc > ctl->initial_argc) + { + pos = state->cmd_argc; /* Initially attempt the whole command line. */ + while (pos > ctl->initial_argc) + { + /* Try executing the first POS arguments. */ + int r; + +#if TRACE + fprintf (stderr, "-> initial_argc=%d\n", + ctl->initial_argc); + dump_args (ctl, state, pos); +#endif + assert (state->cmd_argc > 0); + + if (invoke_exec_callback (ctl, state, pos)) + { + strategy_on_success (ctl, state, pos); + + /* Success. Shift any remaining unused arguments down. */ + downshift (state->cmd_argv + ctl->initial_argc, + pos - ctl->initial_argc, + state->cmd_argc - pos); + state->cmd_argc -= (pos - ctl->initial_argc); +#if TRACE + fprintf (stderr, "-> new cmd_argc is %d (initial_argc=%d)\n", + state->cmd_argc, ctl->initial_argc); +#endif + recompute_argv_chars (state); + /* break out of the inner loop, resetting pos. */ + break; + } + else + { + if ((E2BIG != errno) || !ctl->small_exec_allowed) + { + error (1, errno, _("exec failed")); + } + else + { + int new_pos; + new_pos = strategy_on_fail (ctl, state, pos); + assert (new_pos > 0); + + if (new_pos <= ctl->initial_argc) + { + error (1, 0, + _("Command-line argument length %lu of " + "the first %d arguments exceeds the system limit"), + (unsigned long) state->cmd_argv_chars, ctl->initial_argc); + } + assert (new_pos < pos); + pos = new_pos; + } + } + } + if (!all) + { + /* We did at least one. */ + break; + } + } + bc_clear_args(ctl, state); } @@ -254,6 +392,7 @@ bc_argc_limit_reached(int initial_args, } + /* Add ARG to the end of the list of arguments `cmd_argv' to pass to the command. LEN is the length of ARG, including the terminating null. @@ -265,7 +404,7 @@ bc_argc_limit_reached(int initial_args, * for greater clarity. */ void -bc_push_arg (const struct buildcmd_control *ctl, +bc_push_arg (struct buildcmd_control *ctl, struct buildcmd_state *state, const char *arg, size_t len, const char *prefix, size_t pfxlen, @@ -278,11 +417,6 @@ bc_push_arg (const struct buildcmd_control *ctl, if (arg) { - /* XXX_SOC: if do_exec() is only guaranteeed to free up one - * argument, this if statement may need to become a while loop. - * If it becomes a while loop, it needs not to be an infinite - * loop... - */ if (state->cmd_argv_chars + len > ctl->arg_max) { if (initial_args || state->cmd_argc == ctl->initial_argc) @@ -292,17 +426,10 @@ bc_push_arg (const struct buildcmd_control *ctl, || (ctl->exit_if_size_exceeded && (ctl->lines_per_exec || ctl->args_per_exec))) error (1, 0, _("argument list too long")); - do_exec (ctl, state); + bc_do_exec (ctl, state, false); } - /* XXX_SOC: this if may also need to become a while loop. In - fact perhaps it is best to factor this out into a separate - function which ceeps calling the exec handler until there is - space for our next argument. Each exec will free one argc - "slot" so the main thing to worry about repeated exec calls - for would be total argument length. - */ if (bc_argc_limit_reached(initial_args, ctl, state)) - do_exec (ctl, state); + bc_do_exec (ctl, state, false); } if (state->cmd_argc >= state->cmd_argv_alloc) @@ -341,7 +468,7 @@ bc_push_arg (const struct buildcmd_control *ctl, * actually calls bc_push_arg(ctl, state, NULL, 0, false). */ if (bc_argc_limit_reached(initial_args, ctl, state)) - do_exec (ctl, state); + bc_do_exec (ctl, state, false); } /* If this is an initial argument, set the high-water mark. */ @@ -420,7 +547,7 @@ bc_get_arg_max(void) } -static int cb_exec_noop(const struct buildcmd_control *ctl, +static int cb_exec_noop(struct buildcmd_control *ctl, struct buildcmd_state *state) { /* does nothing. */ @@ -447,7 +574,8 @@ bc_size_of_environment (void) enum BC_INIT_STATUS bc_init_controlinfo(struct buildcmd_control *ctl, - size_t headroom) + size_t headroom, + int small_exec_allowed) { size_t size_of_environment = bc_size_of_environment(); @@ -495,6 +623,11 @@ bc_init_controlinfo(struct buildcmd_control *ctl, */ ctl->arg_max = ctl->posix_arg_size_max; + /* When small_exec_allowed is nonzero, we may just use + * the first part of the argument list when exec fails + * due to size limits. + */ + ctl->small_exec_allowed = small_exec_allowed; return BC_INIT_OK; } @@ -552,3 +685,130 @@ bc_clear_args(const struct buildcmd_control *ctl, state->todo = 0; state->dir_fd = -1; } + + +/* Fork the process and exec a child program PROG, with arguments ARGV. + * The hook PREPARE_CHILD is called in the child after a successful fork + * and before the exec is attempted. + * + * If something goes wrong, zero is returned. If the problem is in the + * parent, *PARENT_ERR is set to the parent's errno value. If the problem + * is in the child, *CHILD_ERR is set to the child's errno value. + * + * If the exec succeeded, the child's process ID is stored at *CHILD + * and a non-zero value is returned. + * + * If the exec failed, the defunct child is reaped, and the errno value + * from the failed exec is stored at *CHILD_ERR. The return value is zero. + * + * If the PREPARE_CHILD function returns zero, the exec is not attempted. + * If PREPARE_CHILD also sets errno, that value will be stored in *CHILD_ERR. + */ +int +bc_spawn (const char *prog, char *const argv[], void *usercontext, + int (*prepare_child)(void *), + pid_t *child, int *child_err, int *parent_err) +{ + int fd[2]; + int r; + int retval = 0; + + *child_err = 0; + *parent_err = 0; + + if (pipe (fd)) + { + *parent_err = errno; + return 0; + } + if (fcntl (fd[1], F_SETFD, FD_CLOEXEC)) + { + *parent_err = errno; + return 0; + } + + + *child = fork (); + switch (*child) + { + case -1: /* fork failed. */ + close (fd[1]); + break; + + case 0: /* Child. */ + { + close (fd[0]); + + errno = 0; + if (prepare_child (usercontext)) + { + execvp (prog, argv); + } + + if (errno) + { + /* Write errno value to parent. We do this even if + * the error was not E2BIG, because we need to + * distinguish successful execs from unsuccessful + * ones. + */ + write(fd[1], &errno, sizeof(int)); + } + + close (fd[1]); + /* + * Terminate the chid process with _exit() instead of exit() in order + * to avoid calling the parent's atexit functions. + */ + _exit (2); + /*NOTREACHED*/ + } /* child */ + + default: + /* Parent; close the write end of the pipe. */ + close (fd[1]); + break; + } /* switch(child) */ + + /* We are executing in the parent. */ + switch (r = read (fd[0], child_err, sizeof(int))) + { + case 0: + { + /* No data available from pipe; the exec must have + * succeeded (closing the close-on-exec file descriptor + * for the write end of the pipe). + */ + retval = 1; /* success */ + break; + } + + case -1: + { + /* The child exists but we failed to read from the pipe. */ + close (fd[0]); + error (0, errno, "errno-buffer read failed in xargs_do_exec"); + break; + } + + default: + { + /* Failure */ + int childstatus; + + close (fd[0]); + + /* we know the child is about to exit, so wait for that. + * We have to do this so that wait_for_proc() does not + * change the value of child_error on the basis of the + * return value -- since in this case we did not launch + * the utility. + */ + waitpid (*child, &childstatus, 0); + break; + } + + } /* switch on bytes read */ + close (fd[0]); + return retval; +} diff --git a/lib/buildcmd.h b/lib/buildcmd.h index e295852..ea2a989 100644 --- a/lib/buildcmd.h +++ b/lib/buildcmd.h @@ -22,7 +22,7 @@ struct buildcmd_state { - /* Number of valid elements in `cmd_argv'. */ + /* Number of valid elements in `cmd_argv', including terminating NULL. */ int cmd_argc; /* 0 */ /* The list of args being built. */ @@ -87,7 +87,7 @@ struct buildcmd_control int initial_argc; /* 0 */ /* exec callback. */ - int (*exec_callback)(const struct buildcmd_control *, struct buildcmd_state *); + int (*exec_callback)(struct buildcmd_control *, struct buildcmd_state *); /* If nonzero, the maximum number of nonblank lines from stdin to use per command line. */ @@ -95,6 +95,11 @@ struct buildcmd_control /* The maximum number of arguments to use per command line. */ long args_per_exec; + + /* If true, we are allowed to execute the assembled argument list in chunks (for + * example if it exceeds ARG_MAX or some system limit) + */ + int small_exec_allowed; /* 1 */ }; enum BC_INIT_STATUS @@ -107,14 +112,18 @@ enum BC_INIT_STATUS extern size_t bc_size_of_environment (void); -extern void bc_do_insert (const struct buildcmd_control *ctl, +extern void bc_do_insert (struct buildcmd_control *ctl, struct buildcmd_state *state, char *arg, size_t arglen, const char *prefix, size_t pfxlen, const char *linebuf, size_t lblen, int initial_args); -extern void bc_push_arg (const struct buildcmd_control *ctl, +extern void bc_do_exec (struct buildcmd_control *ctl, + struct buildcmd_state *state, + int all); + +extern void bc_push_arg (struct buildcmd_control *ctl, struct buildcmd_state *state, const char *arg, size_t len, const char *prefix, size_t pfxlen, @@ -124,11 +133,16 @@ extern void bc_init_state(const struct buildcmd_control *ctl, struct buildcmd_state *state, void *usercontext); extern enum BC_INIT_STATUS bc_init_controlinfo(struct buildcmd_control *ctl, - size_t arglen_headroom); + size_t arglen_headroom, + int small_exec_allowed); extern size_t bc_get_arg_max(void); extern void bc_use_sensible_arg_max(struct buildcmd_control *ctl); extern void bc_clear_args(const struct buildcmd_control *ctl, struct buildcmd_state *state); +extern int bc_spawn (const char *prog, char * const argv[], void *usercontext, + int (*prepare_child)(void*), + pid_t *child, + int *child_err, int *parent_err); #endif diff --git a/xargs/xargs.1 b/xargs/xargs.1 index 413b55e..8f3811f 100644 --- a/xargs/xargs.1 +++ b/xargs/xargs.1 @@ -246,6 +246,8 @@ and is calculated as the argument length limit for exec, less the size of your environment, less 2048 bytes of headroom. If this value is more than 128KiB, 128Kib is used as the default value; otherwise, the default value is the maximum. 1KiB is 1024 bytes. +.B xargs +automatically adapts to tighter constraints. .TP .PD 0 .B \-\-verbose diff --git a/xargs/xargs.c b/xargs/xargs.c index 1365a71..01b9ec6 100644 --- a/xargs/xargs.c +++ b/xargs/xargs.c @@ -220,6 +220,9 @@ static pid_t *pids = NULL; /* The number of allocated elements in `pids'. */ static size_t pids_alloc = 0u; +/* Process ID of the parent xarsg process. */ +static pid_t parent; + /* Exit status; nonzero if any child process exited with a status of 1-125. */ static volatile int child_error = 0; @@ -264,7 +267,7 @@ static int read_line PARAMS ((void)); static int read_string PARAMS ((void)); static boolean print_args PARAMS ((boolean ask)); /* static void do_exec PARAMS ((void)); */ -static int xargs_do_exec (const struct buildcmd_control *cl, struct buildcmd_state *state); +static int xargs_do_exec (struct buildcmd_control *cl, struct buildcmd_state *state); static void exec_if_possible PARAMS ((void)); static void add_proc PARAMS ((pid_t pid)); static void wait_for_proc PARAMS ((boolean all, unsigned int minreap)); @@ -413,6 +416,7 @@ main (int argc, char **argv) enum { XARGS_POSIX_HEADROOM = 2048u }; program_name = argv[0]; + parent = getpid(); original_exit_value = 0; #ifdef HAVE_SETLOCALE @@ -423,11 +427,7 @@ main (int argc, char **argv) atexit (close_stdin); atexit (wait_for_proc_all); - /* xargs is required by POSIX to allow 2048 bytes of headroom - * for extra environment variables (that perhaps the utliity might - * want to set before execing something else). - */ - bcstatus = bc_init_controlinfo(&bc_ctl, XARGS_POSIX_HEADROOM); + bcstatus = bc_init_controlinfo(&bc_ctl, XARGS_POSIX_HEADROOM, 1); /* The bc_init_controlinfo call may have determined that the * environment is too big. In that case, we will fail with @@ -443,27 +443,13 @@ main (int argc, char **argv) { act_on_init_result = fail_due_to_env_size; } - else if (BC_INIT_CANNOT_ACCOMODATE_HEADROOM == bcstatus) - { - /* All POSIX systems are required to support ARG_MAX of at least - * 4096. For everything to work the total of (command line + - * headroom + environment) must fit into this. POSIX requires - * that we use a headroom of 2048 bytes. The user is in control - * of the size of the environment. - * - * In general if bc_init_controlinfo() returns - * BC_INIT_CANNOT_ACCOMODATE_HEADROOM, its caller can try again - * with a smaller headroom. However, in the case of xargs, this - * would not be POSIX-compliant. - */ - act_on_init_result = fail_due_to_env_size; - } else { /* IEEE Std 1003.1, 2003 specifies that the combined argument and * environment list shall not exceed {ARG_MAX}-2048 bytes. It also * specifies that it shall be at least LINE_MAX. */ + assert(bc_ctl.arg_max <= (ARG_MAX-2048)); #ifdef _SC_ARG_MAX long val = sysconf(_SC_ARG_MAX); if (val > 0) @@ -629,6 +615,17 @@ main (int argc, char **argv) } } + /* For xargs -I we can't just execute the first part of the + * argument list. We don't initialise the small_exec_allowed + * field when we see -i, because the effect of that option + * needs to be reversed by -n. + */ + if (bc_ctl.replace_pat) + { + bc_ctl.small_exec_allowed = 0; + } + + /* If we had deferred failing due to problems in bc_init_controlinfo(), * do it now. * @@ -689,12 +686,10 @@ main (int argc, char **argv) _("Your environment variables take up %lu bytes\n"), (unsigned long)bc_size_of_environment()); fprintf(stderr, - _("POSIX upper limit on argument length (this system): %lu\n"), + _("POSIX lower and upper limits on argument length: %lu, %lu\n"), + (unsigned long)bc_ctl.posix_arg_size_min, (unsigned long)bc_ctl.posix_arg_size_max); fprintf(stderr, - _("POSIX smallest allowable upper limit on argument length (all systems): %lu\n"), - (unsigned long)bc_ctl.posix_arg_size_min); - fprintf(stderr, _("Maximum length of command we could actually use: %ld\n"), (unsigned long)(bc_ctl.posix_arg_size_max - bc_size_of_environment())); @@ -737,19 +732,21 @@ main (int argc, char **argv) initial_args = false; bc_ctl.initial_argc = bc_state.cmd_argc; bc_state.cmd_initial_argv_chars = bc_state.cmd_argv_chars; + bc_ctl.initial_argc = bc_state.cmd_argc; + /*fprintf(stderr, "setting initial_argc=%d\n", bc_state.cmd_initial_argc);*/ while ((*read_args) () != -1) if (bc_ctl.lines_per_exec && lineno >= bc_ctl.lines_per_exec) { - xargs_do_exec (&bc_ctl, &bc_state); + bc_do_exec (&bc_ctl, &bc_state, 0); lineno = 0; } /* SYSV xargs seems to do at least one exec, even if the input is empty. */ if (bc_state.cmd_argc != bc_ctl.initial_argc - || (always_run_command && !procs_executed)) - xargs_do_exec (&bc_ctl, &bc_state); + || (always_run_command && procs_executed==0)) + bc_do_exec (&bc_ctl, &bc_state, 1); } else @@ -780,7 +777,7 @@ main (int argc, char **argv) NULL, 0, linebuf, len, initial_args); - xargs_do_exec (&bc_ctl, &bc_state); + bc_do_exec (&bc_ctl, &bc_state, 1); } } @@ -1066,16 +1063,17 @@ print_args (boolean ask) /* Close stdin and attach /dev/null to it. * This resolves Savannah bug #3992. */ -static void -prep_child_for_exec (void) +static int +prep_child_for_exec (void *context) { if (!keep_stdin) { const char inputfile[] = "/dev/null"; - /* fprintf(stderr, "attaching stdin to /dev/null\n"); */ - close(0); - if (open(inputfile, O_RDONLY) < 0) + if (close (0)) + return 0; + + if (open (inputfile, O_RDONLY) < 0) { /* This is not entirely fatal, since * executing the child with a closed @@ -1085,72 +1083,117 @@ prep_child_for_exec (void) error (0, errno, "%s", quotearg_n_style(0, locale_quoting_style, inputfile)); } } + return 1; } /* Execute the command that has been built in `cmd_argv'. This may involve - waiting for processes that were previously executed. */ - + waiting for processes that were previously executed. +*/ static int -xargs_do_exec (const struct buildcmd_control *ctl, struct buildcmd_state *state) +xargs_do_exec (struct buildcmd_control *ctl, struct buildcmd_state *state) { pid_t child; + int r, child_err, parent_err; + unsigned int min_wait; (void) ctl; (void) state; - bc_push_arg (&bc_ctl, &bc_state, - (char *) NULL, 0, - NULL, 0, - false); /* Null terminate the arg list. */ - - if (!query_before_executing || print_args (true)) + if (proc_max) + { + if (procs_executing >= proc_max) + wait_for_proc (false, proc_max - procs_executing + 1); + assert (procs_executing < proc_max); + } + + if (query_before_executing) { - if (proc_max) + if (!print_args (true)) { - if (procs_executing >= proc_max) - wait_for_proc (false, proc_max - procs_executing + 1); - assert (procs_executing < proc_max); + /* user did not want to execute this particular command, + * but we did not fail. + */ + return 1; } - if (!query_before_executing && print_command) - print_args (false); - + } + else if (print_command) + { + print_args (false); + } + + min_wait = 0u; + do + { /* Before forking, reap any already-exited child. We do this so that we don't leave unreaped children around while we build a new command line. For example this command will spend most of its time waiting for sufficient arguments to launch another command line: - + seq 1 1000 | fmt | while read x ; do echo $x; sleep 1 ; done | ./xargs -P 200 -n 20 sh -c 'echo "$@"; sleep $((1 + $RANDOM % 5))' sleeper */ - wait_for_proc (false, 0u); - - /* If we run out of processes, wait for a child to return and - try again. */ - while ((child = fork ()) < 0 && errno == EAGAIN && procs_executing) - wait_for_proc (false, 1u); + wait_for_proc (false, min_wait); - switch (child) + /* We need to be careful to distinguish successful execs from + * unsuccessful ones. + * + * If an exec ewas successful, the return code of the program + * must be reflected in child_error. + */ + if (bc_spawn (bc_state.cmd_argv[0], bc_state.cmd_argv, bc_state.usercontext, + prep_child_for_exec, &child, &child_err, &parent_err)) { - case -1: - error (1, errno, _("cannot fork")); - - case 0: /* Child. */ - prep_child_for_exec(); - execvp (bc_state.cmd_argv[0], bc_state.cmd_argv); - error (0, errno, "%s", bc_state.cmd_argv[0]); - _exit (errno == ENOENT ? 127 : 126); - /*NOTREACHED*/ + add_proc (child); + return 1; } - add_proc (child); - } + else + { + if (parent_err) + { + /* Typically this means that we failed to set up the pipe. + * It's not that we can't run the program, it's that we cannot + * get far enough throught the process of arranging to execute it. + */ + error (1, parent_err, _("failed to invoke program %s"), + bc_state.cmd_argv[0]); + } + else if (child_err == EAGAIN) + { + min_wait = 1u; /* Try again */ + } + else if (child_err == E2BIG) + { + errno = child_err; + return 0; /* arg list too big */ + } + else if (child_err == EMFILE || child_err == ENFILE) + { + /* Likely a failure in prep_child_for_exec(), though it + * could I suppose be a result of there being no free FDs + * to complete an exec call on some systems (in which case + * this error message is misleading). + */ + error (1, child_err, "failed to redirect stdin"); + } + else if (child_err == ENOENT || child_err == ELOOP || child_err == ENOTDIR) + { + error (127, child_err, _("failed to find program %s"), + bc_state.cmd_argv[0]); + } + else + { + error (126, child_err, _("failed to run program %s"), + bc_state.cmd_argv[0]); + } + } + } while (errno == EAGAIN && procs_executing); - bc_clear_args(&bc_ctl, &bc_state); - return 1; /* Success */ + return 0; /* Failure */ } -/* Execute the command if possible. */ +/* Execute the command if possible, using up all built-up args. */ static void exec_if_possible (void) @@ -1158,7 +1201,7 @@ exec_if_possible (void) if (bc_ctl.replace_pat || initial_args || bc_state.cmd_argc == bc_ctl.initial_argc || bc_ctl.exit_if_size_exceeded) return; - xargs_do_exec (&bc_ctl, &bc_state); + bc_do_exec (&bc_ctl, &bc_state, 1); } /* Add the process with id PID to the list of processes that have @@ -1269,8 +1312,6 @@ wait_for_proc (boolean all, unsigned int minreap) procs_executing--; reaped++; - if (WEXITSTATUS (status) == 126 || WEXITSTATUS (status) == 127) - exit (WEXITSTATUS (status)); /* Can't find or run the command. */ if (WEXITSTATUS (status) == 255) error (124, 0, _("%s: exited with status 255; aborting"), bc_state.cmd_argv[0]); if (WIFSTOPPED (status)) @@ -1289,6 +1330,14 @@ wait_for_proc_all (void) { static boolean waiting = false; + /* This function was registered by the original, parent, process. + * The child processes must not call exit() to terminate, since this + * will mean that their exit status will be inappropriately changed. + * Instead the child processes should call _exit(). If someone + * forgets this rule, you may see the following assert() fail. + */ + assert(getpid() == parent); + if (waiting) return;