summaryrefslogtreecommitdiffstats
path: root/usr.bin/xargs
diff options
context:
space:
mode:
authormckay <mckay@FreeBSD.org>2010-11-21 10:55:16 +0000
committermckay <mckay@FreeBSD.org>2010-11-21 10:55:16 +0000
commita8e94264ae1e9bf9fc689d5a0e157185b8deeef3 (patch)
treed22765931877bde4f68f57d7c035b23730cb7f58 /usr.bin/xargs
parente6b1bc24ddbcae9b307ca7c6b42147b8e40d7dcb (diff)
downloadFreeBSD-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/xargs')
-rw-r--r--usr.bin/xargs/xargs.c123
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;
}
/*
OpenPOWER on IntegriCloud