From c1daf59b444a9e425e919c0f7b9d15c0a8ee0dbc Mon Sep 17 00:00:00 2001 From: mckay Date: Sun, 12 Feb 2012 05:01:49 +0000 Subject: [PATCH] MFC r215615, r215642: Fix several misbehaviours by making xargs keep track of its child processes. git-svn-id: svn://svn.freebsd.org/base/stable/8@231542 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- usr.bin/xargs/xargs.c | 123 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 5 deletions(-) diff --git a/usr.bin/xargs/xargs.c b/usr.bin/xargs/xargs.c index 27f7cd309..aa8f07be7 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(void) +{ + 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(void) +{ + return (curprocs == 0); +} + +static int +pids_full(void) +{ + 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(void) +{ + 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; } /* -- 2.45.0