Print this page
4703 would like xargs support for -P

@@ -87,10 +87,11 @@
 #define FORKFAIL        "Could not fork child"
 #define EXECFAIL        "Could not exec command"
 #define MISSQUOTE       "Missing quote"
 #define BADESCAPE       "Incomplete escape"
 #define IBUFOVERFLOW    "Insert buffer overflow"
+#define NOCHILDSLOT     "No free child slot available"
 
 #define _(x)    gettext(x)
 
 static wctype_t blank;
 static char     *arglist[MAXARGS+1];

@@ -107,10 +108,11 @@
         char    *p_skel;        /* ptr to arg template */
 } saveargv[MAXINSERTS];
 
 static int      PROMPT = -1;
 static int      BUFLIM = BUFSIZE;
+static int      MAXPROCS = 1;
 static int      N_ARGS = 0;
 static int      N_args = 0;
 static int      N_lines = 0;
 static int      DASHX = FALSE;
 static int      MORE = TRUE;

@@ -125,27 +127,35 @@
 static int      ibufsize = 0;
 static int      exitstat = 0;   /* our exit status                      */
 static int      mac;            /* modified argc, after parsing         */
 static char     **mav;          /* modified argv, after parsing         */
 static int      n_inserts;      /* # of insertions.                     */
+static pid_t    *procs;         /* pids of children                     */
+static int      n_procs;        /* # of child processes.                */
 
 /* our usage message:                                                   */
 #define USAGEMSG "Usage: xargs: [-t] [-p] [-0] [-e[eofstr]] [-E eofstr] "\
-        "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-s size] "\
+        "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-P maxprocs] [-s size] "\
         "[cmd [args ...]]\n"
 
 static int      echoargs();
 static wint_t   getwchr(char *, size_t *);
-static int      lcall(char *sub, char **subargs);
+static void     lcall(char *sub, char **subargs);
 static void     addibuf(struct inserts *p);
 static void     ermsg(char *messages, ...);
 static char     *addarg(char *arg);
 static void     store_str(char **, char *, size_t);
 static char     *getarg(char *);
 static char     *insert(char *pattern, char *subst);
 static void     usage();
 static void     parseargs();
+static void     procs_malloc(void);
+static int      procs_find(pid_t child);
+static void     procs_store(pid_t child);
+static int      procs_delete(pid_t child);
+static pid_t    procs_waitpid(int blocking, int *stat_loc);
+static void     procs_wait(int blocking);
 
 int
 main(int argc, char **argv)
 {
         int     j;

@@ -171,11 +181,11 @@
         }
 
         parseargs(argc, argv);
 
         /* handling all of xargs arguments:                             */
-        while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:s:x")) != EOF) {
+        while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:P:s:x")) != EOF) {
                 switch (c) {
                 case '0':
                         ZERO = TRUE;
                         break;
 

@@ -294,10 +304,18 @@
                                 LEGAL = DASHX || N_ARGS == 1;
                                 INSERT = PER_LINE = FALSE;
                         }
                         break;
 
+                case 'P':       /* -P maxprocs: # of child processses   */
+                        MAXPROCS = atoi(optarg);
+                        if (MAXPROCS <= 0) {
+                                ermsg(_("#maxprocs must be positive int: %s\n"),
+                                    optarg);
+                        }
+                        break;
+
                 case 's':       /* -s size: set max size of each arg list */
                         BUFLIM = atoi(optarg);
                         if (BUFLIM > BUFSIZE || BUFLIM <= 0) {
                                 ermsg(_("0 < max-cmd-line-size <= %d: %s\n"),
                                     BUFSIZE, optarg);

@@ -334,10 +352,12 @@
 
 
         mac -= optind;  /* dec arg count by what we've processed        */
         mav += optind;  /* inc to current mav                           */
 
+        (void) procs_malloc();
+
         if (mac <= 0) { /* if there're no more args to process, */
                 cmdname = "/usr/bin/echo";      /* our default command  */
                 *ARGV++ = addarg(cmdname);      /* use the default cmd. */
         } else {        /* otherwise keep parsing rest of the string.   */
                 /*

@@ -404,10 +424,11 @@
                                  * cannot create any list because it would be
                                  * too big.
                                  */
                                 if (LEGAL || N_args == 0) {
                                         EMSG(LIST2LONG);
+                                        (void) procs_wait(1);
                                         exit(2);
                                         /* NOTREACHED */
                                 }
 
                                 /*

@@ -433,11 +454,11 @@
                 }
 
                 *ARGV = NULL;
                 if (N_args == 0) {
                         /* Reached the end with no more work. */
-                        exit(exitstat);
+                        break;
                 }
 
                 /* insert arg if requested */
 
                 if (!ERR && INSERT) {

@@ -462,10 +483,11 @@
                         for (ARGV = arglist; *ARGV != NULL; ARGV++) {
                                 linesize += strlen(*ARGV) + 1;
                         }
                         if (linesize >= BUFLIM) {
                                 EMSG(LIST2LONG);
+                                (void) procs_wait(1);
                                 exit(2);
                                 /* NOTREACHED */
                         }
                 }
 

@@ -482,15 +504,17 @@
                                  * for xcu4, all invocations of cmdname must
                                  * return 0, in order for us to return 0.
                                  * so if we have a non-zero status here,
                                  * quit immediately.
                                  */
-                                exitstat |= lcall(cmdname, arglist);
+                                (void) lcall(cmdname, arglist);
                         }
                 }
         }
 
+        (void) procs_wait(1);
+
         if (OK)
                 return (exitstat);
 
         /*
          * if exitstat was set, to match XCU4 complience,

@@ -835,38 +859,22 @@
         exit(1);
         /* NOTREACHED */
 }
 
 
-static int
+static void
 lcall(char *sub, char **subargs)
 {
-        int retcode, retry = 0;
-        pid_t iwait, child;
+        int     retry = 0;
+        pid_t   child;
 
         for (;;) {
                 switch (child = fork()) {
                 default:
-                        while ((iwait = wait(&retcode)) != child &&
-                            iwait != (pid_t)-1)
-                                ;
-                        if (iwait == (pid_t)-1) {
-                                PERR(WAITFAIL);
-                                exit(122);
-                                /* NOTREACHED */
-                        }
-                        if (WIFSIGNALED(retcode)) {
-                                EMSG2(CHILDSIG, WTERMSIG(retcode));
-                                exit(125);
-                                /* NOTREACHED */
-                        }
-                        if ((WEXITSTATUS(retcode) & 0377) == 0377) {
-                                EMSG(CHILDFAIL);
-                                exit(124);
-                                /* NOTREACHED */
-                        }
-                        return (WEXITSTATUS(retcode));
+                        (void) procs_store(child);
+                        (void) procs_wait(0);
+                        return;
                 case 0:
                         (void) execvp(sub, subargs);
                         PERR(EXECFAIL);
                         if (errno == EACCES)
                                 exit(126);

@@ -880,10 +888,116 @@
                         (void) sleep(1);
                 }
         }
 }
 
+static void
+procs_malloc(void)
+{
+        int     i;
+
+        procs = (pid_t *)(malloc(MAXPROCS * sizeof(pid_t)));
+        if (procs == NULL) {
+                PERR(MALLOCFAIL);
+                exit(1);
+        }
+
+        for (i = 0; i < MAXPROCS; i++) {
+                procs[i] = (pid_t)(0);
+        }
+}
+
+static int
+procs_find(pid_t child)
+{
+        int     i;
+
+        for (i = 0; i < MAXPROCS; i++) {
+                if (procs[i] == child) {
+                        return (i);
+                }
+        }
+
+        return (-1);
+}
+
+static void
+procs_store(pid_t child)
+{
+        int     i;
+
+        i = procs_find((pid_t)(0));
+        if (i < 0) {
+                PERR(NOCHILDSLOT);
+                exit(1);
+        }
+        procs[i] = child;
+        n_procs++;
+}
+
+static int
+procs_delete(pid_t child)
+{
+        int     i;
+
+        i = procs_find(child);
+        if (i < 0) {
+                return (0);
+        }
+        procs[i] = (pid_t)(0);
+        n_procs--;
+        return (1);
+}
+
+static pid_t
+procs_waitpid(int blocking, int *stat_loc)
+{
+        pid_t   child;
+        int     options;
+
+        if (n_procs == 0) {
+                errno = ECHILD;
+                return (-1);
+        }
+
+        options = (blocking) ? 0 : WNOHANG;
+
+        while ((child = waitpid(-1, stat_loc, options)) > 0) {
+                if (procs_delete(child)) {
+                        break;
+                }
+        }
+
+        return (child);
+}
+
+static void
+procs_wait(int blocking)
+{
+        pid_t   child;
+        int     stat_loc;
+
+        while ((child = procs_waitpid(blocking || (n_procs >= MAXPROCS) ? 1 : 0, &stat_loc)) > 0) {
+                if (WIFSIGNALED(stat_loc)) {
+                        EMSG2(CHILDSIG, WTERMSIG(stat_loc));
+                        exit(125);
+                        /* NOTREACHED */
+                } else if ((WEXITSTATUS(stat_loc) & 0377) == 0377) {
+                        EMSG(CHILDFAIL);
+                        exit(124);
+                        /* NOTREACHED */
+                } else {
+                        exitstat |= WEXITSTATUS(stat_loc);
+                }
+        }
+
+        if (child == (pid_t)(-1) && errno != ECHILD) {
+                EMSG(WAITFAIL);
+                exit(122);
+                /* NOTREACHED */
+        }
+}
 
 static void
 usage()
 {
         ermsg(_(USAGEMSG));

@@ -978,10 +1092,11 @@
                          * latter case. we handle the latter possibility
                          * first so both the old solaris way of handling
                          * and the new XCU4 way of handling things are allowed.
                          */
                         case    'n':    /* FALLTHROUGH                  */
+                        case    'P':    /* FALLTHROUGH                  */
                         case    's':    /* FALLTHROUGH                  */
                         case    'E':    /* FALLTHROUGH                  */
                         case    'I':    /* FALLTHROUGH                  */
                         case    'L':
                                 /*