1faea1495STim J. Robbins /*- 2faea1495STim J. Robbins * Copyright (c) 2002 Tim J. Robbins. 3faea1495STim J. Robbins * All rights reserved. 4faea1495STim J. Robbins * 5faea1495STim J. Robbins * Redistribution and use in source and binary forms, with or without 6faea1495STim J. Robbins * modification, are permitted provided that the following conditions 7faea1495STim J. Robbins * are met: 8faea1495STim J. Robbins * 1. Redistributions of source code must retain the above copyright 9faea1495STim J. Robbins * notice, this list of conditions and the following disclaimer. 10faea1495STim J. Robbins * 2. Redistributions in binary form must reproduce the above copyright 11faea1495STim J. Robbins * notice, this list of conditions and the following disclaimer in the 12faea1495STim J. Robbins * documentation and/or other materials provided with the distribution. 13faea1495STim J. Robbins * 14faea1495STim J. Robbins * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15faea1495STim J. Robbins * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16faea1495STim J. Robbins * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17faea1495STim J. Robbins * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18faea1495STim J. Robbins * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19faea1495STim J. Robbins * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20faea1495STim J. Robbins * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21faea1495STim J. Robbins * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22faea1495STim J. Robbins * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23faea1495STim J. Robbins * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24faea1495STim J. Robbins * SUCH DAMAGE. 25faea1495STim J. Robbins */ 26faea1495STim J. Robbins 27faea1495STim J. Robbins #include "namespace.h" 28faea1495STim J. Robbins #include <sys/cdefs.h> 29faea1495STim J. Robbins #include <sys/types.h> 30faea1495STim J. Robbins #include <sys/wait.h> 31364e9ccbSJilles Tjoelker #include <errno.h> 32faea1495STim J. Robbins #include <fcntl.h> 33faea1495STim J. Robbins #include <paths.h> 34364e9ccbSJilles Tjoelker #include <signal.h> 35faea1495STim J. Robbins #include <stdio.h> 36faea1495STim J. Robbins #include <stdlib.h> 37faea1495STim J. Robbins #include <string.h> 38faea1495STim J. Robbins #include <unistd.h> 39faea1495STim J. Robbins #include <wordexp.h> 40faea1495STim J. Robbins #include "un-namespace.h" 41faea1495STim J. Robbins 42faea1495STim J. Robbins __FBSDID("$FreeBSD$"); 43faea1495STim J. Robbins 44faea1495STim J. Robbins static int we_askshell(const char *, wordexp_t *, int); 45faea1495STim J. Robbins static int we_check(const char *, int); 46faea1495STim J. Robbins 47faea1495STim J. Robbins /* 48faea1495STim J. Robbins * wordexp -- 49faea1495STim J. Robbins * Perform shell word expansion on `words' and place the resulting list 50faea1495STim J. Robbins * of words in `we'. See wordexp(3). 51faea1495STim J. Robbins * 52faea1495STim J. Robbins * Specified by IEEE Std. 1003.1-2001. 53faea1495STim J. Robbins */ 54faea1495STim J. Robbins int 55faea1495STim J. Robbins wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) 56faea1495STim J. Robbins { 57faea1495STim J. Robbins int error; 58faea1495STim J. Robbins 59faea1495STim J. Robbins if (flags & WRDE_REUSE) 60faea1495STim J. Robbins wordfree(we); 61faea1495STim J. Robbins if ((flags & WRDE_APPEND) == 0) { 62faea1495STim J. Robbins we->we_wordc = 0; 63faea1495STim J. Robbins we->we_wordv = NULL; 64faea1495STim J. Robbins we->we_strings = NULL; 65faea1495STim J. Robbins we->we_nbytes = 0; 66faea1495STim J. Robbins } 67faea1495STim J. Robbins if ((error = we_check(words, flags)) != 0) { 68faea1495STim J. Robbins wordfree(we); 69faea1495STim J. Robbins return (error); 70faea1495STim J. Robbins } 71faea1495STim J. Robbins if ((error = we_askshell(words, we, flags)) != 0) { 72faea1495STim J. Robbins wordfree(we); 73faea1495STim J. Robbins return (error); 74faea1495STim J. Robbins } 75faea1495STim J. Robbins return (0); 76faea1495STim J. Robbins } 77faea1495STim J. Robbins 78364e9ccbSJilles Tjoelker static size_t 79364e9ccbSJilles Tjoelker we_read_fully(int fd, char *buffer, size_t len) 80364e9ccbSJilles Tjoelker { 81364e9ccbSJilles Tjoelker size_t done; 82364e9ccbSJilles Tjoelker ssize_t nread; 83364e9ccbSJilles Tjoelker 84364e9ccbSJilles Tjoelker done = 0; 85364e9ccbSJilles Tjoelker do { 86364e9ccbSJilles Tjoelker nread = _read(fd, buffer + done, len - done); 87364e9ccbSJilles Tjoelker if (nread == -1 && errno == EINTR) 88364e9ccbSJilles Tjoelker continue; 89364e9ccbSJilles Tjoelker if (nread <= 0) 90364e9ccbSJilles Tjoelker break; 91364e9ccbSJilles Tjoelker done += nread; 92364e9ccbSJilles Tjoelker } while (done != len); 93364e9ccbSJilles Tjoelker return done; 94364e9ccbSJilles Tjoelker } 95364e9ccbSJilles Tjoelker 96faea1495STim J. Robbins /* 97faea1495STim J. Robbins * we_askshell -- 98faea1495STim J. Robbins * Use the `wordexp' /bin/sh builtin function to do most of the work 99faea1495STim J. Robbins * in expanding the word string. This function is complicated by 100faea1495STim J. Robbins * memory management. 101faea1495STim J. Robbins */ 102faea1495STim J. Robbins static int 103faea1495STim J. Robbins we_askshell(const char *words, wordexp_t *we, int flags) 104faea1495STim J. Robbins { 105faea1495STim J. Robbins int pdes[2]; /* Pipe to child */ 106faea1495STim J. Robbins char bbuf[9]; /* Buffer for byte count */ 107faea1495STim J. Robbins char wbuf[9]; /* Buffer for word count */ 108faea1495STim J. Robbins long nwords, nbytes; /* Number of words, bytes from child */ 109faea1495STim J. Robbins long i; /* Handy integer */ 110faea1495STim J. Robbins size_t sofs; /* Offset into we->we_strings */ 111faea1495STim J. Robbins size_t vofs; /* Offset into we->we_wordv */ 112faea1495STim J. Robbins pid_t pid; /* Process ID of child */ 113364e9ccbSJilles Tjoelker pid_t wpid; /* waitpid return value */ 114faea1495STim J. Robbins int status; /* Child exit status */ 115364e9ccbSJilles Tjoelker int error; /* Our return value */ 116364e9ccbSJilles Tjoelker int serrno; /* errno to return */ 117faea1495STim J. Robbins char *ifs; /* IFS env. var. */ 118faea1495STim J. Robbins char *np, *p; /* Handy pointers */ 119faea1495STim J. Robbins char *nstrings; /* Temporary for realloc() */ 120faea1495STim J. Robbins char **nwv; /* Temporary for realloc() */ 121364e9ccbSJilles Tjoelker sigset_t newsigblock, oldsigblock; 122faea1495STim J. Robbins 123364e9ccbSJilles Tjoelker serrno = errno; 124faea1495STim J. Robbins if ((ifs = getenv("IFS")) == NULL) 125faea1495STim J. Robbins ifs = " \t\n"; 126faea1495STim J. Robbins 127faea1495STim J. Robbins if (pipe(pdes) < 0) 128faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 129364e9ccbSJilles Tjoelker (void)sigemptyset(&newsigblock); 130364e9ccbSJilles Tjoelker (void)sigaddset(&newsigblock, SIGCHLD); 131364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 132faea1495STim J. Robbins if ((pid = fork()) < 0) { 133364e9ccbSJilles Tjoelker serrno = errno; 1342005f192STim J. Robbins _close(pdes[0]); 1352005f192STim J. Robbins _close(pdes[1]); 136364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 137364e9ccbSJilles Tjoelker errno = serrno; 138faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 139faea1495STim J. Robbins } 140faea1495STim J. Robbins else if (pid == 0) { 141faea1495STim J. Robbins /* 142faea1495STim J. Robbins * We are the child; just get /bin/sh to run the wordexp 143faea1495STim J. Robbins * builtin on `words'. 144faea1495STim J. Robbins */ 145faea1495STim J. Robbins int devnull; 146faea1495STim J. Robbins char *cmd; 147faea1495STim J. Robbins 148364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 1492005f192STim J. Robbins _close(pdes[0]); 1502005f192STim J. Robbins if (_dup2(pdes[1], STDOUT_FILENO) < 0) 151faea1495STim J. Robbins _exit(1); 1522005f192STim J. Robbins _close(pdes[1]); 153faea1495STim J. Robbins if (asprintf(&cmd, "wordexp%c%s\n", *ifs, words) < 0) 154faea1495STim J. Robbins _exit(1); 155faea1495STim J. Robbins if ((flags & WRDE_SHOWERR) == 0) { 1562005f192STim J. Robbins if ((devnull = _open(_PATH_DEVNULL, O_RDWR, 0666)) < 0) 157faea1495STim J. Robbins _exit(1); 1582005f192STim J. Robbins if (_dup2(devnull, STDERR_FILENO) < 0) 159faea1495STim J. Robbins _exit(1); 1602005f192STim J. Robbins _close(devnull); 161faea1495STim J. Robbins } 162faea1495STim J. Robbins execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 1637937c23dSTim J. Robbins "-c", cmd, (char *)NULL); 164faea1495STim J. Robbins _exit(1); 165faea1495STim J. Robbins } 166faea1495STim J. Robbins 167faea1495STim J. Robbins /* 168faea1495STim J. Robbins * We are the parent; read the output of the shell wordexp function, 169faea1495STim J. Robbins * which is a 32-bit hexadecimal word count, a 32-bit hexadecimal 170faea1495STim J. Robbins * byte count (not including terminating null bytes), followed by 171faea1495STim J. Robbins * the expanded words separated by nulls. 172faea1495STim J. Robbins */ 1732005f192STim J. Robbins _close(pdes[1]); 174364e9ccbSJilles Tjoelker if (we_read_fully(pdes[0], wbuf, 8) != 8 || 175364e9ccbSJilles Tjoelker we_read_fully(pdes[0], bbuf, 8) != 8) { 176364e9ccbSJilles Tjoelker error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; 177364e9ccbSJilles Tjoelker serrno = errno; 178364e9ccbSJilles Tjoelker goto cleanup; 179faea1495STim J. Robbins } 180faea1495STim J. Robbins wbuf[8] = bbuf[8] = '\0'; 181faea1495STim J. Robbins nwords = strtol(wbuf, NULL, 16); 182faea1495STim J. Robbins nbytes = strtol(bbuf, NULL, 16) + nwords; 183faea1495STim J. Robbins 184faea1495STim J. Robbins /* 185faea1495STim J. Robbins * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 186faea1495STim J. Robbins * and string storage buffers for the expanded words we're about to 187faea1495STim J. Robbins * read from the child. 188faea1495STim J. Robbins */ 189faea1495STim J. Robbins sofs = we->we_nbytes; 190faea1495STim J. Robbins vofs = we->we_wordc; 191b7114d4aSTim J. Robbins if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) 192fe634ca7STim J. Robbins vofs += we->we_offs; 193faea1495STim J. Robbins we->we_wordc += nwords; 194faea1495STim J. Robbins we->we_nbytes += nbytes; 195faea1495STim J. Robbins if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + 196b7114d4aSTim J. Robbins (flags & WRDE_DOOFFS ? we->we_offs : 0)) * 197faea1495STim J. Robbins sizeof(char *))) == NULL) { 198364e9ccbSJilles Tjoelker error = WRDE_NOSPACE; 199364e9ccbSJilles Tjoelker goto cleanup; 200faea1495STim J. Robbins } 201faea1495STim J. Robbins we->we_wordv = nwv; 202faea1495STim J. Robbins if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 203364e9ccbSJilles Tjoelker error = WRDE_NOSPACE; 204364e9ccbSJilles Tjoelker goto cleanup; 205faea1495STim J. Robbins } 206faea1495STim J. Robbins for (i = 0; i < vofs; i++) 207faea1495STim J. Robbins if (we->we_wordv[i] != NULL) 208faea1495STim J. Robbins we->we_wordv[i] += nstrings - we->we_strings; 209faea1495STim J. Robbins we->we_strings = nstrings; 210faea1495STim J. Robbins 211364e9ccbSJilles Tjoelker if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 212364e9ccbSJilles Tjoelker error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; 213364e9ccbSJilles Tjoelker serrno = errno; 214364e9ccbSJilles Tjoelker goto cleanup; 215faea1495STim J. Robbins } 216faea1495STim J. Robbins 217364e9ccbSJilles Tjoelker error = 0; 218364e9ccbSJilles Tjoelker cleanup: 2192005f192STim J. Robbins _close(pdes[0]); 220364e9ccbSJilles Tjoelker do 221364e9ccbSJilles Tjoelker wpid = _waitpid(pid, &status, 0); 222364e9ccbSJilles Tjoelker while (wpid < 0 && errno == EINTR); 223364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 224364e9ccbSJilles Tjoelker if (error != 0) { 225364e9ccbSJilles Tjoelker errno = serrno; 226364e9ccbSJilles Tjoelker return (error); 227faea1495STim J. Robbins } 228364e9ccbSJilles Tjoelker if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 229364e9ccbSJilles Tjoelker return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); 230faea1495STim J. Robbins 231faea1495STim J. Robbins /* 232faea1495STim J. Robbins * Break the null-terminated expanded word strings out into 233faea1495STim J. Robbins * the vector. 234faea1495STim J. Robbins */ 235b7114d4aSTim J. Robbins if (vofs == 0 && flags & WRDE_DOOFFS) 236faea1495STim J. Robbins while (vofs < we->we_offs) 237faea1495STim J. Robbins we->we_wordv[vofs++] = NULL; 238faea1495STim J. Robbins p = we->we_strings + sofs; 239faea1495STim J. Robbins while (nwords-- != 0) { 240faea1495STim J. Robbins we->we_wordv[vofs++] = p; 241faea1495STim J. Robbins if ((np = memchr(p, '\0', nbytes)) == NULL) 242faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 243faea1495STim J. Robbins nbytes -= np - p + 1; 244faea1495STim J. Robbins p = np + 1; 245faea1495STim J. Robbins } 246faea1495STim J. Robbins we->we_wordv[vofs] = NULL; 247faea1495STim J. Robbins 248faea1495STim J. Robbins return (0); 249faea1495STim J. Robbins } 250faea1495STim J. Robbins 251faea1495STim J. Robbins /* 252faea1495STim J. Robbins * we_check -- 253faea1495STim J. Robbins * Check that the string contains none of the following unquoted 254faea1495STim J. Robbins * special characters: <newline> |&;<>(){} 255faea1495STim J. Robbins * or command substitutions when WRDE_NOCMD is set in flags. 256faea1495STim J. Robbins */ 25797c1c8f8STim J. Robbins static int 258faea1495STim J. Robbins we_check(const char *words, int flags) 259faea1495STim J. Robbins { 260faea1495STim J. Robbins char c; 261faea1495STim J. Robbins int dquote, level, quote, squote; 262faea1495STim J. Robbins 263faea1495STim J. Robbins quote = squote = dquote = 0; 264faea1495STim J. Robbins while ((c = *words++) != '\0') { 265faea1495STim J. Robbins switch (c) { 266faea1495STim J. Robbins case '\\': 267faea1495STim J. Robbins quote ^= 1; 268fe634ca7STim J. Robbins continue; 269faea1495STim J. Robbins case '\'': 270faea1495STim J. Robbins if (quote + dquote == 0) 271faea1495STim J. Robbins squote ^= 1; 272faea1495STim J. Robbins break; 273faea1495STim J. Robbins case '"': 274faea1495STim J. Robbins if (quote + squote == 0) 275faea1495STim J. Robbins dquote ^= 1; 276faea1495STim J. Robbins break; 277faea1495STim J. Robbins case '`': 278faea1495STim J. Robbins if (quote + squote == 0 && flags & WRDE_NOCMD) 279faea1495STim J. Robbins return (WRDE_CMDSUB); 280faea1495STim J. Robbins while ((c = *words++) != '\0' && c != '`') 281faea1495STim J. Robbins if (c == '\\' && (c = *words++) == '\0') 282faea1495STim J. Robbins break; 283faea1495STim J. Robbins if (c == '\0') 284faea1495STim J. Robbins return (WRDE_SYNTAX); 285faea1495STim J. Robbins break; 286faea1495STim J. Robbins case '|': case '&': case ';': case '<': case '>': 287faea1495STim J. Robbins case '{': case '}': case '(': case ')': case '\n': 288faea1495STim J. Robbins if (quote + squote + dquote == 0) 289faea1495STim J. Robbins return (WRDE_BADCHAR); 290faea1495STim J. Robbins break; 291faea1495STim J. Robbins case '$': 292faea1495STim J. Robbins if ((c = *words++) == '\0') 293faea1495STim J. Robbins break; 294fe634ca7STim J. Robbins else if (quote + squote == 0 && c == '(') { 295fe634ca7STim J. Robbins if (flags & WRDE_NOCMD && *words != '(') 296faea1495STim J. Robbins return (WRDE_CMDSUB); 297faea1495STim J. Robbins level = 1; 298faea1495STim J. Robbins while ((c = *words++) != '\0') { 299faea1495STim J. Robbins if (c == '\\') { 300faea1495STim J. Robbins if ((c = *words++) == '\0') 301faea1495STim J. Robbins break; 302faea1495STim J. Robbins } else if (c == '(') 303faea1495STim J. Robbins level++; 304faea1495STim J. Robbins else if (c == ')' && --level == 0) 305faea1495STim J. Robbins break; 306faea1495STim J. Robbins } 307faea1495STim J. Robbins if (c == '\0' || level != 0) 308faea1495STim J. Robbins return (WRDE_SYNTAX); 309fe634ca7STim J. Robbins } else if (quote + squote == 0 && c == '{') { 310faea1495STim J. Robbins level = 1; 311faea1495STim J. Robbins while ((c = *words++) != '\0') { 312faea1495STim J. Robbins if (c == '\\') { 313faea1495STim J. Robbins if ((c = *words++) == '\0') 314faea1495STim J. Robbins break; 315faea1495STim J. Robbins } else if (c == '{') 316faea1495STim J. Robbins level++; 317faea1495STim J. Robbins else if (c == '}' && --level == 0) 318faea1495STim J. Robbins break; 319faea1495STim J. Robbins } 320faea1495STim J. Robbins if (c == '\0' || level != 0) 321faea1495STim J. Robbins return (WRDE_SYNTAX); 322fe634ca7STim J. Robbins } else 323fe634ca7STim J. Robbins c = *--words; 324faea1495STim J. Robbins break; 325faea1495STim J. Robbins default: 326faea1495STim J. Robbins break; 327faea1495STim J. Robbins } 328fe634ca7STim J. Robbins quote = 0; 329faea1495STim J. Robbins } 330faea1495STim J. Robbins if (quote + squote + dquote != 0) 331faea1495STim J. Robbins return (WRDE_SYNTAX); 332faea1495STim J. Robbins 333faea1495STim J. Robbins return (0); 334faea1495STim J. Robbins } 335faea1495STim J. Robbins 336faea1495STim J. Robbins /* 337faea1495STim J. Robbins * wordfree -- 338faea1495STim J. Robbins * Free the result of wordexp(). See wordexp(3). 339faea1495STim J. Robbins * 340faea1495STim J. Robbins * Specified by IEEE Std. 1003.1-2001. 341faea1495STim J. Robbins */ 342faea1495STim J. Robbins void 343faea1495STim J. Robbins wordfree(wordexp_t *we) 344faea1495STim J. Robbins { 345faea1495STim J. Robbins 346faea1495STim J. Robbins if (we == NULL) 347faea1495STim J. Robbins return; 348faea1495STim J. Robbins free(we->we_wordv); 349faea1495STim J. Robbins free(we->we_strings); 350faea1495STim J. Robbins we->we_wordv = NULL; 351faea1495STim J. Robbins we->we_strings = NULL; 352faea1495STim J. Robbins we->we_nbytes = 0; 353faea1495STim J. Robbins we->we_wordc = 0; 354faea1495STim J. Robbins } 355