Print this page
4703 would like xargs support for -P

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/xargs/xargs.c
          +++ new/usr/src/cmd/xargs/xargs.c
↓ open down ↓ 81 lines elided ↑ open up ↑
  82   82  #define MALLOCFAIL      "Memory allocation failure"
  83   83  #define CORRUPTFILE     "Corrupt input file"
  84   84  #define WAITFAIL        "Wait failure"
  85   85  #define CHILDSIG        "Child killed with signal %d"
  86   86  #define CHILDFAIL       "Command could not continue processing data"
  87   87  #define FORKFAIL        "Could not fork child"
  88   88  #define EXECFAIL        "Could not exec command"
  89   89  #define MISSQUOTE       "Missing quote"
  90   90  #define BADESCAPE       "Incomplete escape"
  91   91  #define IBUFOVERFLOW    "Insert buffer overflow"
       92 +#define NOCHILDSLOT     "No free child slot available"
  92   93  
  93   94  #define _(x)    gettext(x)
  94   95  
  95   96  static wctype_t blank;
  96   97  static char     *arglist[MAXARGS+1];
  97   98  static char     argbuf[BUFSIZE * 2 + 1];
  98   99  static char     lastarg[BUFSIZE + 1];
  99  100  static char     **ARGV = arglist;
 100  101  static char     *LEOF = "_";
 101  102  static char     *INSPAT = INSPAT_STR;
 102  103  static char     ins_buf[MAXIBUF];
 103  104  static char     *p_ibuf;
 104  105  
 105  106  static struct inserts {
 106  107          char    **p_ARGV;       /* where to put newarg ptr in arg list */
 107  108          char    *p_skel;        /* ptr to arg template */
 108  109  } saveargv[MAXINSERTS];
 109  110  
 110  111  static int      PROMPT = -1;
 111  112  static int      BUFLIM = BUFSIZE;
      113 +static int      MAXPROCS = 1;
 112  114  static int      N_ARGS = 0;
 113  115  static int      N_args = 0;
 114  116  static int      N_lines = 0;
 115  117  static int      DASHX = FALSE;
 116  118  static int      MORE = TRUE;
 117  119  static int      PER_LINE = FALSE;
 118  120  static int      ERR = FALSE;
 119  121  static int      OK = TRUE;
 120  122  static int      LEGAL = FALSE;
 121  123  static int      TRACE = FALSE;
 122  124  static int      INSERT = FALSE;
 123  125  static int      ZERO = FALSE;
 124  126  static int      linesize = 0;
 125  127  static int      ibufsize = 0;
 126  128  static int      exitstat = 0;   /* our exit status                      */
 127  129  static int      mac;            /* modified argc, after parsing         */
 128  130  static char     **mav;          /* modified argv, after parsing         */
 129  131  static int      n_inserts;      /* # of insertions.                     */
      132 +static pid_t    *procs;         /* pids of children                     */
      133 +static int      n_procs;        /* # of child processes.                */
 130  134  
 131  135  /* our usage message:                                                   */
 132  136  #define USAGEMSG "Usage: xargs: [-t] [-p] [-0] [-e[eofstr]] [-E eofstr] "\
 133      -        "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-s size] "\
      137 +        "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-P maxprocs] [-s size] "\
 134  138          "[cmd [args ...]]\n"
 135  139  
 136  140  static int      echoargs();
 137  141  static wint_t   getwchr(char *, size_t *);
 138      -static int      lcall(char *sub, char **subargs);
      142 +static void     lcall(char *sub, char **subargs);
 139  143  static void     addibuf(struct inserts *p);
 140  144  static void     ermsg(char *messages, ...);
 141  145  static char     *addarg(char *arg);
 142  146  static void     store_str(char **, char *, size_t);
 143  147  static char     *getarg(char *);
 144  148  static char     *insert(char *pattern, char *subst);
 145  149  static void     usage();
 146  150  static void     parseargs();
      151 +static void     procs_malloc(void);
      152 +static int      procs_find(pid_t child);
      153 +static void     procs_store(pid_t child);
      154 +static int      procs_delete(pid_t child);
      155 +static pid_t    procs_waitpid(int blocking, int *stat_loc);
      156 +static void     procs_wait(int blocking);
 147  157  
 148  158  int
 149  159  main(int argc, char **argv)
 150  160  {
 151  161          int     j;
 152  162          struct inserts *psave;
 153  163          int c;
 154  164          int     initsize;
 155  165          char    *cmdname, **initlist;
 156  166          char    *arg;
↓ open down ↓ 9 lines elided ↑ open up ↑
 166  176  #endif
 167  177          (void) textdomain(TEXT_DOMAIN);
 168  178          if (init_yes() < 0) {
 169  179                  ermsg(_(ERR_MSG_INIT_YES), strerror(errno));
 170  180                  exit(1);
 171  181          }
 172  182  
 173  183          parseargs(argc, argv);
 174  184  
 175  185          /* handling all of xargs arguments:                             */
 176      -        while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:s:x")) != EOF) {
      186 +        while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:P:s:x")) != EOF) {
 177  187                  switch (c) {
 178  188                  case '0':
 179  189                          ZERO = TRUE;
 180  190                          break;
 181  191  
 182  192                  case 't':       /* -t: turn trace mode on               */
 183  193                          TRACE = TRUE;
 184  194                          break;
 185  195  
 186  196                  case 'p':       /* -p: turn on prompt mode.             */
↓ open down ↓ 102 lines elided ↑ open up ↑
 289  299                           */
 290  300                          if ((N_ARGS = atoi(optarg)) <= 0) {
 291  301                                  ermsg(_("#args must be positive int: %s\n"),
 292  302                                      optarg);
 293  303                          } else {
 294  304                                  LEGAL = DASHX || N_ARGS == 1;
 295  305                                  INSERT = PER_LINE = FALSE;
 296  306                          }
 297  307                          break;
 298  308  
      309 +                case 'P':       /* -P maxprocs: # of child processses   */
      310 +                        MAXPROCS = atoi(optarg);
      311 +                        if (MAXPROCS <= 0) {
      312 +                                ermsg(_("#maxprocs must be positive int: %s\n"),
      313 +                                    optarg);
      314 +                        }
      315 +                        break;
      316 +
 299  317                  case 's':       /* -s size: set max size of each arg list */
 300  318                          BUFLIM = atoi(optarg);
 301  319                          if (BUFLIM > BUFSIZE || BUFLIM <= 0) {
 302  320                                  ermsg(_("0 < max-cmd-line-size <= %d: %s\n"),
 303  321                                      BUFSIZE, optarg);
 304  322                          }
 305  323                          break;
 306  324  
 307  325                  case 'x':       /* -x: terminate if args > size limit   */
 308  326                          DASHX = LEGAL = TRUE;
↓ open down ↓ 20 lines elided ↑ open up ↑
 329  347  
 330  348          /*
 331  349           * we're finished handling xargs's options, so now pick up
 332  350           * the command name (if any), and it's options.
 333  351           */
 334  352  
 335  353  
 336  354          mac -= optind;  /* dec arg count by what we've processed        */
 337  355          mav += optind;  /* inc to current mav                           */
 338  356  
      357 +        (void) procs_malloc();
      358 +
 339  359          if (mac <= 0) { /* if there're no more args to process, */
 340  360                  cmdname = "/usr/bin/echo";      /* our default command  */
 341  361                  *ARGV++ = addarg(cmdname);      /* use the default cmd. */
 342  362          } else {        /* otherwise keep parsing rest of the string.   */
 343  363                  /*
 344  364                   * note that we can't use getopts(3C), and *must* parse
 345  365                   * this by hand, as we don't know apriori what options the
 346  366                   * command will take.
 347  367                   */
 348  368                  cmdname = *mav; /* get the command name */
↓ open down ↓ 50 lines elided ↑ open up ↑
 399  419                          /* Inserts are handled specially later. */
 400  420                          if ((n_inserts == 0) && (linesize >= BUFLIM)) {
 401  421                                  /*
 402  422                                   * Legal indicates hard fail if the list is
 403  423                                   * truncated due to size.  So fail, or if we
 404  424                                   * cannot create any list because it would be
 405  425                                   * too big.
 406  426                                   */
 407  427                                  if (LEGAL || N_args == 0) {
 408  428                                          EMSG(LIST2LONG);
      429 +                                        (void) procs_wait(1);
 409  430                                          exit(2);
 410  431                                          /* NOTREACHED */
 411  432                                  }
 412  433  
 413  434                                  /*
 414  435                                   * Otherwise just save argument for later.
 415  436                                   */
 416  437                                  (void) strcpy(lastarg, arg);
 417  438                                  break;
 418  439                          }
↓ open down ↓ 9 lines elided ↑ open up ↑
 428  449  
 429  450  
 430  451                          if ((ARGV - arglist) == MAXARGS) {
 431  452                                  break;
 432  453                          }
 433  454                  }
 434  455  
 435  456                  *ARGV = NULL;
 436  457                  if (N_args == 0) {
 437  458                          /* Reached the end with no more work. */
 438      -                        exit(exitstat);
      459 +                        break;
 439  460                  }
 440  461  
 441  462                  /* insert arg if requested */
 442  463  
 443  464                  if (!ERR && INSERT) {
 444  465  
 445  466                          p_ibuf = ins_buf;
 446  467                          ARGV--;
 447  468                          j = ibufsize = 0;
 448  469                          for (psave = saveargv; ++j <= n_inserts; ++psave) {
↓ open down ↓ 8 lines elided ↑ open up ↑
 457  478                          /*
 458  479                           * if we've done any insertions, re-calculate the
 459  480                           * linesize. bomb out if we've exceeded our length.
 460  481                           */
 461  482                          linesize = 0;
 462  483                          for (ARGV = arglist; *ARGV != NULL; ARGV++) {
 463  484                                  linesize += strlen(*ARGV) + 1;
 464  485                          }
 465  486                          if (linesize >= BUFLIM) {
 466  487                                  EMSG(LIST2LONG);
      488 +                                (void) procs_wait(1);
 467  489                                  exit(2);
 468  490                                  /* NOTREACHED */
 469  491                          }
 470  492                  }
 471  493  
 472  494                  /* exec command */
 473  495  
 474  496                  if (!ERR) {
 475  497                          if (!MORE &&
 476  498                              (PER_LINE && N_lines == 0 || N_ARGS && N_args == 0))
 477  499                                  exit(exitstat);
 478  500                          OK = TRUE;
 479  501                          j = TRACE ? echoargs() : TRUE;
 480  502                          if (j) {
 481  503                                  /*
 482  504                                   * for xcu4, all invocations of cmdname must
 483  505                                   * return 0, in order for us to return 0.
 484  506                                   * so if we have a non-zero status here,
 485  507                                   * quit immediately.
 486  508                                   */
 487      -                                exitstat |= lcall(cmdname, arglist);
      509 +                                (void) lcall(cmdname, arglist);
 488  510                          }
 489  511                  }
 490  512          }
 491  513  
      514 +        (void) procs_wait(1);
      515 +
 492  516          if (OK)
 493  517                  return (exitstat);
 494  518  
 495  519          /*
 496  520           * if exitstat was set, to match XCU4 complience,
 497  521           * return that value, otherwise, return 1.
 498  522           */
 499  523          return (exitstat ? exitstat : 1);
 500  524  }
 501  525  
↓ open down ↓ 328 lines elided ↑ open up ↑
 830  854           * of view.  After all what if they are feeding us a file
 831  855           * generated in another locale?
 832  856           */
 833  857          errno = EILSEQ;
 834  858          PERR(CORRUPTFILE);
 835  859          exit(1);
 836  860          /* NOTREACHED */
 837  861  }
 838  862  
 839  863  
 840      -static int
      864 +static void
 841  865  lcall(char *sub, char **subargs)
 842  866  {
 843      -        int retcode, retry = 0;
 844      -        pid_t iwait, child;
      867 +        int     retry = 0;
      868 +        pid_t   child;
 845  869  
 846  870          for (;;) {
 847  871                  switch (child = fork()) {
 848  872                  default:
 849      -                        while ((iwait = wait(&retcode)) != child &&
 850      -                            iwait != (pid_t)-1)
 851      -                                ;
 852      -                        if (iwait == (pid_t)-1) {
 853      -                                PERR(WAITFAIL);
 854      -                                exit(122);
 855      -                                /* NOTREACHED */
 856      -                        }
 857      -                        if (WIFSIGNALED(retcode)) {
 858      -                                EMSG2(CHILDSIG, WTERMSIG(retcode));
 859      -                                exit(125);
 860      -                                /* NOTREACHED */
 861      -                        }
 862      -                        if ((WEXITSTATUS(retcode) & 0377) == 0377) {
 863      -                                EMSG(CHILDFAIL);
 864      -                                exit(124);
 865      -                                /* NOTREACHED */
 866      -                        }
 867      -                        return (WEXITSTATUS(retcode));
      873 +                        (void) procs_store(child);
      874 +                        (void) procs_wait(0);
      875 +                        return;
 868  876                  case 0:
 869  877                          (void) execvp(sub, subargs);
 870  878                          PERR(EXECFAIL);
 871  879                          if (errno == EACCES)
 872  880                                  exit(126);
 873  881                          exit(127);
 874  882                          /* NOTREACHED */
 875  883                  case -1:
 876  884                          if (errno != EAGAIN && retry++ < FORK_RETRY) {
 877  885                                  PERR(FORKFAIL);
 878  886                                  exit(123);
 879  887                          }
 880  888                          (void) sleep(1);
 881  889                  }
 882  890          }
 883  891  }
 884  892  
      893 +static void
      894 +procs_malloc(void)
      895 +{
      896 +        int     i;
      897 +
      898 +        procs = (pid_t *)(malloc(MAXPROCS * sizeof(pid_t)));
      899 +        if (procs == NULL) {
      900 +                PERR(MALLOCFAIL);
      901 +                exit(1);
      902 +        }
      903 +
      904 +        for (i = 0; i < MAXPROCS; i++) {
      905 +                procs[i] = (pid_t)(0);
      906 +        }
      907 +}
      908 +
      909 +static int
      910 +procs_find(pid_t child)
      911 +{
      912 +        int     i;
      913 +
      914 +        for (i = 0; i < MAXPROCS; i++) {
      915 +                if (procs[i] == child) {
      916 +                        return (i);
      917 +                }
      918 +        }
      919 +
      920 +        return (-1);
      921 +}
      922 +
      923 +static void
      924 +procs_store(pid_t child)
      925 +{
      926 +        int     i;
      927 +
      928 +        i = procs_find((pid_t)(0));
      929 +        if (i < 0) {
      930 +                PERR(NOCHILDSLOT);
      931 +                exit(1);
      932 +        }
      933 +        procs[i] = child;
      934 +        n_procs++;
      935 +}
      936 +
      937 +static int
      938 +procs_delete(pid_t child)
      939 +{
      940 +        int     i;
      941 +
      942 +        i = procs_find(child);
      943 +        if (i < 0) {
      944 +                return (0);
      945 +        }
      946 +        procs[i] = (pid_t)(0);
      947 +        n_procs--;
      948 +        return (1);
      949 +}
      950 +
      951 +static pid_t
      952 +procs_waitpid(int blocking, int *stat_loc)
      953 +{
      954 +        pid_t   child;
      955 +        int     options;
      956 +
      957 +        if (n_procs == 0) {
      958 +                errno = ECHILD;
      959 +                return (-1);
      960 +        }
      961 +
      962 +        options = (blocking) ? 0 : WNOHANG;
      963 +
      964 +        while ((child = waitpid(-1, stat_loc, options)) > 0) {
      965 +                if (procs_delete(child)) {
      966 +                        break;
      967 +                }
      968 +        }
      969 +
      970 +        return (child);
      971 +}
      972 +
      973 +static void
      974 +procs_wait(int blocking)
      975 +{
      976 +        pid_t   child;
      977 +        int     stat_loc;
      978 +
      979 +        while ((child = procs_waitpid(blocking || (n_procs >= MAXPROCS) ? 1 : 0, &stat_loc)) > 0) {
      980 +                if (WIFSIGNALED(stat_loc)) {
      981 +                        EMSG2(CHILDSIG, WTERMSIG(stat_loc));
      982 +                        exit(125);
      983 +                        /* NOTREACHED */
      984 +                } else if ((WEXITSTATUS(stat_loc) & 0377) == 0377) {
      985 +                        EMSG(CHILDFAIL);
      986 +                        exit(124);
      987 +                        /* NOTREACHED */
      988 +                } else {
      989 +                        exitstat |= WEXITSTATUS(stat_loc);
      990 +                }
      991 +        }
      992 +
      993 +        if (child == (pid_t)(-1) && errno != ECHILD) {
      994 +                EMSG(WAITFAIL);
      995 +                exit(122);
      996 +                /* NOTREACHED */
      997 +        }
      998 +}
 885  999  
 886 1000  static void
 887 1001  usage()
 888 1002  {
 889 1003          ermsg(_(USAGEMSG));
 890 1004          OK = FALSE;
 891 1005  }
 892 1006  
 893 1007  
 894 1008  
↓ open down ↓ 78 lines elided ↑ open up ↑
 973 1087                           * flag can have the normal XCU4 handling
 974 1088                           * (of the form: -X subargument); or it can have
 975 1089                           * the old solaris 2.[0-4] handling (of the
 976 1090                           * form: -Xsubargument). in order to maintain
 977 1091                           * backwards compatibility, we must support the
 978 1092                           * latter case. we handle the latter possibility
 979 1093                           * first so both the old solaris way of handling
 980 1094                           * and the new XCU4 way of handling things are allowed.
 981 1095                           */
 982 1096                          case    'n':    /* FALLTHROUGH                  */
     1097 +                        case    'P':    /* FALLTHROUGH                  */
 983 1098                          case    's':    /* FALLTHROUGH                  */
 984 1099                          case    'E':    /* FALLTHROUGH                  */
 985 1100                          case    'I':    /* FALLTHROUGH                  */
 986 1101                          case    'L':
 987 1102                                  /*
 988 1103                                   * if the second character isn't null, then
 989 1104                                   * the user has specified the old syntax.
 990 1105                                   * we move the subargument into our
 991 1106                                   * mod'd argument list.
 992 1107                                   */
↓ open down ↓ 55 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX