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 */ 106*89cead33SJilles Tjoelker char buf[18]; /* Buffer for byte and word count */ 107faea1495STim J. Robbins long nwords, nbytes; /* Number of words, bytes from child */ 108faea1495STim J. Robbins long i; /* Handy integer */ 109faea1495STim J. Robbins size_t sofs; /* Offset into we->we_strings */ 110faea1495STim J. Robbins size_t vofs; /* Offset into we->we_wordv */ 111faea1495STim J. Robbins pid_t pid; /* Process ID of child */ 112364e9ccbSJilles Tjoelker pid_t wpid; /* waitpid return value */ 113faea1495STim J. Robbins int status; /* Child exit status */ 114364e9ccbSJilles Tjoelker int error; /* Our return value */ 115364e9ccbSJilles Tjoelker int serrno; /* errno to return */ 116faea1495STim J. Robbins char *np, *p; /* Handy pointers */ 117faea1495STim J. Robbins char *nstrings; /* Temporary for realloc() */ 118faea1495STim J. Robbins char **nwv; /* Temporary for realloc() */ 119364e9ccbSJilles Tjoelker sigset_t newsigblock, oldsigblock; 1202f61288cSJilles Tjoelker const char *ifs; 121*89cead33SJilles Tjoelker char save; 122faea1495STim J. Robbins 123364e9ccbSJilles Tjoelker serrno = errno; 1242f61288cSJilles Tjoelker ifs = getenv("IFS"); 125faea1495STim J. Robbins 126f6d7148dSJilles Tjoelker if (pipe2(pdes, O_CLOEXEC) < 0) 127faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 128364e9ccbSJilles Tjoelker (void)sigemptyset(&newsigblock); 129364e9ccbSJilles Tjoelker (void)sigaddset(&newsigblock, SIGCHLD); 130364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 131faea1495STim J. Robbins if ((pid = fork()) < 0) { 132364e9ccbSJilles Tjoelker serrno = errno; 1332005f192STim J. Robbins _close(pdes[0]); 1342005f192STim J. Robbins _close(pdes[1]); 135364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 136364e9ccbSJilles Tjoelker errno = serrno; 137faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 138faea1495STim J. Robbins } 139faea1495STim J. Robbins else if (pid == 0) { 140faea1495STim J. Robbins /* 141842ad8acSJilles Tjoelker * We are the child; make /bin/sh expand `words'. 142faea1495STim J. Robbins */ 143364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 144f6d7148dSJilles Tjoelker if ((pdes[1] != STDOUT_FILENO ? 145f6d7148dSJilles Tjoelker _dup2(pdes[1], STDOUT_FILENO) : 146f6d7148dSJilles Tjoelker _fcntl(pdes[1], F_SETFD, 0)) < 0) 147faea1495STim J. Robbins _exit(1); 148faea1495STim J. Robbins execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 149*89cead33SJilles Tjoelker "-c", "IFS=$1;eval \"$2\";eval \"echo;set -- $3\";" 150*89cead33SJilles Tjoelker "IFS=;a=\"$*\";printf '%08x' \"$#\" \"${#a}\";" 151*89cead33SJilles Tjoelker "printf '%s\\0' \"$@\"", 152842ad8acSJilles Tjoelker "", 1532f61288cSJilles Tjoelker ifs != NULL ? ifs : " \t\n", 154ae4c676cSJilles Tjoelker flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", words, 155ae4c676cSJilles Tjoelker (char *)NULL); 156faea1495STim J. Robbins _exit(1); 157faea1495STim J. Robbins } 158faea1495STim J. Robbins 159faea1495STim J. Robbins /* 160faea1495STim J. Robbins * We are the parent; read the output of the shell wordexp function, 161*89cead33SJilles Tjoelker * which is a byte indicating that the words were parsed successfully, 162*89cead33SJilles Tjoelker * a 32-bit hexadecimal word count, a 32-bit hexadecimal byte count 163*89cead33SJilles Tjoelker * (not including terminating null bytes), followed by the expanded 164*89cead33SJilles Tjoelker * words separated by nulls. 165faea1495STim J. Robbins */ 1662005f192STim J. Robbins _close(pdes[1]); 167*89cead33SJilles Tjoelker switch (we_read_fully(pdes[0], buf, 17)) { 168*89cead33SJilles Tjoelker case 1: 169*89cead33SJilles Tjoelker error = WRDE_BADVAL; 170*89cead33SJilles Tjoelker serrno = errno; 171*89cead33SJilles Tjoelker goto cleanup; 172*89cead33SJilles Tjoelker case 17: 173*89cead33SJilles Tjoelker break; 174*89cead33SJilles Tjoelker default: 175*89cead33SJilles Tjoelker error = WRDE_SYNTAX; 176364e9ccbSJilles Tjoelker serrno = errno; 177364e9ccbSJilles Tjoelker goto cleanup; 178faea1495STim J. Robbins } 179*89cead33SJilles Tjoelker save = buf[9]; 180*89cead33SJilles Tjoelker buf[9] = '\0'; 181*89cead33SJilles Tjoelker nwords = strtol(buf + 1, NULL, 16); 182*89cead33SJilles Tjoelker buf[9] = save; 183*89cead33SJilles Tjoelker buf[17] = '\0'; 184*89cead33SJilles Tjoelker nbytes = strtol(buf + 9, NULL, 16) + nwords; 185faea1495STim J. Robbins 186faea1495STim J. Robbins /* 187faea1495STim J. Robbins * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 188faea1495STim J. Robbins * and string storage buffers for the expanded words we're about to 189faea1495STim J. Robbins * read from the child. 190faea1495STim J. Robbins */ 191faea1495STim J. Robbins sofs = we->we_nbytes; 192faea1495STim J. Robbins vofs = we->we_wordc; 193b7114d4aSTim J. Robbins if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) 194fe634ca7STim J. Robbins vofs += we->we_offs; 195faea1495STim J. Robbins we->we_wordc += nwords; 196faea1495STim J. Robbins we->we_nbytes += nbytes; 197faea1495STim J. Robbins if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + 198b7114d4aSTim J. Robbins (flags & WRDE_DOOFFS ? we->we_offs : 0)) * 199faea1495STim J. Robbins sizeof(char *))) == NULL) { 200364e9ccbSJilles Tjoelker error = WRDE_NOSPACE; 201364e9ccbSJilles Tjoelker goto cleanup; 202faea1495STim J. Robbins } 203faea1495STim J. Robbins we->we_wordv = nwv; 204faea1495STim J. Robbins if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 205364e9ccbSJilles Tjoelker error = WRDE_NOSPACE; 206364e9ccbSJilles Tjoelker goto cleanup; 207faea1495STim J. Robbins } 208faea1495STim J. Robbins for (i = 0; i < vofs; i++) 209faea1495STim J. Robbins if (we->we_wordv[i] != NULL) 210faea1495STim J. Robbins we->we_wordv[i] += nstrings - we->we_strings; 211faea1495STim J. Robbins we->we_strings = nstrings; 212faea1495STim J. Robbins 213364e9ccbSJilles Tjoelker if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 214*89cead33SJilles Tjoelker error = WRDE_NOSPACE; /* abort for unknown reason */ 215364e9ccbSJilles Tjoelker serrno = errno; 216364e9ccbSJilles Tjoelker goto cleanup; 217faea1495STim J. Robbins } 218faea1495STim J. Robbins 219364e9ccbSJilles Tjoelker error = 0; 220364e9ccbSJilles Tjoelker cleanup: 2212005f192STim J. Robbins _close(pdes[0]); 222364e9ccbSJilles Tjoelker do 223364e9ccbSJilles Tjoelker wpid = _waitpid(pid, &status, 0); 224364e9ccbSJilles Tjoelker while (wpid < 0 && errno == EINTR); 225364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 226364e9ccbSJilles Tjoelker if (error != 0) { 227364e9ccbSJilles Tjoelker errno = serrno; 228364e9ccbSJilles Tjoelker return (error); 229faea1495STim J. Robbins } 230364e9ccbSJilles Tjoelker if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 231*89cead33SJilles Tjoelker return (WRDE_NOSPACE); /* abort for unknown reason */ 232faea1495STim J. Robbins 233faea1495STim J. Robbins /* 234faea1495STim J. Robbins * Break the null-terminated expanded word strings out into 235faea1495STim J. Robbins * the vector. 236faea1495STim J. Robbins */ 237b7114d4aSTim J. Robbins if (vofs == 0 && flags & WRDE_DOOFFS) 238faea1495STim J. Robbins while (vofs < we->we_offs) 239faea1495STim J. Robbins we->we_wordv[vofs++] = NULL; 240faea1495STim J. Robbins p = we->we_strings + sofs; 241faea1495STim J. Robbins while (nwords-- != 0) { 242faea1495STim J. Robbins we->we_wordv[vofs++] = p; 243faea1495STim J. Robbins if ((np = memchr(p, '\0', nbytes)) == NULL) 244faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 245faea1495STim J. Robbins nbytes -= np - p + 1; 246faea1495STim J. Robbins p = np + 1; 247faea1495STim J. Robbins } 248faea1495STim J. Robbins we->we_wordv[vofs] = NULL; 249faea1495STim J. Robbins 250faea1495STim J. Robbins return (0); 251faea1495STim J. Robbins } 252faea1495STim J. Robbins 253faea1495STim J. Robbins /* 254faea1495STim J. Robbins * we_check -- 255faea1495STim J. Robbins * Check that the string contains none of the following unquoted 256faea1495STim J. Robbins * special characters: <newline> |&;<>(){} 257faea1495STim J. Robbins * or command substitutions when WRDE_NOCMD is set in flags. 258faea1495STim J. Robbins */ 25997c1c8f8STim J. Robbins static int 260faea1495STim J. Robbins we_check(const char *words, int flags) 261faea1495STim J. Robbins { 262faea1495STim J. Robbins char c; 263faea1495STim J. Robbins int dquote, level, quote, squote; 264faea1495STim J. Robbins 265faea1495STim J. Robbins quote = squote = dquote = 0; 266faea1495STim J. Robbins while ((c = *words++) != '\0') { 267faea1495STim J. Robbins switch (c) { 268faea1495STim J. Robbins case '\\': 2698d0f6b5fSJilles Tjoelker if (squote == 0) 270faea1495STim J. Robbins quote ^= 1; 271fe634ca7STim J. Robbins continue; 272faea1495STim J. Robbins case '\'': 273faea1495STim J. Robbins if (quote + dquote == 0) 274faea1495STim J. Robbins squote ^= 1; 275faea1495STim J. Robbins break; 276faea1495STim J. Robbins case '"': 277faea1495STim J. Robbins if (quote + squote == 0) 278faea1495STim J. Robbins dquote ^= 1; 279faea1495STim J. Robbins break; 280faea1495STim J. Robbins case '`': 281faea1495STim J. Robbins if (quote + squote == 0 && flags & WRDE_NOCMD) 282faea1495STim J. Robbins return (WRDE_CMDSUB); 283faea1495STim J. Robbins while ((c = *words++) != '\0' && c != '`') 284faea1495STim J. Robbins if (c == '\\' && (c = *words++) == '\0') 285faea1495STim J. Robbins break; 286faea1495STim J. Robbins if (c == '\0') 287faea1495STim J. Robbins return (WRDE_SYNTAX); 288faea1495STim J. Robbins break; 289faea1495STim J. Robbins case '|': case '&': case ';': case '<': case '>': 290faea1495STim J. Robbins case '{': case '}': case '(': case ')': case '\n': 291faea1495STim J. Robbins if (quote + squote + dquote == 0) 292faea1495STim J. Robbins return (WRDE_BADCHAR); 293faea1495STim J. Robbins break; 294faea1495STim J. Robbins case '$': 295faea1495STim J. Robbins if ((c = *words++) == '\0') 296faea1495STim J. Robbins break; 297fe634ca7STim J. Robbins else if (quote + squote == 0 && c == '(') { 298fe634ca7STim J. Robbins if (flags & WRDE_NOCMD && *words != '(') 299faea1495STim J. Robbins return (WRDE_CMDSUB); 300faea1495STim J. Robbins level = 1; 301faea1495STim J. Robbins while ((c = *words++) != '\0') { 302faea1495STim J. Robbins if (c == '\\') { 303faea1495STim J. Robbins if ((c = *words++) == '\0') 304faea1495STim J. Robbins break; 305faea1495STim J. Robbins } else if (c == '(') 306faea1495STim J. Robbins level++; 307faea1495STim J. Robbins else if (c == ')' && --level == 0) 308faea1495STim J. Robbins break; 309faea1495STim J. Robbins } 310faea1495STim J. Robbins if (c == '\0' || level != 0) 311faea1495STim J. Robbins return (WRDE_SYNTAX); 312fe634ca7STim J. Robbins } else if (quote + squote == 0 && c == '{') { 313faea1495STim J. Robbins level = 1; 314faea1495STim J. Robbins while ((c = *words++) != '\0') { 315faea1495STim J. Robbins if (c == '\\') { 316faea1495STim J. Robbins if ((c = *words++) == '\0') 317faea1495STim J. Robbins break; 318faea1495STim J. Robbins } else if (c == '{') 319faea1495STim J. Robbins level++; 320faea1495STim J. Robbins else if (c == '}' && --level == 0) 321faea1495STim J. Robbins break; 322faea1495STim J. Robbins } 323faea1495STim J. Robbins if (c == '\0' || level != 0) 324faea1495STim J. Robbins return (WRDE_SYNTAX); 325fe634ca7STim J. Robbins } else 3260c0349bfSGarrett Wollman --words; 327faea1495STim J. Robbins break; 328faea1495STim J. Robbins default: 329faea1495STim J. Robbins break; 330faea1495STim J. Robbins } 331fe634ca7STim J. Robbins quote = 0; 332faea1495STim J. Robbins } 333faea1495STim J. Robbins if (quote + squote + dquote != 0) 334faea1495STim J. Robbins return (WRDE_SYNTAX); 335faea1495STim J. Robbins 336faea1495STim J. Robbins return (0); 337faea1495STim J. Robbins } 338faea1495STim J. Robbins 339faea1495STim J. Robbins /* 340faea1495STim J. Robbins * wordfree -- 341faea1495STim J. Robbins * Free the result of wordexp(). See wordexp(3). 342faea1495STim J. Robbins * 343faea1495STim J. Robbins * Specified by IEEE Std. 1003.1-2001. 344faea1495STim J. Robbins */ 345faea1495STim J. Robbins void 346faea1495STim J. Robbins wordfree(wordexp_t *we) 347faea1495STim J. Robbins { 348faea1495STim J. Robbins 349faea1495STim J. Robbins if (we == NULL) 350faea1495STim J. Robbins return; 351faea1495STim J. Robbins free(we->we_wordv); 352faea1495STim J. Robbins free(we->we_strings); 353faea1495STim J. Robbins we->we_wordv = NULL; 354faea1495STim J. Robbins we->we_strings = NULL; 355faea1495STim J. Robbins we->we_nbytes = 0; 356faea1495STim J. Robbins we->we_wordc = 0; 357faea1495STim J. Robbins } 358