xargs can be fooled by exiting children that it did not start, causing

it to kick off a new command before the previous has finished, resulting
in corrupted (interleaved) output.  It is also fooled by non-exiting
children it did not start, failing to exit until all extraneous children
have exited.

This patch makes xargs keep track of children it starts, ignoring
pre-existing ones.
This commit is contained in:
mckay 2010-11-21 10:55:16 +00:00
parent e6b1bc24dd
commit a8e94264ae

View File

@ -73,7 +73,16 @@ static int prompt(void);
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 waitchildren(const char *, int);
static void pids_init(void);
static int pids_empty(void);
static int pids_full(void);
static void pids_add(pid_t pid);
static int pids_remove(pid_t pid);
static int findslot(pid_t pid);
static int findfreeslot(void);
static void clearslot(int slot);
static char echo[] = _PATH_ECHO;
static char **av, **bxp, **ep, **endxp, **xp;
@ -82,6 +91,7 @@ static const char *eofstr;
static int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag;
static int cnt, Iflag, jfound, Lflag, Sflag, wasquoted, xflag;
static int curprocs, maxprocs;
static pid_t *childpids;
static volatile int childerr;
@ -205,6 +215,8 @@ main(int argc, char *argv[])
if (replstr != NULL && *replstr == '\0')
errx(1, "replstr may not be empty");
pids_init();
/*
* Allocate pointers for the utility name, the utility arguments,
* the maximum arguments to be read from stdin and the trailing
@ -556,19 +568,41 @@ run(char **argv)
childerr = errno;
_exit(1);
}
curprocs++;
pids_add(pid);
waitchildren(*argv, 0);
}
/*
* Wait for a tracked child to exit and return its pid and exit status.
*
* Ignores (discards) all untracked child processes.
* Returns -1 and sets errno to ECHILD if no tracked children exist.
* If block is set, waits indefinitely for a child process to exit.
* If block is not set and no children have exited, returns 0 immediately.
*/
static pid_t
xwait(int block, int *status) {
pid_t pid;
if (pids_empty()) {
errno = ECHILD;
return -1;
}
while ((pid = waitpid(-1, status, block ? 0 : WNOHANG)) > 0)
if (pids_remove(pid))
break;
return pid;
}
static void
waitchildren(const char *name, int waitall)
{
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
WNOHANG : 0)) > 0) {
curprocs--;
while ((pid = xwait(waitall || pids_full(), &status)) > 0) {
/* If we couldn't invoke the utility, exit. */
if (childerr != 0) {
errno = childerr;
@ -583,8 +617,87 @@ waitchildren(const char *name, int waitall)
if (WEXITSTATUS(status))
rval = 1;
}
if (pid == -1 && errno != ECHILD)
err(1, "wait3");
err(1, "waitpid");
}
#define NOPID (0)
static void
pids_init()
{
int i;
if ((childpids = malloc(maxprocs * sizeof(*childpids))) == NULL)
errx(1, "malloc failed");
for (i = 0; i < maxprocs; i++)
clearslot(i);
}
static int
pids_empty()
{
return curprocs == 0;
}
static int
pids_full()
{
return curprocs >= maxprocs;
}
static void
pids_add(pid_t pid)
{
int slot;
slot = findfreeslot();
childpids[slot] = pid;
curprocs++;
}
static int
pids_remove(pid_t pid)
{
int slot;
if ((slot = findslot(pid)) < 0)
return 0;
clearslot(slot);
curprocs--;
return 1;
}
static int
findfreeslot()
{
int slot;
if ((slot = findslot(NOPID)) < 0)
errx(1, "internal error: no free pid slot");
return slot;
}
static int
findslot(pid_t pid)
{
int slot;
for (slot = 0; slot < maxprocs; slot++)
if (childpids[slot] == pid)
return slot;
return -1;
}
static void
clearslot(int slot)
{
childpids[slot] = NOPID;
}
/*