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 2006 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 * 45 */ 46 47 #pragma weak wordexp = _wordexp 48 #pragma weak wordfree = _wordfree 49 50 #include "synonyms.h" 51 #include <stdio.h> 52 #include <unistd.h> 53 #include <limits.h> 54 #include <fcntl.h> 55 #include <limits.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <sys/wait.h> 59 #include <unistd.h> 60 #include <wordexp.h> 61 #include <stdio.h> 62 #include <spawn.h> 63 #include <errno.h> 64 65 #define INITIAL 8 /* initial pathv allocation */ 66 #define BUFSZ 256 /* allocation unit of the line buffer */ 67 68 static int append(wordexp_t *, char *); 69 70 extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ 71 72 /* 73 * Needs no locking if fetched only once. 74 * See getenv()/putenv()/setenv(). 75 */ 76 extern const char **environ; 77 78 /* 79 * Do word expansion. 80 * We just pass our arguments to shell with -E option. Note that the 81 * underlying shell must recognize the -E option, and do the right thing 82 * with it. 83 */ 84 int 85 wordexp(const char *word, wordexp_t *wp, int flags) 86 { 87 char options[9]; 88 char *optendp = options; 89 char *argv[4]; 90 const char *path; 91 wordexp_t wptmp; 92 size_t si; 93 int i; 94 pid_t pid; 95 char *line, *eob, *cp; /* word from shell */ 96 int rv = WRDE_ERRNO; 97 int status; 98 int pv[2]; /* pipe from shell stdout */ 99 FILE *fp; /* pipe read stream */ 100 int tmpalloc; 101 char *wd = NULL; 102 const char **env = NULL; 103 const char **envp; 104 const char *ev; 105 int n; 106 posix_spawnattr_t attr; 107 posix_spawn_file_actions_t fact; 108 int error; 109 110 static const char *sun_path = "/bin/ksh"; 111 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 112 113 /* 114 * Do absolute minimum neccessary for the REUSE flag. Eventually 115 * want to be able to actually avoid excessive malloc calls. 116 */ 117 if (flags & WRDE_REUSE) 118 wordfree(wp); 119 120 /* 121 * Initialize wordexp_t 122 * 123 * XPG requires that the struct pointed to by wp not be modified 124 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE. 125 * So we work with wptmp, and only copy wptmp to wp if one of the 126 * previously mentioned conditions is satisfied. 127 */ 128 wptmp = *wp; 129 130 /* 131 * Man page says: 132 * 2. All of the calls must set WRDE_DOOFFS, or all must not 133 * set it. 134 * Therefore, if it's not set, we_offs will always be reset. 135 */ 136 if ((flags & WRDE_DOOFFS) == 0) 137 wptmp.we_offs = 0; 138 139 /* 140 * If we get APPEND|REUSE, how should we do? 141 * allocating buffer anyway to avoid segfault. 142 */ 143 tmpalloc = 0; 144 if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) { 145 wptmp.we_wordc = 0; 146 wptmp.we_wordn = wptmp.we_offs + INITIAL; 147 wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn); 148 if (wptmp.we_wordv == NULL) 149 return (WRDE_NOSPACE); 150 wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs; 151 for (si = 0; si < wptmp.we_offs; si++) 152 wptmp.we_wordv[si] = NULL; 153 tmpalloc = 1; 154 } 155 156 /* 157 * Turn flags into shell options 158 */ 159 *optendp++ = '-'; 160 *optendp++ = (char)0x05; /* ksh -^E */ 161 if (flags & WRDE_UNDEF) 162 *optendp++ = 'u'; 163 if (flags & WRDE_NOCMD) 164 *optendp++ = 'N'; 165 *optendp = '\0'; 166 167 /* 168 * Make sure PWD is in the environment. 169 */ 170 if ((envp = environ) == NULL) { /* can't happen? */ 171 ev = NULL; 172 n = 0; 173 } else { 174 for (n = 0; (ev = envp[n]) != NULL; n++) { 175 if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0) 176 break; 177 } 178 } 179 if (ev == NULL) { /* PWD missing from the environment */ 180 /* allocate a new environment */ 181 if ((env = malloc((n + 2) * sizeof (char *))) == NULL || 182 (wd = malloc(PATH_MAX + 4)) == NULL) 183 goto cleanup; 184 for (i = 0; i < n; i++) 185 env[i] = envp[i]; 186 (void) strcpy(wd, "PWD="); 187 if (getcwd(&wd[4], PATH_MAX) == NULL) 188 (void) strcpy(&wd[4], "/"); 189 env[i] = wd; 190 env[i + 1] = NULL; 191 envp = env; 192 } 193 194 if ((error = posix_spawnattr_init(&attr)) != 0) { 195 errno = error; 196 goto cleanup; 197 } 198 if ((error = posix_spawn_file_actions_init(&fact)) != 0) { 199 (void) posix_spawnattr_destroy(&attr); 200 errno = error; 201 goto cleanup; 202 } 203 204 /* 205 * Set up pipe from shell stdout to "fp" for us 206 */ 207 if (pipe(pv) < 0) { 208 error = errno; 209 (void) posix_spawnattr_destroy(&attr); 210 (void) posix_spawn_file_actions_destroy(&fact); 211 errno = error; 212 goto cleanup; 213 } 214 215 /* 216 * Spawn shell with -E word 217 */ 218 error = posix_spawnattr_setflags(&attr, 219 POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP); 220 if (error == 0) 221 error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1); 222 if (error == 0 && pv[0] != 1) 223 error = posix_spawn_file_actions_addclose(&fact, pv[0]); 224 if (error == 0 && pv[1] != 1) 225 error = posix_spawn_file_actions_addclose(&fact, pv[1]); 226 if (error == 0 && !(flags & WRDE_SHOWERR)) 227 error = posix_spawn_file_actions_addopen(&fact, 2, 228 "/dev/null", O_WRONLY, 0); 229 path = __xpg4 ? xpg4_path : sun_path; 230 argv[0] = strrchr(path, '/') + 1; 231 argv[1] = options; 232 argv[2] = (char *)word; 233 argv[3] = NULL; 234 if (error == 0) 235 error = posix_spawn(&pid, path, &fact, &attr, 236 (char *const *)argv, (char *const *)envp); 237 (void) posix_spawnattr_destroy(&attr); 238 (void) posix_spawn_file_actions_destroy(&fact); 239 (void) close(pv[1]); 240 if (error) { 241 (void) close(pv[0]); 242 errno = error; 243 goto cleanup; 244 } 245 246 if ((fp = fdopen(pv[0], "rF")) == NULL) { 247 error = errno; 248 (void) close(pv[0]); 249 errno = error; 250 goto wait_cleanup; 251 } 252 253 /* 254 * Read words from shell, separated with '\0'. 255 * Since there is no way to disable IFS splitting, 256 * it would be possible to separate the output with '\n'. 257 */ 258 cp = line = malloc(BUFSZ); 259 if (line == NULL) { 260 error = errno; 261 (void) fclose(fp); 262 errno = error; 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 while (waitpid(pid, &status, 0) == -1) { 297 if (errno != EINTR) { 298 if (rv == 0) 299 rv = WRDE_ERRNO; 300 break; 301 } 302 } 303 if (rv == 0) 304 rv = WEXITSTATUS(status); /* shell WRDE_* status */ 305 306 cleanup: 307 if (rv == 0) 308 *wp = wptmp; 309 else if (tmpalloc) 310 wordfree(&wptmp); 311 312 if (env) 313 free(env); 314 if (wd) 315 free(wd); 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 return (rv); 326 } 327 328 /* 329 * Append a word to the wordexp_t structure, growing it as neccessary. 330 */ 331 static int 332 append(wordexp_t *wp, char *str) 333 { 334 char *cp; 335 char **nwp; 336 337 /* 338 * We will be adding one entry and later adding 339 * one more NULL. So we need 2 more free slots. 340 */ 341 if ((wp->we_wordp + wp->we_wordc) == 342 (wp->we_wordv + wp->we_wordn - 1)) { 343 nwp = realloc(wp->we_wordv, 344 (wp->we_wordn + INITIAL) * sizeof (char *)); 345 if (nwp == NULL) 346 return (WRDE_NOSPACE); 347 wp->we_wordn += INITIAL; 348 wp->we_wordv = nwp; 349 wp->we_wordp = wp->we_wordv + wp->we_offs; 350 } 351 if ((cp = strdup(str)) == NULL) 352 return (WRDE_NOSPACE); 353 wp->we_wordp[wp->we_wordc++] = cp; 354 return (0); 355 } 356 357 /* 358 * Free all space owned by wordexp_t. 359 */ 360 void 361 wordfree(wordexp_t *wp) 362 { 363 size_t i; 364 365 if (wp->we_wordv == NULL) 366 return; 367 for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++) 368 free(wp->we_wordv[i]); 369 free((void *)wp->we_wordv); 370 wp->we_wordc = 0; 371 wp->we_wordv = NULL; 372 } 373