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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This code is MKS code ported to Solaris originally with minimum 31 * modifications so that upgrades from MKS would readily integrate. 32 * The MKS basis for this modification was: 33 * 34 * $Id: wordexp.c 1.22 1994/11/21 18:24:50 miked 35 * 36 * Additional modifications have been made to this code to make it 37 * 64-bit clean. 38 */ 39 40 /* 41 * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines. 42 * 43 * Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved. 44 * Modified by Roland Mainz <roland.mainz@nrubsig.org> to support ksh93. 45 * 46 */ 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 "synonyms.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) { /* can't happen? */ 440 ev = NULL; 441 n = 0; 442 } else { 443 for (n = 0; (ev = envp[n]) != NULL; n++) { 444 if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0) 445 break; 446 } 447 } 448 if (ev == NULL) { /* PWD missing from the environment */ 449 /* allocate a new environment */ 450 if ((env = malloc((n + 2) * sizeof (char *))) == NULL || 451 (wd = malloc(PATH_MAX + 4)) == NULL) 452 goto cleanup; 453 for (i = 0; i < n; i++) 454 env[i] = envp[i]; 455 (void) strcpy(wd, "PWD="); 456 if (getcwd(&wd[4], PATH_MAX) == NULL) 457 (void) strcpy(&wd[4], "/"); 458 env[i] = wd; 459 env[i + 1] = NULL; 460 envp = env; 461 } 462 463 if ((error = posix_spawnattr_init(&attr)) != 0) { 464 errno = error; 465 goto cleanup; 466 } 467 if ((error = posix_spawn_file_actions_init(&fact)) != 0) { 468 (void) posix_spawnattr_destroy(&attr); 469 errno = error; 470 goto cleanup; 471 } 472 473 /* 474 * Set up pipe from shell stdout to "fp" for us 475 */ 476 if (pipe(pv) < 0) { 477 error = errno; 478 (void) posix_spawnattr_destroy(&attr); 479 (void) posix_spawn_file_actions_destroy(&fact); 480 errno = error; 481 goto cleanup; 482 } 483 484 /* 485 * Spawn shell with -E word 486 */ 487 error = posix_spawnattr_setflags(&attr, 488 POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP); 489 if (error == 0) 490 error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1); 491 if (error == 0 && pv[0] != 1) 492 error = posix_spawn_file_actions_addclose(&fact, pv[0]); 493 if (error == 0 && pv[1] != 1) 494 error = posix_spawn_file_actions_addclose(&fact, pv[1]); 495 if (error == 0 && !(flags & WRDE_SHOWERR)) 496 error = posix_spawn_file_actions_addopen(&fact, 2, 497 "/dev/null", O_WRONLY, 0); 498 path = __xpg4 ? xpg4_path : sun_path; 499 argv[0] = strrchr(path, '/') + 1; 500 argv[1] = options; 501 argv[2] = (char *)word; 502 argv[3] = NULL; 503 if (error == 0) 504 error = posix_spawn(&pid, path, &fact, &attr, 505 (char *const *)argv, (char *const *)envp); 506 (void) posix_spawnattr_destroy(&attr); 507 (void) posix_spawn_file_actions_destroy(&fact); 508 (void) close(pv[1]); 509 if (error) { 510 (void) close(pv[0]); 511 errno = error; 512 goto cleanup; 513 } 514 515 if ((fp = fdopen(pv[0], "rF")) == NULL) { 516 error = errno; 517 (void) close(pv[0]); 518 errno = error; 519 goto wait_cleanup; 520 } 521 522 /* 523 * Read words from shell, separated with '\0'. 524 * Since there is no way to disable IFS splitting, 525 * it would be possible to separate the output with '\n'. 526 */ 527 cp = line = malloc(BUFSZ); 528 if (line == NULL) { 529 error = errno; 530 (void) fclose(fp); 531 errno = error; 532 goto wait_cleanup; 533 } 534 eob = line + BUFSZ; 535 536 rv = 0; 537 while ((i = getc(fp)) != EOF) { 538 *cp++ = (char)i; 539 if (i == '\0') { 540 cp = line; 541 if ((rv = append(&wptmp, cp)) != 0) { 542 break; 543 } 544 } 545 if (cp == eob) { 546 size_t bs = (eob - line); 547 char *nl; 548 549 if ((nl = realloc(line, bs + BUFSZ)) == NULL) { 550 rv = WRDE_NOSPACE; 551 break; 552 } 553 line = nl; 554 cp = line + bs; 555 eob = cp + BUFSZ; 556 } 557 } 558 559 wptmp.we_wordp[wptmp.we_wordc] = NULL; 560 561 free(line); 562 (void) fclose(fp); /* kill shell if still writing */ 563 564 wait_cleanup: 565 while (waitpid(pid, &status, 0) == -1) { 566 if (errno != EINTR) { 567 if (rv == 0) 568 rv = WRDE_ERRNO; 569 break; 570 } 571 } 572 if (rv == 0) 573 rv = WEXITSTATUS(status); /* shell WRDE_* status */ 574 575 cleanup: 576 if (rv == 0) 577 *wp = wptmp; 578 else if (tmpalloc) 579 wordfree(&wptmp); 580 581 if (env) 582 free(env); 583 if (wd) 584 free(wd); 585 /* 586 * Map ksh errors to wordexp() errors 587 */ 588 if (rv == 4) 589 rv = WRDE_CMDSUB; 590 else if (rv == 5) 591 rv = WRDE_BADVAL; 592 else if (rv == 6) 593 rv = WRDE_SYNTAX; 594 595 (void) pthread_setcancelstate(cancel_state, NULL); 596 return (rv); 597 } 598 599 #endif /* WORDEXP_KSH93 */ 600 601 /* 602 * Append a word to the wordexp_t structure, growing it as necessary. 603 */ 604 static int 605 append(wordexp_t *wp, char *str) 606 { 607 char *cp; 608 char **nwp; 609 610 /* 611 * We will be adding one entry and later adding 612 * one more NULL. So we need 2 more free slots. 613 */ 614 if ((wp->we_wordp + wp->we_wordc) == 615 (wp->we_wordv + wp->we_wordn - 1)) { 616 nwp = realloc(wp->we_wordv, 617 (wp->we_wordn + INITIAL) * sizeof (char *)); 618 if (nwp == NULL) 619 return (WRDE_NOSPACE); 620 wp->we_wordn += INITIAL; 621 wp->we_wordv = nwp; 622 wp->we_wordp = wp->we_wordv + wp->we_offs; 623 } 624 if ((cp = strdup(str)) == NULL) 625 return (WRDE_NOSPACE); 626 wp->we_wordp[wp->we_wordc++] = cp; 627 return (0); 628 } 629 630 /* 631 * Free all space owned by wordexp_t. 632 */ 633 void 634 wordfree(wordexp_t *wp) 635 { 636 size_t i; 637 638 if (wp->we_wordv == NULL) 639 return; 640 for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++) 641 free(wp->we_wordv[i]); 642 free((void *)wp->we_wordv); 643 wp->we_wordc = 0; 644 wp->we_wordv = NULL; 645 } 646