1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2014 Garrett D'Amore <garrett@damore.org> 23 * Copyright 2012 DEY Storage Systems, Inc. 24 * Copyright (c) 2018, Joyent, Inc. 25 * Copyright 2026 Oxide Computer Company 26 * 27 * Portions of this file developed by DEY Storage Systems, Inc. are licensed 28 * under the terms of the Common Development and Distribution License (CDDL) 29 * version 1.0 only. The use of subsequent versions of the License are 30 * is specifically prohibited unless those terms are not in conflict with 31 * version 1.0 of the License. You can find this license on-line at 32 * http://www.illumos.org/license/CDDL 33 */ 34 /* 35 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 36 * Use is subject to license terms. 37 */ 38 39 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 40 /* All Rights Reserved */ 41 42 43 #include <stdio.h> 44 #include <sys/types.h> 45 #include <sys/wait.h> 46 #include <unistd.h> 47 #include <fcntl.h> 48 #include <string.h> 49 #include <stdarg.h> 50 #include <stdlib.h> 51 #include <limits.h> 52 #include <wchar.h> 53 #include <locale.h> 54 #include <langinfo.h> 55 #include <stropts.h> 56 #include <poll.h> 57 #include <errno.h> 58 #include <spawn.h> 59 #include "getresponse.h" 60 61 extern char **environ; 62 63 #define HEAD 0 64 #define TAIL 1 65 #define FALSE 0 66 #define TRUE 1 67 #define MAXSBUF 255 68 #define MAXIBUF 512 69 #define MAXINSERTS 5 70 #define BUFSIZE LINE_MAX 71 #define MAXARGS 255 72 #define INSPAT_STR "{}" /* default replstr string for -[Ii] */ 73 74 #define QBUF_STARTLEN 255 /* start size of growable string buffer */ 75 #define QBUF_INC 100 /* how much to grow a growable string by */ 76 77 /* We use these macros to help make formatting look "consistent" */ 78 #define EMSG(s) ermsg(gettext(s "\n")) 79 #define EMSG2(s, a) ermsg(gettext(s "\n"), a) 80 #define PERR(s) perror(gettext("xargs: " s)) 81 82 /* Some common error messages */ 83 84 #define LIST2LONG "Argument list too long" 85 #define ARG2LONG "A single argument was greater than %d bytes" 86 #define MALLOCFAIL "Memory allocation failure" 87 #define CORRUPTFILE "Corrupt input file" 88 #define WAITFAIL "Wait failure" 89 #define CHILDSIG "Child killed with signal %d" 90 #define CHILDFAIL "Command could not continue processing data" 91 #define FORKFAIL "Could not fork child" 92 #define EXECFAIL "Could not exec command" 93 #define MISSQUOTE "Missing quote" 94 #define BADESCAPE "Incomplete escape" 95 #define IBUFOVERFLOW "Insert buffer overflow" 96 #define NOCHILDSLOT "No free child slot available" 97 98 #define _(x) gettext(x) 99 100 static wctype_t blank; 101 static char *arglist[MAXARGS+1]; 102 static char argbuf[BUFSIZE * 2 + 1]; 103 static char lastarg[BUFSIZE + 1]; 104 static char **ARGV = arglist; 105 static char *LEOF = "_"; 106 static char *INSPAT = INSPAT_STR; 107 static char ins_buf[MAXIBUF]; 108 static char *p_ibuf; 109 110 static struct inserts { 111 char **p_ARGV; /* where to put newarg ptr in arg list */ 112 char *p_skel; /* ptr to arg template */ 113 } saveargv[MAXINSERTS]; 114 115 static int PROMPT = -1; 116 static int BUFLIM = BUFSIZE; 117 static int MAXPROCS = 1; 118 static int N_ARGS = 0; 119 static int N_args = 0; 120 static int N_lines = 0; 121 static int DASHX = FALSE; 122 static int MORE = TRUE; 123 static int PER_LINE = FALSE; 124 static int LINE_CONT = FALSE; 125 static int EAT_LEAD = FALSE; 126 static int ERR = FALSE; 127 static int OK = TRUE; 128 static int LEGAL = FALSE; 129 static int TRACE = FALSE; 130 static int INSERT = FALSE; 131 static int ZERO = FALSE; 132 static int linesize = 0; 133 static int ibufsize = 0; 134 static int exitstat = 0; /* our exit status */ 135 static int mac; /* modified argc, after parsing */ 136 static char **mav; /* modified argv, after parsing */ 137 static int n_inserts; /* # of insertions. */ 138 static pid_t *procs; /* pids of children */ 139 static int n_procs; /* # of child processes. */ 140 141 /* our usage message: */ 142 #define USAGEMSG "Usage: xargs: [-t] [-p] [-0] [-e[eofstr]] [-E eofstr] "\ 143 "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-P maxprocs] "\ 144 "[-s size] [cmd [args ...]]\n" 145 146 static int echoargs(); 147 static wint_t getwchr(char *, size_t *); 148 static void lcall(char *sub, char **subargs); 149 static void addibuf(struct inserts *p); 150 static void ermsg(char *messages, ...); 151 static char *addarg(char *arg); 152 static void store_str(char **, char *, size_t); 153 static char *getarg(char *); 154 static char *insert(char *pattern, char *subst); 155 static void usage(); 156 static void parseargs(); 157 static int procs_find(pid_t child); 158 static void procs_store(pid_t child); 159 static boolean_t procs_delete(pid_t child); 160 static pid_t procs_waitpid(boolean_t blocking, int *stat_loc); 161 static void procs_wait(boolean_t blocking); 162 163 int 164 main(int argc, char **argv) 165 { 166 int j; 167 long l; 168 struct inserts *psave; 169 int c; 170 int initsize; 171 char *cmdname, **initlist; 172 char *arg; 173 char *next; 174 char *eptr; 175 176 /* initialization */ 177 blank = wctype("blank"); 178 n_inserts = 0; 179 psave = saveargv; 180 (void) setlocale(LC_ALL, ""); 181 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 182 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 183 #endif 184 (void) textdomain(TEXT_DOMAIN); 185 if (init_yes() < 0) { 186 ermsg(_(ERR_MSG_INIT_YES), strerror(errno)); 187 exit(1); 188 } 189 190 parseargs(argc, argv); 191 192 /* handling all of xargs arguments: */ 193 while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:P:s:x")) != EOF) { 194 switch (c) { 195 case '0': 196 ZERO = TRUE; 197 break; 198 199 case 't': /* -t: turn trace mode on */ 200 TRACE = TRUE; 201 break; 202 203 case 'p': /* -p: turn on prompt mode. */ 204 if ((PROMPT = open("/dev/tty", O_RDONLY)) == -1) { 205 PERR("can't read from tty for -p"); 206 } else { 207 TRACE = TRUE; 208 } 209 break; 210 211 case 'e': 212 /* 213 * -e[eofstr]: set/disable end-of-file. 214 * N.B. that an argument *isn't* required here; but 215 * parseargs forced an argument if not was given. The 216 * forced argument is the default... 217 */ 218 LEOF = optarg; /* can be empty */ 219 break; 220 221 case 'E': 222 /* 223 * -E eofstr: change end-of-file string. 224 * eofstr *is* required here, but can be empty: 225 */ 226 LEOF = optarg; 227 break; 228 229 case 'I': 230 /* -I replstr: Insert mode. replstr *is* required. */ 231 INSERT = PER_LINE = LEGAL = EAT_LEAD = TRUE; 232 LINE_CONT = FALSE; 233 N_ARGS = 0; 234 INSPAT = optarg; 235 if (*optarg == '\0') { 236 ermsg(_("Option requires an argument: -%c\n"), 237 c); 238 } 239 break; 240 241 case 'i': 242 /* 243 * -i [replstr]: insert mode, with *optional* replstr. 244 * N.B. that an argument *isn't* required here; if 245 * it's not given, then the string INSPAT_STR will 246 * be assumed. 247 * 248 * Since getopt(3C) doesn't handle the case of an 249 * optional variable argument at all, we have to 250 * parse this by hand: 251 */ 252 253 INSERT = PER_LINE = LEGAL = EAT_LEAD = TRUE; 254 LINE_CONT = FALSE; 255 N_ARGS = 0; 256 if ((optarg != NULL) && (*optarg != '\0')) { 257 INSPAT = optarg; 258 } else { 259 /* 260 * here, there is no next argument. so 261 * we reset INSPAT to the INSPAT_STR. 262 * we *have* to do this, as -i/I may have 263 * been given previously, and XCU4 requires 264 * that only "the last one specified takes 265 * effect". 266 */ 267 INSPAT = INSPAT_STR; 268 } 269 break; 270 271 case 'L': 272 /* 273 * -L number: # of times cmd is executed 274 * number *is* required here: 275 */ 276 PER_LINE = LINE_CONT = TRUE; 277 N_ARGS = 0; 278 INSERT = EAT_LEAD = FALSE; 279 if ((PER_LINE = atoi(optarg)) <= 0) { 280 ermsg(_("#lines must be positive int: %s\n"), 281 optarg); 282 } 283 break; 284 285 case 'l': 286 /* 287 * -l [number]: # of times cmd is executed 288 * N.B. that an argument *isn't* required here; if 289 * it's not given, then 1 is assumed. 290 * 291 * parseargs handles the optional arg processing. 292 */ 293 294 PER_LINE = LINE_CONT = LEGAL = TRUE; 295 N_ARGS = 0; 296 INSERT = EAT_LEAD = FALSE; 297 298 if ((optarg != NULL) && (*optarg != '\0')) { 299 if ((PER_LINE = atoi(optarg)) <= 0) 300 PER_LINE = 1; 301 } 302 break; 303 304 case 'n': /* -n number: # stdin args */ 305 /* 306 * -n number: # stdin args. 307 * number *is* required here: 308 */ 309 if ((N_ARGS = atoi(optarg)) <= 0) { 310 ermsg(_("#args must be positive int: %s\n"), 311 optarg); 312 } else { 313 LEGAL = DASHX || N_ARGS == 1; 314 INSERT = PER_LINE = LINE_CONT = FALSE; 315 } 316 break; 317 318 case 'P': /* -P maxprocs: # of child processses */ 319 errno = 0; 320 l = strtol(optarg, &eptr, 10); 321 if (*eptr != '\0' || errno != 0) { 322 ermsg(_("failed to parse maxprocs (-P): %s\n"), 323 optarg); 324 break; 325 } 326 327 if (l < 0) { 328 ermsg(_("maximum number of processes (-P) " 329 "cannot be negative\n")); 330 break; 331 } 332 333 /* 334 * Come up with an upper bound that'll probably fit in 335 * memory. 336 */ 337 if (l == 0 || l > ((INT_MAX / sizeof (pid_t) >> 1))) { 338 l = INT_MAX / sizeof (pid_t) >> 1; 339 } 340 MAXPROCS = (int)l; 341 break; 342 343 case 's': /* -s size: set max size of each arg list */ 344 BUFLIM = atoi(optarg); 345 if (BUFLIM > BUFSIZE || BUFLIM <= 0) { 346 ermsg(_("0 < max-cmd-line-size <= %d: %s\n"), 347 BUFSIZE, optarg); 348 } 349 break; 350 351 case 'x': /* -x: terminate if args > size limit */ 352 DASHX = LEGAL = TRUE; 353 break; 354 355 default: 356 /* 357 * bad argument. complain and get ready to die. 358 */ 359 usage(); 360 exit(2); 361 break; 362 } 363 } 364 365 /* 366 * if anything called ermsg(), something screwed up, so 367 * we exit early. 368 */ 369 if (OK == FALSE) { 370 usage(); 371 exit(2); 372 } 373 374 /* 375 * we're finished handling xargs's options, so now pick up 376 * the command name (if any), and it's options. 377 */ 378 379 380 mac -= optind; /* dec arg count by what we've processed */ 381 mav += optind; /* inc to current mav */ 382 383 procs = calloc(MAXPROCS, sizeof (pid_t)); 384 if (procs == NULL) { 385 PERR(MALLOCFAIL); 386 exit(1); 387 } 388 389 if (mac <= 0) { /* if there're no more args to process, */ 390 cmdname = "/usr/bin/echo"; /* our default command */ 391 *ARGV++ = addarg(cmdname); /* use the default cmd. */ 392 } else { /* otherwise keep parsing rest of the string. */ 393 /* 394 * note that we can't use getopt(3C), and *must* parse 395 * this by hand, as we don't know apriori what options the 396 * command will take. 397 */ 398 cmdname = *mav; /* get the command name */ 399 400 401 /* pick up the remaining args from the command line: */ 402 while ((OK == TRUE) && (mac-- > 0)) { 403 /* 404 * while we haven't crapped out, and there's 405 * work to do: 406 */ 407 if (INSERT && ! ERR) { 408 if (strstr(*mav, INSPAT) != NULL) { 409 if (++n_inserts > MAXINSERTS) { 410 ermsg(_("too many args " 411 "with %s\n"), INSPAT); 412 ERR = TRUE; 413 } 414 psave->p_ARGV = ARGV; 415 (psave++)->p_skel = *mav; 416 } 417 } 418 *ARGV++ = addarg(*mav++); 419 } 420 } 421 422 /* pick up args from standard input */ 423 424 initlist = ARGV; 425 initsize = linesize; 426 lastarg[0] = '\0'; 427 428 while (OK) { 429 N_args = 0; 430 N_lines = 0; 431 ARGV = initlist; 432 linesize = initsize; 433 next = argbuf; 434 435 while (MORE || (lastarg[0] != '\0')) { 436 int l; 437 438 if (*lastarg != '\0') { 439 arg = strcpy(next, lastarg); 440 *lastarg = '\0'; 441 } else if ((arg = getarg(next)) == NULL) { 442 break; 443 } 444 445 l = strlen(arg) + 1; 446 linesize += l; 447 next += l; 448 449 /* Inserts are handled specially later. */ 450 if ((n_inserts == 0) && (linesize >= BUFLIM)) { 451 /* 452 * Legal indicates hard fail if the list is 453 * truncated due to size. So fail, or if we 454 * cannot create any list because it would be 455 * too big. 456 */ 457 if (LEGAL || N_args == 0) { 458 EMSG(LIST2LONG); 459 procs_wait(B_TRUE); 460 exit(2); 461 /* NOTREACHED */ 462 } 463 464 /* 465 * Otherwise just save argument for later. 466 */ 467 (void) strcpy(lastarg, arg); 468 break; 469 } 470 471 *ARGV++ = arg; 472 473 N_args++; 474 475 if ((PER_LINE && (N_lines >= PER_LINE)) || 476 (N_ARGS && (N_args >= N_ARGS))) { 477 break; 478 } 479 480 481 if ((ARGV - arglist) == MAXARGS) { 482 break; 483 } 484 } 485 486 *ARGV = NULL; 487 if (N_args == 0) { 488 /* Reached the end with no more work. */ 489 break; 490 } 491 492 /* insert arg if requested */ 493 494 if (!ERR && INSERT) { 495 496 p_ibuf = ins_buf; 497 ARGV--; 498 j = ibufsize = 0; 499 for (psave = saveargv; ++j <= n_inserts; ++psave) { 500 addibuf(psave); 501 if (ERR) 502 break; 503 } 504 } 505 *ARGV = NULL; 506 507 if (n_inserts > 0) { 508 /* 509 * if we've done any insertions, re-calculate the 510 * linesize. bomb out if we've exceeded our length. 511 */ 512 linesize = 0; 513 for (ARGV = arglist; *ARGV != NULL; ARGV++) { 514 linesize += strlen(*ARGV) + 1; 515 } 516 if (linesize >= BUFLIM) { 517 EMSG(LIST2LONG); 518 procs_wait(B_TRUE); 519 exit(2); 520 /* NOTREACHED */ 521 } 522 } 523 524 /* exec command */ 525 526 if (!ERR) { 527 if (!MORE && 528 (PER_LINE && N_lines == 0 || N_ARGS && N_args == 0)) 529 exit(exitstat); 530 OK = TRUE; 531 j = TRACE ? echoargs() : TRUE; 532 if (j) { 533 /* 534 * for xcu4, all invocations of cmdname must 535 * return 0, in order for us to return 0. 536 * so if we have a non-zero status here, 537 * quit immediately. 538 */ 539 (void) lcall(cmdname, arglist); 540 } 541 } 542 } 543 544 procs_wait(B_TRUE); 545 546 if (OK) 547 return (exitstat); 548 549 /* 550 * if exitstat was set, to match XCU4 complience, 551 * return that value, otherwise, return 1. 552 */ 553 return (exitstat ? exitstat : 1); 554 } 555 556 static char * 557 addarg(char *arg) 558 { 559 linesize += (strlen(arg) + 1); 560 return (arg); 561 } 562 563 564 static void 565 store_str(char **buffer, char *str, size_t len) 566 { 567 (void) memcpy(*buffer, str, len); 568 (*buffer)[len] = '\0'; 569 *buffer += len; 570 } 571 572 573 static char * 574 getarg(char *arg) 575 { 576 char *xarg = arg; 577 wchar_t c = 0; 578 char mbc[MB_LEN_MAX]; 579 size_t len; 580 int escape = 0; 581 int inquote = 0; 582 int last = 0; 583 584 arg[0] = '\0'; 585 586 while (MORE) { 587 588 len = 0; 589 last = c; 590 c = getwchr(mbc, &len); 591 592 if (((arg - xarg) + len) > BUFLIM) { 593 EMSG2(ARG2LONG, BUFLIM); 594 exit(2); 595 ERR = TRUE; 596 return (NULL); 597 } 598 599 switch (c) { 600 case '\n': 601 if (ZERO) { 602 store_str(&arg, mbc, len); 603 continue; 604 } 605 /* 606 * NB: Some other versions rip off all of the trailing 607 * blanks. The spec only claims that this should 608 * be done for a single blank. We follow the spec. 609 */ 610 if (LINE_CONT && iswctype(last, blank)) { 611 len = 0; 612 *arg = 0; 613 continue; 614 } 615 /* FALLTHRU */ 616 617 case '\0': 618 case WEOF: /* Note WEOF == EOF */ 619 620 if (escape) { 621 EMSG(BADESCAPE); 622 ERR = TRUE; 623 return (NULL); 624 } 625 if (inquote) { 626 EMSG(MISSQUOTE); 627 ERR = TRUE; 628 return (NULL); 629 } 630 631 N_lines++; 632 break; 633 634 case '"': 635 if (ZERO || escape || (inquote == 1)) { 636 /* treat it literally */ 637 escape = 0; 638 store_str(&arg, mbc, len); 639 640 } else if (inquote == 2) { 641 /* terminating double quote */ 642 inquote = 0; 643 644 } else { 645 /* starting quoted string */ 646 inquote = 2; 647 } 648 continue; 649 650 case '\'': 651 if (ZERO || escape || (inquote == 2)) { 652 /* treat it literally */ 653 escape = 0; 654 store_str(&arg, mbc, len); 655 656 } else if (inquote == 1) { 657 /* terminating single quote */ 658 inquote = 0; 659 660 } else { 661 /* starting quoted string */ 662 inquote = 1; 663 } 664 continue; 665 666 case '\\': 667 /* 668 * Any unquoted character can be escaped by 669 * preceding it with a backslash. 670 */ 671 if (ZERO || inquote || escape) { 672 escape = 0; 673 store_str(&arg, mbc, len); 674 } else { 675 escape = 1; 676 } 677 continue; 678 679 default: 680 /* most times we will just want to store it */ 681 if (inquote || escape || ZERO || !iswctype(c, blank)) { 682 escape = 0; 683 store_str(&arg, mbc, len); 684 continue; 685 } 686 if (EAT_LEAD && last == 0) { 687 c = 0; /* Roll it back */ 688 continue; 689 } 690 if (PER_LINE) { 691 store_str(&arg, mbc, len); 692 continue; 693 } 694 695 /* unquoted blank without special handling */ 696 break; 697 } 698 699 /* 700 * At this point we are processing a complete argument. 701 */ 702 if (strcmp(xarg, LEOF) == 0 && *LEOF != '\0') { 703 MORE = FALSE; 704 return (NULL); 705 } 706 if (c == WEOF) { 707 MORE = FALSE; 708 } 709 if (xarg[0] == '\0') 710 continue; 711 break; 712 } 713 714 return (xarg[0] == '\0' ? NULL : xarg); 715 } 716 717 /* 718 * ermsg(): print out an error message, and indicate failure globally. 719 * 720 * Assumes that message has already been gettext()'d. It would be 721 * nice if we could just do the gettext() here, but we can't, since 722 * since xgettext(1) wouldn't be able to pick up our error message. 723 */ 724 /* PRINTFLIKE1 */ 725 static void 726 ermsg(char *messages, ...) 727 { 728 va_list ap; 729 730 va_start(ap, messages); 731 732 (void) fprintf(stderr, "xargs: "); 733 (void) vfprintf(stderr, messages, ap); 734 735 va_end(ap); 736 OK = FALSE; 737 } 738 739 static int 740 echoargs(void) 741 { 742 char **anarg; 743 char **tanarg; /* tmp ptr */ 744 int i; 745 char reply[LINE_MAX]; 746 747 tanarg = anarg = arglist-1; 748 749 /* 750 * write out each argument, separated by a space. the tanarg 751 * nonsense is for xcu4 testsuite compliance - so that an 752 * extra space isn't echoed after the last argument. 753 */ 754 while (*++anarg) { /* while there's an argument */ 755 ++tanarg; /* follow anarg */ 756 (void) write(2, *anarg, strlen(*anarg)); 757 758 if (*++tanarg) { /* if there's another argument: */ 759 (void) write(2, " ", 1); /* add a space */ 760 --tanarg; /* reset back to anarg */ 761 } 762 } 763 if (PROMPT == -1) { 764 (void) write(2, "\n", 1); 765 return (TRUE); 766 } 767 768 (void) write(2, "?...", 4); /* ask the user for input */ 769 770 for (i = 0; i < LINE_MAX && read(PROMPT, &reply[i], 1) > 0; i++) { 771 if (reply[i] == '\n') { 772 if (i == 0) 773 return (FALSE); 774 break; 775 } 776 } 777 if (i < LINE_MAX) 778 reply[i] = '\0'; 779 else 780 reply[LINE_MAX - 1] = '\0'; 781 782 /* flush remainder of line if necessary */ 783 if (i == LINE_MAX) { 784 char bitbucket; 785 786 while ((read(PROMPT, &bitbucket, 1) > 0) && (bitbucket != '\n')) 787 ; 788 } 789 790 return (yes_check(reply)); 791 } 792 793 794 static char * 795 insert(char *pattern, char *subst) 796 { 797 static char buffer[MAXSBUF+1]; 798 int len, ipatlen; 799 char *pat; 800 char *bufend; 801 char *pbuf; 802 803 len = strlen(subst); 804 ipatlen = strlen(INSPAT) - 1; 805 pat = pattern - 1; 806 pbuf = buffer; 807 bufend = &buffer[MAXSBUF]; 808 809 while (*++pat) { 810 if (strncmp(pat, INSPAT, ipatlen + 1) == 0) { 811 if (pbuf + len >= bufend) { 812 break; 813 } else { 814 (void) strcpy(pbuf, subst); 815 pat += ipatlen; 816 pbuf += len; 817 } 818 } else { 819 *pbuf++ = *pat; 820 if (pbuf >= bufend) 821 break; 822 } 823 } 824 825 if (!*pat) { 826 *pbuf = '\0'; 827 return (buffer); 828 } else { 829 ermsg(gettext("Maximum argument size with insertion via %s's " 830 "exceeded\n"), INSPAT); 831 ERR = TRUE; 832 return (NULL); 833 } 834 } 835 836 837 static void 838 addibuf(struct inserts *p) 839 { 840 char *newarg, *skel, *sub; 841 int l; 842 843 skel = p->p_skel; 844 sub = *ARGV; 845 newarg = insert(skel, sub); 846 if (ERR) 847 return; 848 849 l = strlen(newarg) + 1; 850 if ((ibufsize += l) > MAXIBUF) { 851 EMSG(IBUFOVERFLOW); 852 ERR = TRUE; 853 } 854 (void) strcpy(p_ibuf, newarg); 855 *(p->p_ARGV) = p_ibuf; 856 p_ibuf += l; 857 } 858 859 860 /* 861 * getwchr(): get the next wide character. 862 * description: 863 * we get the next character from stdin. This returns WEOF if no 864 * character is present. If ZERO is set, it gets a single byte instead 865 * a wide character. 866 */ 867 static wint_t 868 getwchr(char *mbc, size_t *sz) 869 { 870 size_t i; 871 int c; 872 wchar_t wch; 873 874 i = 0; 875 while (i < MB_CUR_MAX) { 876 877 if ((c = fgetc(stdin)) == EOF) { 878 879 if (i == 0) { 880 /* TRUE EOF has been reached */ 881 return (WEOF); 882 } 883 884 /* 885 * We have some characters in our buffer still so it 886 * must be an invalid character right before EOF. 887 */ 888 break; 889 } 890 mbc[i++] = (char)c; 891 892 /* If this succeeds then we are done */ 893 if (ZERO) { 894 *sz = i; 895 return ((char)c); 896 } 897 if (mbtowc(&wch, mbc, i) != -1) { 898 *sz = i; 899 return ((wint_t)wch); 900 } 901 } 902 903 /* 904 * We have now encountered an illegal character sequence. 905 * There is nothing much we can do at this point but 906 * return an error. If we attempt to recover we may in fact 907 * return garbage as arguments, from the customer's point 908 * of view. After all what if they are feeding us a file 909 * generated in another locale? 910 */ 911 errno = EILSEQ; 912 PERR(CORRUPTFILE); 913 exit(1); 914 /* NOTREACHED */ 915 } 916 917 918 static void 919 lcall(char *sub, char **subargs) 920 { 921 int err; 922 pid_t child; 923 posix_spawnattr_t attr; 924 925 if ((err = posix_spawnattr_init(&attr)) == 0) 926 err = posix_spawnattr_setflags(&attr, POSIX_SPAWN_NOSIGCHLD_NP); 927 if (err != 0) { 928 errno = err; 929 PERR(FORKFAIL); 930 exit(123); 931 } 932 933 for (;;) { 934 err = posix_spawnp(&child, sub, NULL, &attr, subargs, environ); 935 if (err == 0) { 936 (void) posix_spawnattr_destroy(&attr); 937 procs_store(child); 938 /* 939 * Note, if we have used up all of our slots, then this 940 * call may end up blocking. 941 */ 942 procs_wait(B_FALSE); 943 return; 944 } 945 if (err == EAGAIN) { 946 /* A temporary resource shortage. Wait, then retry. */ 947 (void) sleep(1); 948 continue; 949 } 950 errno = err; 951 if (err == EPERM) { 952 /* Creation of the new process was denied. */ 953 PERR(FORKFAIL); 954 exit(123); 955 } 956 /* 957 * The utility could not be invoked. POSIX requires that a 958 * diagnostic is written and that we exit without processing 959 * any remaining input. 960 */ 961 PERR(EXECFAIL); 962 procs_wait(B_TRUE); 963 exit((err == EACCES) ? 126 : 127); 964 } 965 } 966 967 /* 968 * Return the index of child in the procs array. 969 */ 970 static int 971 procs_find(pid_t child) 972 { 973 int i; 974 975 for (i = 0; i < MAXPROCS; i++) { 976 if (procs[i] == child) { 977 return (i); 978 } 979 } 980 981 return (-1); 982 } 983 984 static void 985 procs_store(pid_t child) 986 { 987 int i; 988 989 i = procs_find(0); 990 if (i < 0) { 991 EMSG(NOCHILDSLOT); 992 exit(1); 993 } 994 procs[i] = child; 995 n_procs++; 996 } 997 998 static boolean_t 999 procs_delete(pid_t child) 1000 { 1001 int i; 1002 1003 i = procs_find(child); 1004 if (i < 0) { 1005 return (B_FALSE); 1006 } 1007 1008 procs[i] = (pid_t)0; 1009 n_procs--; 1010 1011 return (B_TRUE); 1012 } 1013 1014 static pid_t 1015 procs_waitpid(boolean_t blocking, int *stat_loc) 1016 { 1017 pid_t child; 1018 int options; 1019 1020 if (n_procs == 0) { 1021 errno = ECHILD; 1022 return (-1); 1023 } 1024 1025 options = 0; 1026 if (!blocking) { 1027 options |= WNOHANG; 1028 } 1029 1030 while ((child = waitpid((pid_t)-1, stat_loc, options)) > 0) { 1031 if (procs_delete(child)) { 1032 break; 1033 } 1034 } 1035 1036 return (child); 1037 } 1038 1039 static void 1040 procs_wait(boolean_t blocking) 1041 { 1042 pid_t child; 1043 int stat_loc; 1044 1045 /* 1046 * If we currently have filled all of our slots, then we need to block 1047 * further execution. 1048 */ 1049 if (n_procs >= MAXPROCS) 1050 blocking = B_TRUE; 1051 while ((child = procs_waitpid(blocking, &stat_loc)) > 0) { 1052 if (WIFSIGNALED(stat_loc)) { 1053 EMSG2(CHILDSIG, WTERMSIG(stat_loc)); 1054 exit(125); 1055 /* NOTREACHED */ 1056 } else if ((WEXITSTATUS(stat_loc) & 0377) == 0377) { 1057 EMSG(CHILDFAIL); 1058 exit(124); 1059 /* NOTREACHED */ 1060 } else { 1061 exitstat |= WEXITSTATUS(stat_loc); 1062 } 1063 } 1064 1065 if (child == (pid_t)(-1) && errno != ECHILD) { 1066 EMSG(WAITFAIL); 1067 exit(122); 1068 /* NOTREACHED */ 1069 } 1070 } 1071 1072 static void 1073 usage() 1074 { 1075 ermsg(_(USAGEMSG)); 1076 OK = FALSE; 1077 } 1078 1079 1080 1081 /* 1082 * parseargs(): modify the args 1083 * since the -e, -i and -l flags all take optional subarguments, 1084 * and getopt(3C) is clueless about this nonsense, we change 1085 * our local argument count and strings to separate this out, 1086 * and make it easier to handle via getopt(3C). 1087 * 1088 * -e -> "-e "" 1089 * -e3 -> "-e "3" 1090 * -Estr -> "-E "str" 1091 * -i -> "-i "{}" 1092 * -irep -> "-i "rep" 1093 * -l -> "-l "1" 1094 * -l10 -> "-l "10" 1095 * 1096 * since the -e, -i and -l flags all take optional subarguments, 1097 */ 1098 static void 1099 parseargs(int ac, char **av) 1100 { 1101 int i; /* current argument */ 1102 int cflag; /* 0 = not processing cmd arg */ 1103 1104 if ((mav = malloc((ac * 2 + 1) * sizeof (char *))) == NULL) { 1105 PERR(MALLOCFAIL); 1106 exit(1); 1107 } 1108 1109 /* for each argument, see if we need to change things: */ 1110 for (i = mac = cflag = 0; (av[i] != NULL) && i < ac; i++, mac++) { 1111 if ((mav[mac] = strdup(av[i])) == NULL) { 1112 PERR(MALLOCFAIL); 1113 exit(1); 1114 } 1115 1116 /* -- has been found or argument list is fully processes */ 1117 if (cflag) 1118 continue; 1119 1120 /* 1121 * if we're doing special processing, and we've got a flag 1122 */ 1123 else if ((av[i][0] == '-') && (av[i][1] != '\0')) { 1124 char *def; 1125 1126 switch (av[i][1]) { 1127 case 'e': 1128 def = ""; /* -e with no arg turns off eof */ 1129 goto process_special; 1130 case 'i': 1131 def = INSPAT_STR; 1132 goto process_special; 1133 case 'l': 1134 def = "1"; 1135 process_special: 1136 /* 1137 * if there's no sub-option, we *must* add 1138 * a default one. this is because xargs must 1139 * be able to distinguish between a valid 1140 * suboption, and a command name. 1141 */ 1142 if (av[i][2] == '\0') { 1143 mav[++mac] = strdup(def); 1144 } else { 1145 /* clear out our version: */ 1146 mav[mac][2] = '\0'; 1147 mav[++mac] = strdup(&av[i][2]); 1148 } 1149 if (mav[mac] == NULL) { 1150 PERR(MALLOCFAIL); 1151 exit(1); 1152 } 1153 break; 1154 1155 /* flags with required subarguments: */ 1156 1157 /* 1158 * there are two separate cases here. either the 1159 * flag can have the normal XCU4 handling 1160 * (of the form: -X subargument); or it can have 1161 * the old solaris 2.[0-4] handling (of the 1162 * form: -Xsubargument). in order to maintain 1163 * backwards compatibility, we must support the 1164 * latter case. we handle the latter possibility 1165 * first so both the old solaris way of handling 1166 * and the new XCU4 way of handling things are allowed. 1167 */ 1168 case 'n': /* FALLTHROUGH */ 1169 case 'P': /* FALLTHROUGH */ 1170 case 's': /* FALLTHROUGH */ 1171 case 'E': /* FALLTHROUGH */ 1172 case 'I': /* FALLTHROUGH */ 1173 case 'L': 1174 /* 1175 * if the second character isn't null, then 1176 * the user has specified the old syntax. 1177 * we move the subargument into our 1178 * mod'd argument list. 1179 */ 1180 if (av[i][2] != '\0') { 1181 /* first clean things up: */ 1182 mav[mac][2] = '\0'; 1183 1184 /* now add the separation: */ 1185 ++mac; /* inc to next mod'd arg */ 1186 if ((mav[mac] = strdup(&av[i][2])) == 1187 NULL) { 1188 PERR(MALLOCFAIL); 1189 exit(1); 1190 } 1191 break; 1192 } 1193 i++; 1194 mac++; 1195 1196 if (av[i] == NULL) { 1197 mav[mac] = NULL; 1198 return; 1199 } 1200 if ((mav[mac] = strdup(av[i])) == NULL) { 1201 PERR(MALLOCFAIL); 1202 exit(1); 1203 } 1204 break; 1205 1206 /* flags */ 1207 case 'p' : 1208 case 't' : 1209 case 'x' : 1210 case '0' : 1211 break; 1212 1213 case '-' : 1214 default: 1215 /* 1216 * here we've hit the cmd argument. so 1217 * we'll stop special processing, as the 1218 * cmd may have a "-i" etc., argument, 1219 * and we don't want to add a "" to it. 1220 */ 1221 cflag = 1; 1222 break; 1223 } 1224 } else if (i > 0) { /* if we're not the 1st arg */ 1225 /* 1226 * if it's not a flag, then it *must* be the cmd. 1227 * set cflag, so we don't mishandle the -[eil] flags. 1228 */ 1229 cflag = 1; 1230 } 1231 } 1232 1233 mav[mac] = NULL; 1234 } 1235