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 *np, *p; /* Handy pointers */ 118faea1495STim J. Robbins char *nstrings; /* Temporary for realloc() */ 119faea1495STim J. Robbins char **nwv; /* Temporary for realloc() */ 120364e9ccbSJilles Tjoelker sigset_t newsigblock, oldsigblock; 121faea1495STim J. Robbins 122364e9ccbSJilles Tjoelker serrno = errno; 123faea1495STim J. Robbins 124*f6d7148dSJilles Tjoelker if (pipe2(pdes, O_CLOEXEC) < 0) 125faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 126364e9ccbSJilles Tjoelker (void)sigemptyset(&newsigblock); 127364e9ccbSJilles Tjoelker (void)sigaddset(&newsigblock, SIGCHLD); 128364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 129faea1495STim J. Robbins if ((pid = fork()) < 0) { 130364e9ccbSJilles Tjoelker serrno = errno; 1312005f192STim J. Robbins _close(pdes[0]); 1322005f192STim J. Robbins _close(pdes[1]); 133364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 134364e9ccbSJilles Tjoelker errno = serrno; 135faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 136faea1495STim J. Robbins } 137faea1495STim J. Robbins else if (pid == 0) { 138faea1495STim J. Robbins /* 139faea1495STim J. Robbins * We are the child; just get /bin/sh to run the wordexp 140faea1495STim J. Robbins * builtin on `words'. 141faea1495STim J. Robbins */ 142364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 143*f6d7148dSJilles Tjoelker if ((pdes[1] != STDOUT_FILENO ? 144*f6d7148dSJilles Tjoelker _dup2(pdes[1], STDOUT_FILENO) : 145*f6d7148dSJilles Tjoelker _fcntl(pdes[1], F_SETFD, 0)) < 0) 146faea1495STim J. Robbins _exit(1); 147faea1495STim J. Robbins execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 148ae4c676cSJilles Tjoelker "-c", "eval \"$1\";eval \"wordexp $2\"", "", 149ae4c676cSJilles Tjoelker flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", words, 150ae4c676cSJilles Tjoelker (char *)NULL); 151faea1495STim J. Robbins _exit(1); 152faea1495STim J. Robbins } 153faea1495STim J. Robbins 154faea1495STim J. Robbins /* 155faea1495STim J. Robbins * We are the parent; read the output of the shell wordexp function, 156faea1495STim J. Robbins * which is a 32-bit hexadecimal word count, a 32-bit hexadecimal 157faea1495STim J. Robbins * byte count (not including terminating null bytes), followed by 158faea1495STim J. Robbins * the expanded words separated by nulls. 159faea1495STim J. Robbins */ 1602005f192STim J. Robbins _close(pdes[1]); 161364e9ccbSJilles Tjoelker if (we_read_fully(pdes[0], wbuf, 8) != 8 || 162364e9ccbSJilles Tjoelker we_read_fully(pdes[0], bbuf, 8) != 8) { 163364e9ccbSJilles Tjoelker error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; 164364e9ccbSJilles Tjoelker serrno = errno; 165364e9ccbSJilles Tjoelker goto cleanup; 166faea1495STim J. Robbins } 167faea1495STim J. Robbins wbuf[8] = bbuf[8] = '\0'; 168faea1495STim J. Robbins nwords = strtol(wbuf, NULL, 16); 169faea1495STim J. Robbins nbytes = strtol(bbuf, NULL, 16) + nwords; 170faea1495STim J. Robbins 171faea1495STim J. Robbins /* 172faea1495STim J. Robbins * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 173faea1495STim J. Robbins * and string storage buffers for the expanded words we're about to 174faea1495STim J. Robbins * read from the child. 175faea1495STim J. Robbins */ 176faea1495STim J. Robbins sofs = we->we_nbytes; 177faea1495STim J. Robbins vofs = we->we_wordc; 178b7114d4aSTim J. Robbins if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) 179fe634ca7STim J. Robbins vofs += we->we_offs; 180faea1495STim J. Robbins we->we_wordc += nwords; 181faea1495STim J. Robbins we->we_nbytes += nbytes; 182faea1495STim J. Robbins if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + 183b7114d4aSTim J. Robbins (flags & WRDE_DOOFFS ? we->we_offs : 0)) * 184faea1495STim J. Robbins sizeof(char *))) == NULL) { 185364e9ccbSJilles Tjoelker error = WRDE_NOSPACE; 186364e9ccbSJilles Tjoelker goto cleanup; 187faea1495STim J. Robbins } 188faea1495STim J. Robbins we->we_wordv = nwv; 189faea1495STim J. Robbins if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 190364e9ccbSJilles Tjoelker error = WRDE_NOSPACE; 191364e9ccbSJilles Tjoelker goto cleanup; 192faea1495STim J. Robbins } 193faea1495STim J. Robbins for (i = 0; i < vofs; i++) 194faea1495STim J. Robbins if (we->we_wordv[i] != NULL) 195faea1495STim J. Robbins we->we_wordv[i] += nstrings - we->we_strings; 196faea1495STim J. Robbins we->we_strings = nstrings; 197faea1495STim J. Robbins 198364e9ccbSJilles Tjoelker if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 199364e9ccbSJilles Tjoelker error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; 200364e9ccbSJilles Tjoelker serrno = errno; 201364e9ccbSJilles Tjoelker goto cleanup; 202faea1495STim J. Robbins } 203faea1495STim J. Robbins 204364e9ccbSJilles Tjoelker error = 0; 205364e9ccbSJilles Tjoelker cleanup: 2062005f192STim J. Robbins _close(pdes[0]); 207364e9ccbSJilles Tjoelker do 208364e9ccbSJilles Tjoelker wpid = _waitpid(pid, &status, 0); 209364e9ccbSJilles Tjoelker while (wpid < 0 && errno == EINTR); 210364e9ccbSJilles Tjoelker (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 211364e9ccbSJilles Tjoelker if (error != 0) { 212364e9ccbSJilles Tjoelker errno = serrno; 213364e9ccbSJilles Tjoelker return (error); 214faea1495STim J. Robbins } 215364e9ccbSJilles Tjoelker if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 216364e9ccbSJilles Tjoelker return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); 217faea1495STim J. Robbins 218faea1495STim J. Robbins /* 219faea1495STim J. Robbins * Break the null-terminated expanded word strings out into 220faea1495STim J. Robbins * the vector. 221faea1495STim J. Robbins */ 222b7114d4aSTim J. Robbins if (vofs == 0 && flags & WRDE_DOOFFS) 223faea1495STim J. Robbins while (vofs < we->we_offs) 224faea1495STim J. Robbins we->we_wordv[vofs++] = NULL; 225faea1495STim J. Robbins p = we->we_strings + sofs; 226faea1495STim J. Robbins while (nwords-- != 0) { 227faea1495STim J. Robbins we->we_wordv[vofs++] = p; 228faea1495STim J. Robbins if ((np = memchr(p, '\0', nbytes)) == NULL) 229faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 230faea1495STim J. Robbins nbytes -= np - p + 1; 231faea1495STim J. Robbins p = np + 1; 232faea1495STim J. Robbins } 233faea1495STim J. Robbins we->we_wordv[vofs] = NULL; 234faea1495STim J. Robbins 235faea1495STim J. Robbins return (0); 236faea1495STim J. Robbins } 237faea1495STim J. Robbins 238faea1495STim J. Robbins /* 239faea1495STim J. Robbins * we_check -- 240faea1495STim J. Robbins * Check that the string contains none of the following unquoted 241faea1495STim J. Robbins * special characters: <newline> |&;<>(){} 242faea1495STim J. Robbins * or command substitutions when WRDE_NOCMD is set in flags. 243faea1495STim J. Robbins */ 24497c1c8f8STim J. Robbins static int 245faea1495STim J. Robbins we_check(const char *words, int flags) 246faea1495STim J. Robbins { 247faea1495STim J. Robbins char c; 248faea1495STim J. Robbins int dquote, level, quote, squote; 249faea1495STim J. Robbins 250faea1495STim J. Robbins quote = squote = dquote = 0; 251faea1495STim J. Robbins while ((c = *words++) != '\0') { 252faea1495STim J. Robbins switch (c) { 253faea1495STim J. Robbins case '\\': 2548d0f6b5fSJilles Tjoelker if (squote == 0) 255faea1495STim J. Robbins quote ^= 1; 256fe634ca7STim J. Robbins continue; 257faea1495STim J. Robbins case '\'': 258faea1495STim J. Robbins if (quote + dquote == 0) 259faea1495STim J. Robbins squote ^= 1; 260faea1495STim J. Robbins break; 261faea1495STim J. Robbins case '"': 262faea1495STim J. Robbins if (quote + squote == 0) 263faea1495STim J. Robbins dquote ^= 1; 264faea1495STim J. Robbins break; 265faea1495STim J. Robbins case '`': 266faea1495STim J. Robbins if (quote + squote == 0 && flags & WRDE_NOCMD) 267faea1495STim J. Robbins return (WRDE_CMDSUB); 268faea1495STim J. Robbins while ((c = *words++) != '\0' && c != '`') 269faea1495STim J. Robbins if (c == '\\' && (c = *words++) == '\0') 270faea1495STim J. Robbins break; 271faea1495STim J. Robbins if (c == '\0') 272faea1495STim J. Robbins return (WRDE_SYNTAX); 273faea1495STim J. Robbins break; 274faea1495STim J. Robbins case '|': case '&': case ';': case '<': case '>': 275faea1495STim J. Robbins case '{': case '}': case '(': case ')': case '\n': 276faea1495STim J. Robbins if (quote + squote + dquote == 0) 277faea1495STim J. Robbins return (WRDE_BADCHAR); 278faea1495STim J. Robbins break; 279faea1495STim J. Robbins case '$': 280faea1495STim J. Robbins if ((c = *words++) == '\0') 281faea1495STim J. Robbins break; 282fe634ca7STim J. Robbins else if (quote + squote == 0 && c == '(') { 283fe634ca7STim J. Robbins if (flags & WRDE_NOCMD && *words != '(') 284faea1495STim J. Robbins return (WRDE_CMDSUB); 285faea1495STim J. Robbins level = 1; 286faea1495STim J. Robbins while ((c = *words++) != '\0') { 287faea1495STim J. Robbins if (c == '\\') { 288faea1495STim J. Robbins if ((c = *words++) == '\0') 289faea1495STim J. Robbins break; 290faea1495STim J. Robbins } else if (c == '(') 291faea1495STim J. Robbins level++; 292faea1495STim J. Robbins else if (c == ')' && --level == 0) 293faea1495STim J. Robbins break; 294faea1495STim J. Robbins } 295faea1495STim J. Robbins if (c == '\0' || level != 0) 296faea1495STim J. Robbins return (WRDE_SYNTAX); 297fe634ca7STim J. Robbins } else if (quote + squote == 0 && c == '{') { 298faea1495STim J. Robbins level = 1; 299faea1495STim J. Robbins while ((c = *words++) != '\0') { 300faea1495STim J. Robbins if (c == '\\') { 301faea1495STim J. Robbins if ((c = *words++) == '\0') 302faea1495STim J. Robbins break; 303faea1495STim J. Robbins } else if (c == '{') 304faea1495STim J. Robbins level++; 305faea1495STim J. Robbins else if (c == '}' && --level == 0) 306faea1495STim J. Robbins break; 307faea1495STim J. Robbins } 308faea1495STim J. Robbins if (c == '\0' || level != 0) 309faea1495STim J. Robbins return (WRDE_SYNTAX); 310fe634ca7STim J. Robbins } else 3110c0349bfSGarrett Wollman --words; 312faea1495STim J. Robbins break; 313faea1495STim J. Robbins default: 314faea1495STim J. Robbins break; 315faea1495STim J. Robbins } 316fe634ca7STim J. Robbins quote = 0; 317faea1495STim J. Robbins } 318faea1495STim J. Robbins if (quote + squote + dquote != 0) 319faea1495STim J. Robbins return (WRDE_SYNTAX); 320faea1495STim J. Robbins 321faea1495STim J. Robbins return (0); 322faea1495STim J. Robbins } 323faea1495STim J. Robbins 324faea1495STim J. Robbins /* 325faea1495STim J. Robbins * wordfree -- 326faea1495STim J. Robbins * Free the result of wordexp(). See wordexp(3). 327faea1495STim J. Robbins * 328faea1495STim J. Robbins * Specified by IEEE Std. 1003.1-2001. 329faea1495STim J. Robbins */ 330faea1495STim J. Robbins void 331faea1495STim J. Robbins wordfree(wordexp_t *we) 332faea1495STim J. Robbins { 333faea1495STim J. Robbins 334faea1495STim J. Robbins if (we == NULL) 335faea1495STim J. Robbins return; 336faea1495STim J. Robbins free(we->we_wordv); 337faea1495STim J. Robbins free(we->we_strings); 338faea1495STim J. Robbins we->we_wordv = NULL; 339faea1495STim J. Robbins we->we_strings = NULL; 340faea1495STim J. Robbins we->we_nbytes = 0; 341faea1495STim J. Robbins we->we_wordc = 0; 342faea1495STim J. Robbins } 343