diff options
author | mckay <mckay@FreeBSD.org> | 2010-11-21 10:55:16 +0000 |
---|---|---|
committer | mckay <mckay@FreeBSD.org> | 2010-11-21 10:55:16 +0000 |
commit | a8e94264ae1e9bf9fc689d5a0e157185b8deeef3 (patch) | |
tree | d22765931877bde4f68f57d7c035b23730cb7f58 /usr.bin | |
parent | e6b1bc24ddbcae9b307ca7c6b42147b8e40d7dcb (diff) | |
download | FreeBSD-src-a8e94264ae1e9bf9fc689d5a0e157185b8deeef3.zip FreeBSD-src-a8e94264ae1e9bf9fc689d5a0e157185b8deeef3.tar.gz |
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.
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/xargs/xargs.c | 123 |
1 files changed, 118 insertions, 5 deletions
diff --git a/usr.bin/xargs/xargs.c b/usr.bin/xargs/xargs.c index 27f7cd3..350a534 100644 --- a/usr.bin/xargs/xargs.c +++ b/usr.bin/xargs/xargs.c @@ -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 @@ exec: 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; } /* |