72
73 /* We use these macros to help make formatting look "consistent" */
74 #define EMSG(s) ermsg(gettext(s "\n"))
75 #define EMSG2(s, a) ermsg(gettext(s "\n"), a)
76 #define PERR(s) perror(gettext("xargs: " s))
77
78 /* Some common error messages */
79
80 #define LIST2LONG "Argument list too long"
81 #define ARG2LONG "A single argument was greater than %d bytes"
82 #define MALLOCFAIL "Memory allocation failure"
83 #define CORRUPTFILE "Corrupt input file"
84 #define WAITFAIL "Wait failure"
85 #define CHILDSIG "Child killed with signal %d"
86 #define CHILDFAIL "Command could not continue processing data"
87 #define FORKFAIL "Could not fork child"
88 #define EXECFAIL "Could not exec command"
89 #define MISSQUOTE "Missing quote"
90 #define BADESCAPE "Incomplete escape"
91 #define IBUFOVERFLOW "Insert buffer overflow"
92
93 #define _(x) gettext(x)
94
95 static wctype_t blank;
96 static char *arglist[MAXARGS+1];
97 static char argbuf[BUFSIZE * 2 + 1];
98 static char lastarg[BUFSIZE + 1];
99 static char **ARGV = arglist;
100 static char *LEOF = "_";
101 static char *INSPAT = INSPAT_STR;
102 static char ins_buf[MAXIBUF];
103 static char *p_ibuf;
104
105 static struct inserts {
106 char **p_ARGV; /* where to put newarg ptr in arg list */
107 char *p_skel; /* ptr to arg template */
108 } saveargv[MAXINSERTS];
109
110 static int PROMPT = -1;
111 static int BUFLIM = BUFSIZE;
112 static int N_ARGS = 0;
113 static int N_args = 0;
114 static int N_lines = 0;
115 static int DASHX = FALSE;
116 static int MORE = TRUE;
117 static int PER_LINE = FALSE;
118 static int ERR = FALSE;
119 static int OK = TRUE;
120 static int LEGAL = FALSE;
121 static int TRACE = FALSE;
122 static int INSERT = FALSE;
123 static int ZERO = FALSE;
124 static int linesize = 0;
125 static int ibufsize = 0;
126 static int exitstat = 0; /* our exit status */
127 static int mac; /* modified argc, after parsing */
128 static char **mav; /* modified argv, after parsing */
129 static int n_inserts; /* # of insertions. */
130
131 /* our usage message: */
132 #define USAGEMSG "Usage: xargs: [-t] [-p] [-0] [-e[eofstr]] [-E eofstr] "\
133 "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-s size] "\
134 "[cmd [args ...]]\n"
135
136 static int echoargs();
137 static wint_t getwchr(char *, size_t *);
138 static int lcall(char *sub, char **subargs);
139 static void addibuf(struct inserts *p);
140 static void ermsg(char *messages, ...);
141 static char *addarg(char *arg);
142 static void store_str(char **, char *, size_t);
143 static char *getarg(char *);
144 static char *insert(char *pattern, char *subst);
145 static void usage();
146 static void parseargs();
147
148 int
149 main(int argc, char **argv)
150 {
151 int j;
152 struct inserts *psave;
153 int c;
154 int initsize;
155 char *cmdname, **initlist;
156 char *arg;
157 char *next;
158
159 /* initialization */
160 blank = wctype("blank");
161 n_inserts = 0;
162 psave = saveargv;
163 (void) setlocale(LC_ALL, "");
164 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
165 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
166 #endif
167 (void) textdomain(TEXT_DOMAIN);
168 if (init_yes() < 0) {
169 ermsg(_(ERR_MSG_INIT_YES), strerror(errno));
170 exit(1);
171 }
172
173 parseargs(argc, argv);
174
175 /* handling all of xargs arguments: */
176 while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:s:x")) != EOF) {
177 switch (c) {
178 case '0':
179 ZERO = TRUE;
180 break;
181
182 case 't': /* -t: turn trace mode on */
183 TRACE = TRUE;
184 break;
185
186 case 'p': /* -p: turn on prompt mode. */
187 if ((PROMPT = open("/dev/tty", O_RDONLY)) == -1) {
188 PERR("can't read from tty for -p");
189 } else {
190 TRACE = TRUE;
191 }
192 break;
193
194 case 'e':
195 /*
196 * -e[eofstr]: set/disable end-of-file.
279 if ((optarg != NULL) && (*optarg != '\0')) {
280 if ((PER_LINE = atoi(optarg)) <= 0)
281 PER_LINE = 1;
282 }
283 break;
284
285 case 'n': /* -n number: # stdin args */
286 /*
287 * -n number: # stdin args.
288 * number *is* required here:
289 */
290 if ((N_ARGS = atoi(optarg)) <= 0) {
291 ermsg(_("#args must be positive int: %s\n"),
292 optarg);
293 } else {
294 LEGAL = DASHX || N_ARGS == 1;
295 INSERT = PER_LINE = FALSE;
296 }
297 break;
298
299 case 's': /* -s size: set max size of each arg list */
300 BUFLIM = atoi(optarg);
301 if (BUFLIM > BUFSIZE || BUFLIM <= 0) {
302 ermsg(_("0 < max-cmd-line-size <= %d: %s\n"),
303 BUFSIZE, optarg);
304 }
305 break;
306
307 case 'x': /* -x: terminate if args > size limit */
308 DASHX = LEGAL = TRUE;
309 break;
310
311 default:
312 /*
313 * bad argument. complain and get ready to die.
314 */
315 usage();
316 exit(2);
317 break;
318 }
319 }
320
321 /*
322 * if anything called ermsg(), something screwed up, so
323 * we exit early.
324 */
325 if (OK == FALSE) {
326 usage();
327 exit(2);
328 }
329
330 /*
331 * we're finished handling xargs's options, so now pick up
332 * the command name (if any), and it's options.
333 */
334
335
336 mac -= optind; /* dec arg count by what we've processed */
337 mav += optind; /* inc to current mav */
338
339 if (mac <= 0) { /* if there're no more args to process, */
340 cmdname = "/usr/bin/echo"; /* our default command */
341 *ARGV++ = addarg(cmdname); /* use the default cmd. */
342 } else { /* otherwise keep parsing rest of the string. */
343 /*
344 * note that we can't use getopts(3C), and *must* parse
345 * this by hand, as we don't know apriori what options the
346 * command will take.
347 */
348 cmdname = *mav; /* get the command name */
349
350
351 /* pick up the remaining args from the command line: */
352 while ((OK == TRUE) && (mac-- > 0)) {
353 /*
354 * while we haven't crapped out, and there's
355 * work to do:
356 */
357 if (INSERT && ! ERR) {
358 if (strstr(*mav, INSPAT) != NULL) {
389 arg = strcpy(next, lastarg);
390 *lastarg = '\0';
391 } else if ((arg = getarg(next)) == NULL) {
392 break;
393 }
394
395 l = strlen(arg) + 1;
396 linesize += l;
397 next += l;
398
399 /* Inserts are handled specially later. */
400 if ((n_inserts == 0) && (linesize >= BUFLIM)) {
401 /*
402 * Legal indicates hard fail if the list is
403 * truncated due to size. So fail, or if we
404 * cannot create any list because it would be
405 * too big.
406 */
407 if (LEGAL || N_args == 0) {
408 EMSG(LIST2LONG);
409 exit(2);
410 /* NOTREACHED */
411 }
412
413 /*
414 * Otherwise just save argument for later.
415 */
416 (void) strcpy(lastarg, arg);
417 break;
418 }
419
420 *ARGV++ = arg;
421
422 N_args++;
423
424 if ((PER_LINE && N_lines >= PER_LINE) ||
425 (N_ARGS && (N_args) >= N_ARGS)) {
426 break;
427 }
428
429
430 if ((ARGV - arglist) == MAXARGS) {
431 break;
432 }
433 }
434
435 *ARGV = NULL;
436 if (N_args == 0) {
437 /* Reached the end with no more work. */
438 exit(exitstat);
439 }
440
441 /* insert arg if requested */
442
443 if (!ERR && INSERT) {
444
445 p_ibuf = ins_buf;
446 ARGV--;
447 j = ibufsize = 0;
448 for (psave = saveargv; ++j <= n_inserts; ++psave) {
449 addibuf(psave);
450 if (ERR)
451 break;
452 }
453 }
454 *ARGV = NULL;
455
456 if (n_inserts > 0) {
457 /*
458 * if we've done any insertions, re-calculate the
459 * linesize. bomb out if we've exceeded our length.
460 */
461 linesize = 0;
462 for (ARGV = arglist; *ARGV != NULL; ARGV++) {
463 linesize += strlen(*ARGV) + 1;
464 }
465 if (linesize >= BUFLIM) {
466 EMSG(LIST2LONG);
467 exit(2);
468 /* NOTREACHED */
469 }
470 }
471
472 /* exec command */
473
474 if (!ERR) {
475 if (!MORE &&
476 (PER_LINE && N_lines == 0 || N_ARGS && N_args == 0))
477 exit(exitstat);
478 OK = TRUE;
479 j = TRACE ? echoargs() : TRUE;
480 if (j) {
481 /*
482 * for xcu4, all invocations of cmdname must
483 * return 0, in order for us to return 0.
484 * so if we have a non-zero status here,
485 * quit immediately.
486 */
487 exitstat |= lcall(cmdname, arglist);
488 }
489 }
490 }
491
492 if (OK)
493 return (exitstat);
494
495 /*
496 * if exitstat was set, to match XCU4 complience,
497 * return that value, otherwise, return 1.
498 */
499 return (exitstat ? exitstat : 1);
500 }
501
502 static char *
503 addarg(char *arg)
504 {
505 linesize += (strlen(arg) + 1);
506 return (arg);
507 }
508
509
510 static void
511 store_str(char **buffer, char *str, size_t len)
820 *sz = i;
821 return ((wint_t)wch);
822 }
823 }
824
825 /*
826 * We have now encountered an illegal character sequence.
827 * There is nothing much we can do at this point but
828 * return an error. If we attempt to recover we may in fact
829 * return garbage as arguments, from the customer's point
830 * of view. After all what if they are feeding us a file
831 * generated in another locale?
832 */
833 errno = EILSEQ;
834 PERR(CORRUPTFILE);
835 exit(1);
836 /* NOTREACHED */
837 }
838
839
840 static int
841 lcall(char *sub, char **subargs)
842 {
843 int retcode, retry = 0;
844 pid_t iwait, child;
845
846 for (;;) {
847 switch (child = fork()) {
848 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));
868 case 0:
869 (void) execvp(sub, subargs);
870 PERR(EXECFAIL);
871 if (errno == EACCES)
872 exit(126);
873 exit(127);
874 /* NOTREACHED */
875 case -1:
876 if (errno != EAGAIN && retry++ < FORK_RETRY) {
877 PERR(FORKFAIL);
878 exit(123);
879 }
880 (void) sleep(1);
881 }
882 }
883 }
884
885
886 static void
887 usage()
888 {
889 ermsg(_(USAGEMSG));
890 OK = FALSE;
891 }
892
893
894
895 /*
896 * parseargs(): modify the args
897 * since the -e, -i and -l flags all take optional subarguments,
898 * and getopts(3C) is clueless about this nonsense, we change the
899 * our local argument count and strings to separate this out,
900 * and make it easier to handle via getopts(3c).
901 *
902 * -e -> "-e ""
903 * -e3 -> "-e "3"
904 * -Estr -> "-E "str"
963 if (mav[mac] == NULL) {
964 PERR(MALLOCFAIL);
965 exit(1);
966 }
967 break;
968
969 /* flags with required subarguments: */
970
971 /*
972 * there are two separate cases here. either the
973 * flag can have the normal XCU4 handling
974 * (of the form: -X subargument); or it can have
975 * the old solaris 2.[0-4] handling (of the
976 * form: -Xsubargument). in order to maintain
977 * backwards compatibility, we must support the
978 * latter case. we handle the latter possibility
979 * first so both the old solaris way of handling
980 * and the new XCU4 way of handling things are allowed.
981 */
982 case 'n': /* FALLTHROUGH */
983 case 's': /* FALLTHROUGH */
984 case 'E': /* FALLTHROUGH */
985 case 'I': /* FALLTHROUGH */
986 case 'L':
987 /*
988 * if the second character isn't null, then
989 * the user has specified the old syntax.
990 * we move the subargument into our
991 * mod'd argument list.
992 */
993 if (av[i][2] != NULL) {
994 /* first clean things up: */
995 mav[mac][2] = NULL;
996
997 /* now add the separation: */
998 ++mac; /* inc to next mod'd arg */
999 if ((mav[mac] = strdup(&av[i][2])) ==
1000 NULL) {
1001 PERR(MALLOCFAIL);
1002 exit(1);
|
72
73 /* We use these macros to help make formatting look "consistent" */
74 #define EMSG(s) ermsg(gettext(s "\n"))
75 #define EMSG2(s, a) ermsg(gettext(s "\n"), a)
76 #define PERR(s) perror(gettext("xargs: " s))
77
78 /* Some common error messages */
79
80 #define LIST2LONG "Argument list too long"
81 #define ARG2LONG "A single argument was greater than %d bytes"
82 #define MALLOCFAIL "Memory allocation failure"
83 #define CORRUPTFILE "Corrupt input file"
84 #define WAITFAIL "Wait failure"
85 #define CHILDSIG "Child killed with signal %d"
86 #define CHILDFAIL "Command could not continue processing data"
87 #define FORKFAIL "Could not fork child"
88 #define EXECFAIL "Could not exec command"
89 #define MISSQUOTE "Missing quote"
90 #define BADESCAPE "Incomplete escape"
91 #define IBUFOVERFLOW "Insert buffer overflow"
92 #define NOCHILDSLOT "No free child slot available"
93
94 #define _(x) gettext(x)
95
96 static wctype_t blank;
97 static char *arglist[MAXARGS+1];
98 static char argbuf[BUFSIZE * 2 + 1];
99 static char lastarg[BUFSIZE + 1];
100 static char **ARGV = arglist;
101 static char *LEOF = "_";
102 static char *INSPAT = INSPAT_STR;
103 static char ins_buf[MAXIBUF];
104 static char *p_ibuf;
105
106 static struct inserts {
107 char **p_ARGV; /* where to put newarg ptr in arg list */
108 char *p_skel; /* ptr to arg template */
109 } saveargv[MAXINSERTS];
110
111 static int PROMPT = -1;
112 static int BUFLIM = BUFSIZE;
113 static int MAXPROCS = 1;
114 static int N_ARGS = 0;
115 static int N_args = 0;
116 static int N_lines = 0;
117 static int DASHX = FALSE;
118 static int MORE = TRUE;
119 static int PER_LINE = FALSE;
120 static int ERR = FALSE;
121 static int OK = TRUE;
122 static int LEGAL = FALSE;
123 static int TRACE = FALSE;
124 static int INSERT = FALSE;
125 static int ZERO = FALSE;
126 static int linesize = 0;
127 static int ibufsize = 0;
128 static int exitstat = 0; /* our exit status */
129 static int mac; /* modified argc, after parsing */
130 static char **mav; /* modified argv, after parsing */
131 static int n_inserts; /* # of insertions. */
132 static pid_t *procs; /* pids of children */
133 static int n_procs; /* # of child processes. */
134
135 /* our usage message: */
136 #define USAGEMSG "Usage: xargs: [-t] [-p] [-0] [-e[eofstr]] [-E eofstr] "\
137 "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-P maxprocs] [-s size] "\
138 "[cmd [args ...]]\n"
139
140 static int echoargs();
141 static wint_t getwchr(char *, size_t *);
142 static void lcall(char *sub, char **subargs);
143 static void addibuf(struct inserts *p);
144 static void ermsg(char *messages, ...);
145 static char *addarg(char *arg);
146 static void store_str(char **, char *, size_t);
147 static char *getarg(char *);
148 static char *insert(char *pattern, char *subst);
149 static void usage();
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);
157
158 int
159 main(int argc, char **argv)
160 {
161 int j;
162 struct inserts *psave;
163 int c;
164 int initsize;
165 char *cmdname, **initlist;
166 char *arg;
167 char *next;
168
169 /* initialization */
170 blank = wctype("blank");
171 n_inserts = 0;
172 psave = saveargv;
173 (void) setlocale(LC_ALL, "");
174 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
175 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
176 #endif
177 (void) textdomain(TEXT_DOMAIN);
178 if (init_yes() < 0) {
179 ermsg(_(ERR_MSG_INIT_YES), strerror(errno));
180 exit(1);
181 }
182
183 parseargs(argc, argv);
184
185 /* handling all of xargs arguments: */
186 while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:P:s:x")) != EOF) {
187 switch (c) {
188 case '0':
189 ZERO = TRUE;
190 break;
191
192 case 't': /* -t: turn trace mode on */
193 TRACE = TRUE;
194 break;
195
196 case 'p': /* -p: turn on prompt mode. */
197 if ((PROMPT = open("/dev/tty", O_RDONLY)) == -1) {
198 PERR("can't read from tty for -p");
199 } else {
200 TRACE = TRUE;
201 }
202 break;
203
204 case 'e':
205 /*
206 * -e[eofstr]: set/disable end-of-file.
289 if ((optarg != NULL) && (*optarg != '\0')) {
290 if ((PER_LINE = atoi(optarg)) <= 0)
291 PER_LINE = 1;
292 }
293 break;
294
295 case 'n': /* -n number: # stdin args */
296 /*
297 * -n number: # stdin args.
298 * number *is* required here:
299 */
300 if ((N_ARGS = atoi(optarg)) <= 0) {
301 ermsg(_("#args must be positive int: %s\n"),
302 optarg);
303 } else {
304 LEGAL = DASHX || N_ARGS == 1;
305 INSERT = PER_LINE = FALSE;
306 }
307 break;
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
317 case 's': /* -s size: set max size of each arg list */
318 BUFLIM = atoi(optarg);
319 if (BUFLIM > BUFSIZE || BUFLIM <= 0) {
320 ermsg(_("0 < max-cmd-line-size <= %d: %s\n"),
321 BUFSIZE, optarg);
322 }
323 break;
324
325 case 'x': /* -x: terminate if args > size limit */
326 DASHX = LEGAL = TRUE;
327 break;
328
329 default:
330 /*
331 * bad argument. complain and get ready to die.
332 */
333 usage();
334 exit(2);
335 break;
336 }
337 }
338
339 /*
340 * if anything called ermsg(), something screwed up, so
341 * we exit early.
342 */
343 if (OK == FALSE) {
344 usage();
345 exit(2);
346 }
347
348 /*
349 * we're finished handling xargs's options, so now pick up
350 * the command name (if any), and it's options.
351 */
352
353
354 mac -= optind; /* dec arg count by what we've processed */
355 mav += optind; /* inc to current mav */
356
357 (void) procs_malloc();
358
359 if (mac <= 0) { /* if there're no more args to process, */
360 cmdname = "/usr/bin/echo"; /* our default command */
361 *ARGV++ = addarg(cmdname); /* use the default cmd. */
362 } else { /* otherwise keep parsing rest of the string. */
363 /*
364 * note that we can't use getopts(3C), and *must* parse
365 * this by hand, as we don't know apriori what options the
366 * command will take.
367 */
368 cmdname = *mav; /* get the command name */
369
370
371 /* pick up the remaining args from the command line: */
372 while ((OK == TRUE) && (mac-- > 0)) {
373 /*
374 * while we haven't crapped out, and there's
375 * work to do:
376 */
377 if (INSERT && ! ERR) {
378 if (strstr(*mav, INSPAT) != NULL) {
409 arg = strcpy(next, lastarg);
410 *lastarg = '\0';
411 } else if ((arg = getarg(next)) == NULL) {
412 break;
413 }
414
415 l = strlen(arg) + 1;
416 linesize += l;
417 next += l;
418
419 /* Inserts are handled specially later. */
420 if ((n_inserts == 0) && (linesize >= BUFLIM)) {
421 /*
422 * Legal indicates hard fail if the list is
423 * truncated due to size. So fail, or if we
424 * cannot create any list because it would be
425 * too big.
426 */
427 if (LEGAL || N_args == 0) {
428 EMSG(LIST2LONG);
429 (void) procs_wait(1);
430 exit(2);
431 /* NOTREACHED */
432 }
433
434 /*
435 * Otherwise just save argument for later.
436 */
437 (void) strcpy(lastarg, arg);
438 break;
439 }
440
441 *ARGV++ = arg;
442
443 N_args++;
444
445 if ((PER_LINE && N_lines >= PER_LINE) ||
446 (N_ARGS && (N_args) >= N_ARGS)) {
447 break;
448 }
449
450
451 if ((ARGV - arglist) == MAXARGS) {
452 break;
453 }
454 }
455
456 *ARGV = NULL;
457 if (N_args == 0) {
458 /* Reached the end with no more work. */
459 break;
460 }
461
462 /* insert arg if requested */
463
464 if (!ERR && INSERT) {
465
466 p_ibuf = ins_buf;
467 ARGV--;
468 j = ibufsize = 0;
469 for (psave = saveargv; ++j <= n_inserts; ++psave) {
470 addibuf(psave);
471 if (ERR)
472 break;
473 }
474 }
475 *ARGV = NULL;
476
477 if (n_inserts > 0) {
478 /*
479 * if we've done any insertions, re-calculate the
480 * linesize. bomb out if we've exceeded our length.
481 */
482 linesize = 0;
483 for (ARGV = arglist; *ARGV != NULL; ARGV++) {
484 linesize += strlen(*ARGV) + 1;
485 }
486 if (linesize >= BUFLIM) {
487 EMSG(LIST2LONG);
488 (void) procs_wait(1);
489 exit(2);
490 /* NOTREACHED */
491 }
492 }
493
494 /* exec command */
495
496 if (!ERR) {
497 if (!MORE &&
498 (PER_LINE && N_lines == 0 || N_ARGS && N_args == 0))
499 exit(exitstat);
500 OK = TRUE;
501 j = TRACE ? echoargs() : TRUE;
502 if (j) {
503 /*
504 * for xcu4, all invocations of cmdname must
505 * return 0, in order for us to return 0.
506 * so if we have a non-zero status here,
507 * quit immediately.
508 */
509 (void) lcall(cmdname, arglist);
510 }
511 }
512 }
513
514 (void) procs_wait(1);
515
516 if (OK)
517 return (exitstat);
518
519 /*
520 * if exitstat was set, to match XCU4 complience,
521 * return that value, otherwise, return 1.
522 */
523 return (exitstat ? exitstat : 1);
524 }
525
526 static char *
527 addarg(char *arg)
528 {
529 linesize += (strlen(arg) + 1);
530 return (arg);
531 }
532
533
534 static void
535 store_str(char **buffer, char *str, size_t len)
844 *sz = i;
845 return ((wint_t)wch);
846 }
847 }
848
849 /*
850 * We have now encountered an illegal character sequence.
851 * There is nothing much we can do at this point but
852 * return an error. If we attempt to recover we may in fact
853 * return garbage as arguments, from the customer's point
854 * of view. After all what if they are feeding us a file
855 * generated in another locale?
856 */
857 errno = EILSEQ;
858 PERR(CORRUPTFILE);
859 exit(1);
860 /* NOTREACHED */
861 }
862
863
864 static void
865 lcall(char *sub, char **subargs)
866 {
867 int retry = 0;
868 pid_t child;
869
870 for (;;) {
871 switch (child = fork()) {
872 default:
873 (void) procs_store(child);
874 (void) procs_wait(0);
875 return;
876 case 0:
877 (void) execvp(sub, subargs);
878 PERR(EXECFAIL);
879 if (errno == EACCES)
880 exit(126);
881 exit(127);
882 /* NOTREACHED */
883 case -1:
884 if (errno != EAGAIN && retry++ < FORK_RETRY) {
885 PERR(FORKFAIL);
886 exit(123);
887 }
888 (void) sleep(1);
889 }
890 }
891 }
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 }
999
1000 static void
1001 usage()
1002 {
1003 ermsg(_(USAGEMSG));
1004 OK = FALSE;
1005 }
1006
1007
1008
1009 /*
1010 * parseargs(): modify the args
1011 * since the -e, -i and -l flags all take optional subarguments,
1012 * and getopts(3C) is clueless about this nonsense, we change the
1013 * our local argument count and strings to separate this out,
1014 * and make it easier to handle via getopts(3c).
1015 *
1016 * -e -> "-e ""
1017 * -e3 -> "-e "3"
1018 * -Estr -> "-E "str"
1077 if (mav[mac] == NULL) {
1078 PERR(MALLOCFAIL);
1079 exit(1);
1080 }
1081 break;
1082
1083 /* flags with required subarguments: */
1084
1085 /*
1086 * there are two separate cases here. either the
1087 * flag can have the normal XCU4 handling
1088 * (of the form: -X subargument); or it can have
1089 * the old solaris 2.[0-4] handling (of the
1090 * form: -Xsubargument). in order to maintain
1091 * backwards compatibility, we must support the
1092 * latter case. we handle the latter possibility
1093 * first so both the old solaris way of handling
1094 * and the new XCU4 way of handling things are allowed.
1095 */
1096 case 'n': /* FALLTHROUGH */
1097 case 'P': /* FALLTHROUGH */
1098 case 's': /* FALLTHROUGH */
1099 case 'E': /* FALLTHROUGH */
1100 case 'I': /* FALLTHROUGH */
1101 case 'L':
1102 /*
1103 * if the second character isn't null, then
1104 * the user has specified the old syntax.
1105 * we move the subargument into our
1106 * mod'd argument list.
1107 */
1108 if (av[i][2] != NULL) {
1109 /* first clean things up: */
1110 mav[mac][2] = NULL;
1111
1112 /* now add the separation: */
1113 ++mac; /* inc to next mod'd arg */
1114 if ((mav[mac] = strdup(&av[i][2])) ==
1115 NULL) {
1116 PERR(MALLOCFAIL);
1117 exit(1);
|