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