xargs: Before exiting, wait for all invocations of the utility.

This only has an effect with -P, otherwise errors are only detected when the
utility is not running.

Submitted by:	Matthew Story
This commit is contained in:
jilles 2012-03-16 16:41:28 +00:00
parent c54c4cd884
commit b984bc44ed
2 changed files with 81 additions and 45 deletions

View File

@ -33,7 +33,7 @@
.\" $FreeBSD$
.\" $xMach: xargs.1,v 1.2 2002/02/23 05:23:37 tim Exp $
.\"
.Dd March 24, 2011
.Dd March 16, 2012
.Dt XARGS 1
.Os
.Sh NAME
@ -294,17 +294,17 @@ Undefined behavior may occur if
.Ar utility
reads from the standard input.
.Pp
The
.Nm
utility exits immediately (without processing any further input) if a
command line cannot be assembled,
.Ar utility
cannot be invoked, an invocation of
If a command line cannot be assembled, or
cannot be invoked, or if an invocation of
.Ar utility
is terminated by a signal,
or an invocation of
.Ar utility
exits with a value of 255.
exits with a value of 255, the
.Nm
utility stops processing input and exits after all invocations of
.Ar utility
finish processing.
.Sh EXIT STATUS
The
.Nm

View File

@ -70,6 +70,7 @@ static void run(char **);
static void usage(void);
void strnsubst(char **, const char *, const char *, size_t);
static pid_t xwait(int block, int *status);
static void xexit(const char *, const int);
static void waitchildren(const char *, int);
static void pids_init(void);
static int pids_empty(void);
@ -280,10 +281,8 @@ parse_input(int argc, char *argv[])
switch (ch = getchar()) {
case EOF:
/* No arguments since last exec. */
if (p == bbp) {
waitchildren(*av, 1);
exit(rval);
}
if (p == bbp)
xexit(*av, rval);
goto arg1;
case ' ':
case '\t':
@ -308,8 +307,10 @@ parse_input(int argc, char *argv[])
count++; /* Indicate end-of-line (used by -L) */
/* Quotes do not escape newlines. */
arg1: if (insingle || indouble)
errx(1, "unterminated quote");
arg1: if (insingle || indouble) {
warnx("unterminated quote");
xexit(*av, 1);
}
arg2:
foundeof = *eofstr != '\0' &&
strncmp(argp, eofstr, p - argp) == 0;
@ -342,8 +343,10 @@ arg1: if (insingle || indouble)
*/
inpline = realloc(inpline, curlen + 2 +
strlen(argp));
if (inpline == NULL)
errx(1, "realloc failed");
if (inpline == NULL) {
warnx("realloc failed");
xexit(*av, 1);
}
if (curlen == 1)
strcpy(inpline, argp);
else
@ -360,17 +363,17 @@ arg1: if (insingle || indouble)
*/
if (xp == endxp || p > ebp || ch == EOF ||
(Lflag <= count && xflag) || foundeof) {
if (xflag && xp != endxp && p > ebp)
errx(1, "insufficient space for arguments");
if (xflag && xp != endxp && p > ebp) {
warnx("insufficient space for arguments");
xexit(*av, 1);
}
if (jfound) {
for (avj = argv; *avj; avj++)
*xp++ = *avj;
}
prerun(argc, av);
if (ch == EOF || foundeof) {
waitchildren(*av, 1);
exit(rval);
}
if (ch == EOF || foundeof)
xexit(*av, rval);
p = bbp;
xp = bxp;
count = 0;
@ -394,8 +397,10 @@ arg1: if (insingle || indouble)
if (zflag)
goto addch;
/* Backslash escapes anything, is escaped by quotes. */
if (!insingle && !indouble && (ch = getchar()) == EOF)
errx(1, "backslash at EOF");
if (!insingle && !indouble && (ch = getchar()) == EOF) {
warnx("backslash at EOF");
xexit(*av, 1);
}
/* FALLTHROUGH */
default:
addch: if (p < ebp) {
@ -404,11 +409,15 @@ addch: if (p < ebp) {
}
/* If only one argument, not enough buffer space. */
if (bxp == xp)
errx(1, "insufficient space for argument");
if (bxp == xp) {
warnx("insufficient space for argument");
xexit(*av, 1);
}
/* Didn't hit argument limit, so if xflag object. */
if (xflag)
errx(1, "insufficient space for arguments");
if (xflag) {
warnx("insufficient space for arguments");
xexit(*av, 1);
}
if (jfound) {
for (avj = argv; *avj; avj++)
@ -449,16 +458,20 @@ prerun(int argc, char *argv[])
* a NULL at the tail.
*/
tmp = malloc((argc + 1) * sizeof(char**));
if (tmp == NULL)
errx(1, "malloc failed");
if (tmp == NULL) {
warnx("malloc failed");
xexit(*argv, 1);
}
tmp2 = tmp;
/*
* Save the first argument and iterate over it, we
* cannot do strnsubst() to it.
*/
if ((*tmp++ = strdup(*avj++)) == NULL)
errx(1, "strdup failed");
if ((*tmp++ = strdup(*avj++)) == NULL) {
warnx("strdup failed");
xexit(*argv, 1);
}
/*
* For each argument to utility, if we have not used up
@ -475,8 +488,10 @@ prerun(int argc, char *argv[])
if (repls > 0)
repls--;
} else {
if ((*tmp = strdup(*tmp)) == NULL)
errx(1, "strdup failed");
if ((*tmp = strdup(*tmp)) == NULL) {
warnx("strdup failed");
xexit(*argv, 1);
}
tmp++;
}
}
@ -547,7 +562,8 @@ run(char **argv)
childerr = 0;
switch (pid = vfork()) {
case -1:
err(1, "vfork");
warn("vfork");
xexit(*argv, 1);
case 0:
if (oflag) {
if ((fd = open(_PATH_TTY, O_RDONLY)) == -1)
@ -592,27 +608,47 @@ xwait(int block, int *status) {
return (pid);
}
static void
xexit(const char *name, const int exit_code) {
waitchildren(name, 1);
exit(exit_code);
}
static void
waitchildren(const char *name, int waitall)
{
pid_t pid;
int status;
int cause_exit = 0;
while ((pid = xwait(waitall || pids_full(), &status)) > 0) {
/* If we couldn't invoke the utility, exit. */
if (childerr != 0) {
/*
* If we couldn't invoke the utility or if utility exited
* because of a signal or with a value of 255, warn (per
* POSIX), and then wait until all other children have
* exited before exiting 1-125. POSIX requires us to stop
* reading if child exits because of a signal or with 255,
* but it does not require us to exit immediately; waiting
* is preferable to orphaning.
*/
if (childerr != 0 && cause_exit == 0) {
errno = childerr;
err(errno == ENOENT ? 127 : 126, "%s", name);
}
if (WIFSIGNALED(status))
errx(1, "%s: terminated with signal %d; aborting",
waitall = 1;
cause_exit = ENOENT ? 127 : 126;
warn("%s", name);
} else if (WIFSIGNALED(status)) {
waitall = cause_exit = 1;
warnx("%s: terminated with signal %d; aborting",
name, WTERMSIG(status));
if (WEXITSTATUS(status) == 255)
errx(1, "%s: exited with status 255; aborting", name);
if (WEXITSTATUS(status))
rval = 1;
} else if (WEXITSTATUS(status) == 255) {
waitall = cause_exit = 1;
warnx("%s: exited with status 255; aborting", name);
} else if (WEXITSTATUS(status))
rval = 1;
}
if (cause_exit)
exit(cause_exit);
if (pid == -1 && errno != ECHILD)
err(1, "waitpid");
}