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 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * This code is MKS code ported to Solaris originally with minimum 29 * modifications so that upgrades from MKS would readily integrate. 30 * The MKS basis for this modification was: 31 * 32 * $Id: wordexp.c 1.22 1994/11/21 18:24:50 miked 33 * 34 * Additional modifications have been made to this code to make it 35 * 64-bit clean. 36 */ 37 38 /* 39 * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines. 40 * 41 * Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved. 42 * Modified by Roland Mainz <roland.mainz@nrubsig.org> to support ksh93. 43 * 44 */ 45 46 #pragma ident "%Z%%M% %I% %E% SMI" 47 48 #pragma weak _wordexp = wordexp 49 #pragma weak _wordfree = wordfree 50 51 /* Safeguard against mistakes in the Makefiles */ 52 #ifndef WORDEXP_KSH93 53 #error "WORDEXP_KSH93 not set. Please check the Makefile flags." 54 #endif 55 56 #include "lint.h" 57 #include <stdio.h> 58 #include <unistd.h> 59 #include <limits.h> 60 #include <fcntl.h> 61 #include <limits.h> 62 #include <stdlib.h> 63 #if WORDEXP_KSH93 64 #include <alloca.h> 65 #endif /* WORDEXP_KSH93 */ 66 #include <string.h> 67 #include <sys/wait.h> 68 #include <pthread.h> 69 #include <unistd.h> 70 #include <wordexp.h> 71 #include <stdio.h> 72 #include <spawn.h> 73 #include <errno.h> 74 75 #define INITIAL 8 /* initial pathv allocation */ 76 #define BUFSZ 256 /* allocation unit of the line buffer */ 77 78 /* Local prototypes */ 79 static int append(wordexp_t *, char *); 80 81 #if WORDEXP_KSH93 82 /* 83 * |mystpcpy| - like |strcpy()| but returns the end of the buffer 84 * We'll add this later (and a matching multibyte/widechar version) 85 * as normal libc function. 86 * 87 * Copy string s2 to s1. s1 must be large enough. 88 * return s1-1 (position of string terminator ('\0') in destination buffer). 89 */ 90 static char * 91 mystpcpy(char *s1, const char *s2) 92 { 93 while (*s1++ = *s2++) 94 ; 95 return (s1-1); 96 } 97 98 /* 99 * Do word expansion. 100 * We built a mini-script in |buff| which takes care of all details, 101 * including stdin/stdout/stderr redirection, WRDE_NOCMD mode and 102 * the word expansion itself. 103 */ 104 int 105 wordexp(const char *word, wordexp_t *wp, int flags) 106 { 107 char *args[10]; 108 wordexp_t wptmp; 109 size_t si; 110 int i; 111 pid_t pid; 112 char *line, *eob, *cp; /* word from shell */ 113 int rv = WRDE_ERRNO; 114 int status; 115 int pv[2]; /* pipe from shell stdout */ 116 FILE *fp; /* pipe read stream */ 117 int serrno, tmpalloc; 118 int cancel_state; 119 120 /* 121 * Do absolute minimum necessary for the REUSE flag. Eventually 122 * want to be able to actually avoid excessive malloc calls. 123 */ 124 if (flags & WRDE_REUSE) 125 wordfree(wp); 126 127 /* 128 * Initialize wordexp_t 129 * 130 * XPG requires that the struct pointed to by wp not be modified 131 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE. 132 * So we work with wptmp, and only copy wptmp to wp if one of the 133 * previously mentioned conditions is satisfied. 134 */ 135 wptmp = *wp; 136 137 /* 138 * Man page says: 139 * 2. All of the calls must set WRDE_DOOFFS, or all must not 140 * set it. 141 * Therefore, if it's not set, we_offs will always be reset. 142 */ 143 if ((flags & WRDE_DOOFFS) == 0) 144 wptmp.we_offs = 0; 145 146 /* 147 * If we get APPEND|REUSE, how should we do? 148 * We allocate the buffer anyway to avoid segfault. 149 */ 150 tmpalloc = 0; 151 if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) { 152 wptmp.we_wordc = 0; 153 wptmp.we_wordn = wptmp.we_offs + INITIAL; 154 wptmp.we_wordv = (char **)malloc( 155 sizeof (char *) * wptmp.we_wordn); 156 if (wptmp.we_wordv == NULL) 157 return (WRDE_NOSPACE); 158 wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs; 159 for (si = 0; si < wptmp.we_offs; si++) 160 wptmp.we_wordv[si] = NULL; 161 tmpalloc = 1; 162 } 163 164 /* 165 * The UNIX98 Posix conformance test suite requires 166 * wordexp() to not be a cancellation point. 167 */ 168 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); 169 170 /* 171 * Set up pipe from shell stdout to "fp" for us 172 */ 173 if (pipe(pv) < 0) 174 goto cleanup; 175 176 /* 177 * Fork/exec shell 178 */ 179 180 if ((pid = fork()) == -1) { 181 serrno = errno; 182 (void) close(pv[0]); 183 (void) close(pv[1]); 184 errno = serrno; 185 goto cleanup; 186 } 187 188 if (pid == 0) { /* child */ 189 /* 190 * Calculate size of required buffer (which is size of the 191 * input string (|word|) plus all string literals below; 192 * this value MUST be adjusted each time the literals are 193 * changed!!!!). 194 */ 195 size_t bufflen = 124+strlen(word); /* Length of |buff| */ 196 char *buff = alloca(bufflen); 197 char *currbuffp; /* Current position of '\0' in |buff| */ 198 int i; 199 const char *path; 200 201 (void) dup2(pv[1], 1); 202 (void) close(pv[0]); 203 (void) close(pv[1]); 204 205 path = "/usr/bin/ksh93"; 206 i = 0; 207 208 /* Start filling the buffer */ 209 buff[0] = '\0'; 210 currbuffp = buff; 211 212 if (flags & WRDE_UNDEF) 213 currbuffp = mystpcpy(currbuffp, "set -o nounset ; "); 214 if ((flags & WRDE_SHOWERR) == 0) { 215 /* 216 * The newline ('\n') is neccesary to make sure that 217 * the redirection to /dev/null is already active in 218 * the case the printf below contains a syntax 219 * error... 220 */ 221 currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n"); 222 } 223 /* Squish stdin */ 224 currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n"); 225 226 if (flags & WRDE_NOCMD) { 227 /* 228 * Switch to restricted shell (rksh) mode here to 229 * put the word expansion into a "cage" which 230 * prevents users from executing external commands 231 * (outside those listed by ${PATH} (which we set 232 * explicitly to /usr/no/such/path/element/)). 233 */ 234 currbuffp = mystpcpy(currbuffp, "set -o restricted\n"); 235 236 (void) putenv("PATH=/usr/no/such/path/element/"); 237 238 } 239 240 (void) snprintf(currbuffp, bufflen, 241 "print -f \"%%s\\000\" %s", word); 242 243 args[i++] = strrchr(path, '/') + 1; 244 args[i++] = "-c"; 245 args[i++] = buff; 246 args[i++] = NULL; 247 248 (void) execv(path, args); 249 _exit(127); 250 } 251 252 (void) close(pv[1]); 253 254 if ((fp = fdopen(pv[0], "rF")) == NULL) { 255 serrno = errno; 256 (void) close(pv[0]); 257 errno = serrno; 258 goto wait_cleanup; 259 } 260 261 /* 262 * Read words from shell, separated with '\0'. 263 * Since there is no way to disable IFS splitting, 264 * it would be possible to separate the output with '\n'. 265 */ 266 cp = line = malloc(BUFSZ); 267 if (line == NULL) { 268 (void) fclose(fp); 269 rv = WRDE_NOSPACE; 270 goto wait_cleanup; 271 } 272 eob = line + BUFSZ; 273 274 rv = 0; 275 while ((i = getc(fp)) != EOF) { 276 *cp++ = (char)i; 277 if (i == '\0') { 278 cp = line; 279 if ((rv = append(&wptmp, cp)) != 0) { 280 break; 281 } 282 } 283 if (cp == eob) { 284 size_t bs = (eob - line); 285 char *nl; 286 287 if ((nl = realloc(line, bs + BUFSZ)) == NULL) { 288 rv = WRDE_NOSPACE; 289 break; 290 } 291 line = nl; 292 cp = line + bs; 293 eob = cp + BUFSZ; 294 } 295 } 296 297 wptmp.we_wordp[wptmp.we_wordc] = NULL; 298 299 free(line); 300 (void) fclose(fp); /* kill shell if still writing */ 301 302 wait_cleanup: 303 if (waitpid(pid, &status, 0) == -1) 304 rv = WRDE_ERRNO; 305 else if (rv == 0) 306 rv = WEXITSTATUS(status); /* shell WRDE_* status */ 307 308 cleanup: 309 if (rv == 0) 310 *wp = wptmp; 311 else { 312 if (tmpalloc) 313 wordfree(&wptmp); 314 } 315 316 /* 317 * Map ksh errors to wordexp() errors 318 */ 319 if (rv == 4) 320 rv = WRDE_CMDSUB; 321 else if (rv == 5) 322 rv = WRDE_BADVAL; 323 else if (rv == 6) 324 rv = WRDE_SYNTAX; 325 326 (void) pthread_setcancelstate(cancel_state, NULL); 327 return (rv); 328 } 329 330 #else /* WORDEXP_KSH93 */ 331 332 extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ 333 334 /* 335 * Needs no locking if fetched only once. 336 * See getenv()/putenv()/setenv(). 337 */ 338 extern const char **_environ; 339 340 /* 341 * Do word expansion. 342 * We just pass our arguments to shell with -E option. Note that the 343 * underlying shell must recognize the -E option, and do the right thing 344 * with it. 345 */ 346 int 347 wordexp(const char *word, wordexp_t *wp, int flags) 348 { 349 char options[9]; 350 char *optendp = options; 351 char *argv[4]; 352 const char *path; 353 wordexp_t wptmp; 354 size_t si; 355 int i; 356 pid_t pid; 357 char *line, *eob, *cp; /* word from shell */ 358 int rv = WRDE_ERRNO; 359 int status; 360 int pv[2]; /* pipe from shell stdout */ 361 FILE *fp; /* pipe read stream */ 362 int tmpalloc; 363 char *wd = NULL; 364 const char **env = NULL; 365 const char **envp; 366 const char *ev; 367 int n; 368 posix_spawnattr_t attr; 369 posix_spawn_file_actions_t fact; 370 int error; 371 int cancel_state; 372 373 static const char *sun_path = "/bin/ksh"; 374 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 375 376 /* 377 * Do absolute minimum neccessary for the REUSE flag. Eventually 378 * want to be able to actually avoid excessive malloc calls. 379 */ 380 if (flags & WRDE_REUSE) 381 wordfree(wp); 382 383 /* 384 * Initialize wordexp_t 385 * 386 * XPG requires that the struct pointed to by wp not be modified 387 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE. 388 * So we work with wptmp, and only copy wptmp to wp if one of the 389 * previously mentioned conditions is satisfied. 390 */ 391 wptmp = *wp; 392 393 /* 394 * Man page says: 395 * 2. All of the calls must set WRDE_DOOFFS, or all must not 396 * set it. 397 * Therefore, if it's not set, we_offs will always be reset. 398 */ 399 if ((flags & WRDE_DOOFFS) == 0) 400 wptmp.we_offs = 0; 401 402 /* 403 * If we get APPEND|REUSE, how should we do? 404 * allocating buffer anyway to avoid segfault. 405 */ 406 tmpalloc = 0; 407 if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) { 408 wptmp.we_wordc = 0; 409 wptmp.we_wordn = wptmp.we_offs + INITIAL; 410 wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn); 411 if (wptmp.we_wordv == NULL) 412 return (WRDE_NOSPACE); 413 wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs; 414 for (si = 0; si < wptmp.we_offs; si++) 415 wptmp.we_wordv[si] = NULL; 416 tmpalloc = 1; 417 } 418 419 /* 420 * The UNIX98 Posix conformance test suite requires 421 * wordexp() to not be a cancellation point. 422 */ 423 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); 424 425 /* 426 * Turn flags into shell options 427 */ 428 *optendp++ = '-'; 429 *optendp++ = (char)0x05; /* ksh -^E */ 430 if (flags & WRDE_UNDEF) 431 *optendp++ = 'u'; 432 if (flags & WRDE_NOCMD) 433 *optendp++ = 'N'; 434 *optendp = '\0'; 435 436 /* 437 * Make sure PWD is in the environment. 438 */ 439 if ((envp = _environ) == NULL) { 440 /* can happen when processing a SunOS 4.x AOUT file */ 441 ev = NULL; 442 n = 0; 443 } else { 444 for (n = 0; (ev = envp[n]) != NULL; n++) { 445 if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0) 446 break; 447 } 448 } 449 if (ev == NULL) { /* PWD missing from the environment */ 450 /* allocate a new environment */ 451 if ((env = malloc((n + 2) * sizeof (char *))) == NULL || 452 (wd = malloc(PATH_MAX + 4)) == NULL) 453 goto cleanup; 454 for (i = 0; i < n; i++) 455 env[i] = envp[i]; 456 (void) strcpy(wd, "PWD="); 457 if (getcwd(&wd[4], PATH_MAX) == NULL) 458 (void) strcpy(&wd[4], "/"); 459 env[i] = wd; 460 env[i + 1] = NULL; 461 envp = env; 462 } 463 464 if ((error = posix_spawnattr_init(&attr)) != 0) { 465 errno = error; 466 goto cleanup; 467 } 468 if ((error = posix_spawn_file_actions_init(&fact)) != 0) { 469 (void) posix_spawnattr_destroy(&attr); 470 errno = error; 471 goto cleanup; 472 } 473 474 /* 475 * Set up pipe from shell stdout to "fp" for us 476 */ 477 if (pipe(pv) < 0) { 478 error = errno; 479 (void) posix_spawnattr_destroy(&attr); 480 (void) posix_spawn_file_actions_destroy(&fact); 481 errno = error; 482 goto cleanup; 483 } 484 485 /* 486 * Spawn shell with -E word 487 */ 488 error = posix_spawnattr_setflags(&attr, 489 POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP); 490 if (error == 0) 491 error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1); 492 if (error == 0 && pv[0] != 1) 493 error = posix_spawn_file_actions_addclose(&fact, pv[0]); 494 if (error == 0 && pv[1] != 1) 495 error = posix_spawn_file_actions_addclose(&fact, pv[1]); 496 if (error == 0 && !(flags & WRDE_SHOWERR)) 497 error = posix_spawn_file_actions_addopen(&fact, 2, 498 "/dev/null", O_WRONLY, 0); 499 path = __xpg4 ? xpg4_path : sun_path; 500 argv[0] = strrchr(path, '/') + 1; 501 argv[1] = options; 502 argv[2] = (char *)word; 503 argv[3] = NULL; 504 if (error == 0) 505 error = posix_spawn(&pid, path, &fact, &attr, 506 (char *const *)argv, (char *const *)envp); 507 (void) posix_spawnattr_destroy(&attr); 508 (void) posix_spawn_file_actions_destroy(&fact); 509 (void) close(pv[1]); 510 if (error) { 511 (void) close(pv[0]); 512 errno = error; 513 goto cleanup; 514 } 515 516 if ((fp = fdopen(pv[0], "rF")) == NULL) { 517 error = errno; 518 (void) close(pv[0]); 519 errno = error; 520 goto wait_cleanup; 521 } 522 523 /* 524 * Read words from shell, separated with '\0'. 525 * Since there is no way to disable IFS splitting, 526 * it would be possible to separate the output with '\n'. 527 */ 528 cp = line = malloc(BUFSZ); 529 if (line == NULL) { 530 error = errno; 531 (void) fclose(fp); 532 errno = error; 533 goto wait_cleanup; 534 } 535 eob = line + BUFSZ; 536 537 rv = 0; 538 while ((i = getc(fp)) != EOF) { 539 *cp++ = (char)i; 540 if (i == '\0') { 541 cp = line; 542 if ((rv = append(&wptmp, cp)) != 0) { 543 break; 544 } 545 } 546 if (cp == eob) { 547 size_t bs = (eob - line); 548 char *nl; 549 550 if ((nl = realloc(line, bs + BUFSZ)) == NULL) { 551 rv = WRDE_NOSPACE; 552 break; 553 } 554 line = nl; 555 cp = line + bs; 556 eob = cp + BUFSZ; 557 } 558 } 559 560 wptmp.we_wordp[wptmp.we_wordc] = NULL; 561 562 free(line); 563 (void) fclose(fp); /* kill shell if still writing */ 564 565 wait_cleanup: 566 while (waitpid(pid, &status, 0) == -1) { 567 if (errno != EINTR) { 568 if (rv == 0) 569 rv = WRDE_ERRNO; 570 break; 571 } 572 } 573 if (rv == 0) 574 rv = WEXITSTATUS(status); /* shell WRDE_* status */ 575 576 cleanup: 577 if (rv == 0) 578 *wp = wptmp; 579 else if (tmpalloc) 580 wordfree(&wptmp); 581 582 if (env) 583 free(env); 584 if (wd) 585 free(wd); 586 /* 587 * Map ksh errors to wordexp() errors 588 */ 589 if (rv == 4) 590 rv = WRDE_CMDSUB; 591 else if (rv == 5) 592 rv = WRDE_BADVAL; 593 else if (rv == 6) 594 rv = WRDE_SYNTAX; 595 596 (void) pthread_setcancelstate(cancel_state, NULL); 597 return (rv); 598 } 599 600 #endif /* WORDEXP_KSH93 */ 601 602 /* 603 * Append a word to the wordexp_t structure, growing it as necessary. 604 */ 605 static int 606 append(wordexp_t *wp, char *str) 607 { 608 char *cp; 609 char **nwp; 610 611 /* 612 * We will be adding one entry and later adding 613 * one more NULL. So we need 2 more free slots. 614 */ 615 if ((wp->we_wordp + wp->we_wordc) == 616 (wp->we_wordv + wp->we_wordn - 1)) { 617 nwp = realloc(wp->we_wordv, 618 (wp->we_wordn + INITIAL) * sizeof (char *)); 619 if (nwp == NULL) 620 return (WRDE_NOSPACE); 621 wp->we_wordn += INITIAL; 622 wp->we_wordv = nwp; 623 wp->we_wordp = wp->we_wordv + wp->we_offs; 624 } 625 if ((cp = strdup(str)) == NULL) 626 return (WRDE_NOSPACE); 627 wp->we_wordp[wp->we_wordc++] = cp; 628 return (0); 629 } 630 631 /* 632 * Free all space owned by wordexp_t. 633 */ 634 void 635 wordfree(wordexp_t *wp) 636 { 637 size_t i; 638 639 if (wp->we_wordv == NULL) 640 return; 641 for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++) 642 free(wp->we_wordv[i]); 643 free((void *)wp->we_wordv); 644 wp->we_wordc = 0; 645 wp->we_wordv = NULL; 646 } 647