diff options
| -rw-r--r-- | tests/xargs.test | 15 | ||||
| -rw-r--r-- | toys/posix/xargs.c | 62 | 
2 files changed, 51 insertions, 26 deletions
diff --git a/tests/xargs.test b/tests/xargs.test index afed8a17..dc3c7b32 100644 --- a/tests/xargs.test +++ b/tests/xargs.test @@ -61,6 +61,21 @@ testing "big input forces split" \    'for i in $(seq 1 100);do echo $X;done|{ xargs >/dev/null && echo yes;}' \    "yes\n" "" "" +# -P max-proc +testing "max-proc=1" "xargs -n 1 -P 1" "one\ntwo\nthree\n" "" "one\ntwo\nthree\n" +testing "max-proc=2" "xargs -n 1 -P 2" "y\ny\ny\n" "" "y\ny\ny\n" +testing "max-proc=3" "xargs -n 1 -P 3" "y\ny\ny\n" "" "y\ny\ny\n" +# 3 seconds vs 2 seconds vs 1 second parallel sleep +testing "parallel sleep" " +  xargs_sleep() { +    ( yes | head -3 | tr y 1 | time -p xargs -n 1 \${*} sleep ) 2>&1 | +      sed -n 's/^real *\([0-9]*\)[.]\(.*\)/\1\2/p' +  } && +  A=\`xargs_sleep\` && +  B=\`xargs_sleep -P 2\` && +  C=\`xargs_sleep -P 0\` && +  [ \${A} -gt \${B} -a \${B} -gt \${C} ] && echo OK || echo FAIL" "OK\n" "" "" +  # TODO: what exactly is -x supposed to do? why does coreutils output "one"?  #testing "-x" "xargs -x -s 9 || echo expected" "one\nexpected\n" "" "one\ntwo\nthree" diff --git a/toys/posix/xargs.c b/toys/posix/xargs.c index 79e7dea5..26c1b250 100644 --- a/toys/posix/xargs.c +++ b/toys/posix/xargs.c @@ -8,15 +8,14 @@   * TODO: -I	Insert mode   * TODO: -L	Max number of lines of input per command   * TODO: -x	Exit if can't fit everything in one command - * TODO: -P NUM	Run up to NUM processes at once -USE_XARGS(NEWTOY(xargs, "^E:P#optrn#<1(max-args)s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_XARGS(NEWTOY(xargs, "^E:P#<0=1optrn#<1(max-args)s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN))  config XARGS    bool "xargs"    default y    help -    usage: xargs [-0prt] [-s NUM] [-n NUM] [-E STR] COMMAND... +    usage: xargs [-0prt] [-snE STR] COMMAND...      Run command line one or more times, appending arguments from stdin. @@ -27,7 +26,8 @@ config XARGS      -n	Max number of arguments per command      -o	Open tty for COMMAND's stdin (default /dev/null)      -p	Prompt for y/n from tty before running each command -    -r	Don't run command with empty input (otherwise always run command once) +    -P  Parallel processes (default 1) +    -r	Don't run with empty input (otherwise always run command once)      -s	Size in bytes per command line      -t	Trace, print command line to stderr  */ @@ -39,7 +39,7 @@ GLOBALS(    long s, n, P;    char *E; -  long entries, bytes; +  long entries, bytes, np;    char delim;    FILE *tty;  ) @@ -91,13 +91,23 @@ static char *handle_entries(char *data, char **entry)    return 0;  } +// Handle SIGUSR1 and SIGUSR2 for -P +static void signal_P(int sig) +{ +  if (sig == SIGUSR2 && TT.P>1) TT.P--; +  else TT.P++; +} +  void xargs_main(void)  {    struct double_list *dlist = 0, *dtemp;    int entries, bytes, done = 0, status; -  char *data = 0, **out; +  char *data = 0, **out = 0;    pid_t pid = 0; +  xsignal_flags(SIGUSR1, signal_P, SA_RESTART); +  xsignal_flags(SIGUSR2, signal_P, SA_RESTART); +    // POSIX requires that we never hit the ARG_MAX limit, even if we try to    // with -s. POSIX also says we have to reserve 2048 bytes "to guarantee    // that the invoked utility has room to modify its environment variables @@ -138,7 +148,6 @@ void xargs_main(void)          }        }        dlist_add(&dlist, data); -        // Count data used        if (!(data = handle_entries(data, 0))) continue;        if (data == (char *)2) done++; @@ -150,8 +159,7 @@ void xargs_main(void)      if (!TT.entries) {        if (data) error_exit("argument too long"); -      else if (pid) return; -      else if (FLAG(r)) continue; +      if (pid || FLAG(r)) goto reap_children;      }      // Fill out command line to exec @@ -170,7 +178,7 @@ void xargs_main(void)        if (FLAG(p)) {          fprintf(stderr, "?");          if (!TT.tty) TT.tty = xfopen("/dev/tty", "re"); -        if (!fyesno(TT.tty, 0)) goto skip; +        if (!fyesno(TT.tty, 0)) goto reap_children;        } else fprintf(stderr, "\n");      } @@ -179,28 +187,30 @@ void xargs_main(void)        xopen_stdio(FLAG(o) ? "/dev/tty" : "/dev/null", O_RDONLY);        xexec(out);      } -    waitpid(pid, &status, 0); - -    // xargs is yet another weird collection of exit value special cases, -    // different from all the others. -    if (WIFEXITED(status)) { -      if (WEXITSTATUS(status) == 126 || WEXITSTATUS(status) == 127) { -        toys.exitval = WEXITSTATUS(status); -        return; -      } else if (WEXITSTATUS(status) >= 1 && WEXITSTATUS(status) <= 125) { -        toys.exitval = 123; -      } else if (WEXITSTATUS(status) == 255) { -        error_msg("%s: exited with status 255; aborting", out[0]); +    TT.np++; + +reap_children: +    while (TT.np) { +      int xv = (TT.np == TT.P) || (!data && done); + +      if (1>(xv = waitpid(-1, &status, WNOHANG*!xv))) break; +      TT.np--; +      xv = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+128; +      if (xv == 255) { +        error_msg("%s: exited with status 255; aborting", *out);          toys.exitval = 124; -        return; -      } -    } else toys.exitval = 127; +        break; +      } else if ((xv|1)==127) toys.exitval = xv; +      else if (xv>127) xv = 125; +      else if (xv) toys.exitval = 123; +    }      // Abritrary number of execs, can't just leak memory each time... -skip:      llist_traverse(dlist, llist_free_double);      dlist = 0;      free(out); +    out = 0;    } +  while (TT.np && -1 != wait(&status)) TT.np--;    if (TT.tty) fclose(TT.tty);  }  | 
