Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 12 Feb 2012 05:01:49 +0000 (UTC)
From:      Stephen McKay <mckay@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r231542 - stable/8/usr.bin/xargs
Message-ID:  <201202120501.q1C51noc065893@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mckay
Date: Sun Feb 12 05:01:49 2012
New Revision: 231542
URL: http://svn.freebsd.org/changeset/base/231542

Log:
  MFC r215615, r215642:
  
  Fix several misbehaviours by making xargs keep track of its child processes.

Modified:
  stable/8/usr.bin/xargs/xargs.c
Directory Properties:
  stable/8/usr.bin/xargs/   (props changed)

Modified: stable/8/usr.bin/xargs/xargs.c
==============================================================================
--- stable/8/usr.bin/xargs/xargs.c	Sun Feb 12 01:07:15 2012	(r231541)
+++ stable/8/usr.bin/xargs/xargs.c	Sun Feb 12 05:01:49 2012	(r231542)
@@ -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 waita
 		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;
 }
 
 /*



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201202120501.q1C51noc065893>