Print this page
4703 would like xargs support for -P
*** 87,96 ****
--- 87,97 ----
#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,116 ****
--- 108,118 ----
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,151 ****
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. */
/* our usage message: */
#define USAGEMSG "Usage: xargs: [-t] [-p] [-0] [-e[eofstr]] [-E eofstr] "\
! "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-s size] "\
"[cmd [args ...]]\n"
static int echoargs();
static wint_t getwchr(char *, size_t *);
! static int 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();
int
main(int argc, char **argv)
{
int j;
--- 127,161 ----
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]] [-P maxprocs] [-s size] "\
"[cmd [args ...]]\n"
static int echoargs();
static wint_t getwchr(char *, size_t *);
! 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,181 ****
}
parseargs(argc, argv);
/* handling all of xargs arguments: */
! while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:s:x")) != EOF) {
switch (c) {
case '0':
ZERO = TRUE;
break;
--- 181,191 ----
}
parseargs(argc, argv);
/* handling all of xargs arguments: */
! while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:P:s:x")) != EOF) {
switch (c) {
case '0':
ZERO = TRUE;
break;
*** 294,303 ****
--- 304,321 ----
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,343 ****
--- 352,363 ----
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,413 ****
--- 424,434 ----
* 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,443 ****
}
*ARGV = NULL;
if (N_args == 0) {
/* Reached the end with no more work. */
! exit(exitstat);
}
/* insert arg if requested */
if (!ERR && INSERT) {
--- 454,464 ----
}
*ARGV = NULL;
if (N_args == 0) {
/* Reached the end with no more work. */
! break;
}
/* insert arg if requested */
if (!ERR && INSERT) {
*** 462,471 ****
--- 483,493 ----
for (ARGV = arglist; *ARGV != NULL; ARGV++) {
linesize += strlen(*ARGV) + 1;
}
if (linesize >= BUFLIM) {
EMSG(LIST2LONG);
+ (void) procs_wait(1);
exit(2);
/* NOTREACHED */
}
}
*** 482,496 ****
* 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);
}
}
}
if (OK)
return (exitstat);
/*
* if exitstat was set, to match XCU4 complience,
--- 504,520 ----
* 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.
*/
! (void) lcall(cmdname, arglist);
}
}
}
+ (void) procs_wait(1);
+
if (OK)
return (exitstat);
/*
* if exitstat was set, to match XCU4 complience,
*** 835,872 ****
exit(1);
/* NOTREACHED */
}
! static int
lcall(char *sub, char **subargs)
{
! int retcode, retry = 0;
! pid_t iwait, 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));
case 0:
(void) execvp(sub, subargs);
PERR(EXECFAIL);
if (errno == EACCES)
exit(126);
--- 859,880 ----
exit(1);
/* NOTREACHED */
}
! static void
lcall(char *sub, char **subargs)
{
! int retry = 0;
! pid_t child;
for (;;) {
switch (child = fork()) {
default:
! (void) procs_store(child);
! (void) procs_wait(0);
! return;
case 0:
(void) execvp(sub, subargs);
PERR(EXECFAIL);
if (errno == EACCES)
exit(126);
*** 880,889 ****
--- 888,1003 ----
(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,987 ****
--- 1092,1102 ----
* 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':
/*