1108288Stjr/*- 2108288Stjr * Copyright (c) 2002 Tim J. Robbins. 3108288Stjr * All rights reserved. 4108288Stjr * 5108288Stjr * Redistribution and use in source and binary forms, with or without 6108288Stjr * modification, are permitted provided that the following conditions 7108288Stjr * are met: 8108288Stjr * 1. Redistributions of source code must retain the above copyright 9108288Stjr * notice, this list of conditions and the following disclaimer. 10108288Stjr * 2. Redistributions in binary form must reproduce the above copyright 11108288Stjr * notice, this list of conditions and the following disclaimer in the 12108288Stjr * documentation and/or other materials provided with the distribution. 13108288Stjr * 14108288Stjr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15108288Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16108288Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17108288Stjr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18108288Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19108288Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20108288Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21108288Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22108288Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23108288Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24108288Stjr * SUCH DAMAGE. 25108288Stjr */ 26108288Stjr 27108288Stjr#include "namespace.h" 28108288Stjr#include <sys/cdefs.h> 29108288Stjr#include <sys/types.h> 30108288Stjr#include <sys/wait.h> 31198406Sjilles#include <errno.h> 32108288Stjr#include <fcntl.h> 33108288Stjr#include <paths.h> 34198406Sjilles#include <signal.h> 35108288Stjr#include <stdio.h> 36108288Stjr#include <stdlib.h> 37108288Stjr#include <string.h> 38108288Stjr#include <unistd.h> 39108288Stjr#include <wordexp.h> 40108288Stjr#include "un-namespace.h" 41108288Stjr 42108288Stjr__FBSDID("$FreeBSD$"); 43108288Stjr 44108288Stjrstatic int we_askshell(const char *, wordexp_t *, int); 45108288Stjrstatic int we_check(const char *, int); 46108288Stjr 47108288Stjr/* 48108288Stjr * wordexp -- 49108288Stjr * Perform shell word expansion on `words' and place the resulting list 50108288Stjr * of words in `we'. See wordexp(3). 51108288Stjr * 52108288Stjr * Specified by IEEE Std. 1003.1-2001. 53108288Stjr */ 54108288Stjrint 55108288Stjrwordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) 56108288Stjr{ 57108288Stjr int error; 58108288Stjr 59108288Stjr if (flags & WRDE_REUSE) 60108288Stjr wordfree(we); 61108288Stjr if ((flags & WRDE_APPEND) == 0) { 62108288Stjr we->we_wordc = 0; 63108288Stjr we->we_wordv = NULL; 64108288Stjr we->we_strings = NULL; 65108288Stjr we->we_nbytes = 0; 66108288Stjr } 67108288Stjr if ((error = we_check(words, flags)) != 0) { 68108288Stjr wordfree(we); 69108288Stjr return (error); 70108288Stjr } 71108288Stjr if ((error = we_askshell(words, we, flags)) != 0) { 72108288Stjr wordfree(we); 73108288Stjr return (error); 74108288Stjr } 75108288Stjr return (0); 76108288Stjr} 77108288Stjr 78198406Sjillesstatic size_t 79198406Sjilleswe_read_fully(int fd, char *buffer, size_t len) 80198406Sjilles{ 81198406Sjilles size_t done; 82198406Sjilles ssize_t nread; 83198406Sjilles 84198406Sjilles done = 0; 85198406Sjilles do { 86198406Sjilles nread = _read(fd, buffer + done, len - done); 87198406Sjilles if (nread == -1 && errno == EINTR) 88198406Sjilles continue; 89198406Sjilles if (nread <= 0) 90198406Sjilles break; 91198406Sjilles done += nread; 92198406Sjilles } while (done != len); 93198406Sjilles return done; 94198406Sjilles} 95198406Sjilles 96108288Stjr/* 97108288Stjr * we_askshell -- 98108288Stjr * Use the `wordexp' /bin/sh builtin function to do most of the work 99108288Stjr * in expanding the word string. This function is complicated by 100108288Stjr * memory management. 101108288Stjr */ 102108288Stjrstatic int 103108288Stjrwe_askshell(const char *words, wordexp_t *we, int flags) 104108288Stjr{ 105108288Stjr int pdes[2]; /* Pipe to child */ 106108288Stjr char bbuf[9]; /* Buffer for byte count */ 107108288Stjr char wbuf[9]; /* Buffer for word count */ 108108288Stjr long nwords, nbytes; /* Number of words, bytes from child */ 109108288Stjr long i; /* Handy integer */ 110108288Stjr size_t sofs; /* Offset into we->we_strings */ 111108288Stjr size_t vofs; /* Offset into we->we_wordv */ 112108288Stjr pid_t pid; /* Process ID of child */ 113198406Sjilles pid_t wpid; /* waitpid return value */ 114108288Stjr int status; /* Child exit status */ 115198406Sjilles int error; /* Our return value */ 116198406Sjilles int serrno; /* errno to return */ 117108288Stjr char *ifs; /* IFS env. var. */ 118108288Stjr char *np, *p; /* Handy pointers */ 119108288Stjr char *nstrings; /* Temporary for realloc() */ 120108288Stjr char **nwv; /* Temporary for realloc() */ 121198406Sjilles sigset_t newsigblock, oldsigblock; 122108288Stjr 123198406Sjilles serrno = errno; 124108288Stjr if ((ifs = getenv("IFS")) == NULL) 125108288Stjr ifs = " \t\n"; 126108288Stjr 127108288Stjr if (pipe(pdes) < 0) 128108288Stjr return (WRDE_NOSPACE); /* XXX */ 129198406Sjilles (void)sigemptyset(&newsigblock); 130198406Sjilles (void)sigaddset(&newsigblock, SIGCHLD); 131198406Sjilles (void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 132108288Stjr if ((pid = fork()) < 0) { 133198406Sjilles serrno = errno; 134108865Stjr _close(pdes[0]); 135108865Stjr _close(pdes[1]); 136198406Sjilles (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 137198406Sjilles errno = serrno; 138108288Stjr return (WRDE_NOSPACE); /* XXX */ 139108288Stjr } 140108288Stjr else if (pid == 0) { 141108288Stjr /* 142108288Stjr * We are the child; just get /bin/sh to run the wordexp 143108288Stjr * builtin on `words'. 144108288Stjr */ 145108288Stjr int devnull; 146108288Stjr char *cmd; 147108288Stjr 148198406Sjilles (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 149108865Stjr _close(pdes[0]); 150108865Stjr if (_dup2(pdes[1], STDOUT_FILENO) < 0) 151108288Stjr _exit(1); 152108865Stjr _close(pdes[1]); 153108288Stjr if (asprintf(&cmd, "wordexp%c%s\n", *ifs, words) < 0) 154108288Stjr _exit(1); 155108288Stjr if ((flags & WRDE_SHOWERR) == 0) { 156108865Stjr if ((devnull = _open(_PATH_DEVNULL, O_RDWR, 0666)) < 0) 157108288Stjr _exit(1); 158108865Stjr if (_dup2(devnull, STDERR_FILENO) < 0) 159108288Stjr _exit(1); 160108865Stjr _close(devnull); 161108288Stjr } 162108288Stjr execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 163128050Stjr "-c", cmd, (char *)NULL); 164108288Stjr _exit(1); 165108288Stjr } 166108288Stjr 167108288Stjr /* 168108288Stjr * We are the parent; read the output of the shell wordexp function, 169108288Stjr * which is a 32-bit hexadecimal word count, a 32-bit hexadecimal 170108288Stjr * byte count (not including terminating null bytes), followed by 171108288Stjr * the expanded words separated by nulls. 172108288Stjr */ 173108865Stjr _close(pdes[1]); 174198406Sjilles if (we_read_fully(pdes[0], wbuf, 8) != 8 || 175198406Sjilles we_read_fully(pdes[0], bbuf, 8) != 8) { 176198406Sjilles error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; 177198406Sjilles serrno = errno; 178198406Sjilles goto cleanup; 179108288Stjr } 180108288Stjr wbuf[8] = bbuf[8] = '\0'; 181108288Stjr nwords = strtol(wbuf, NULL, 16); 182108288Stjr nbytes = strtol(bbuf, NULL, 16) + nwords; 183108288Stjr 184108288Stjr /* 185108288Stjr * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 186108288Stjr * and string storage buffers for the expanded words we're about to 187108288Stjr * read from the child. 188108288Stjr */ 189108288Stjr sofs = we->we_nbytes; 190108288Stjr vofs = we->we_wordc; 191131331Stjr if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) 192108641Stjr vofs += we->we_offs; 193108288Stjr we->we_wordc += nwords; 194108288Stjr we->we_nbytes += nbytes; 195108288Stjr if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + 196131331Stjr (flags & WRDE_DOOFFS ? we->we_offs : 0)) * 197108288Stjr sizeof(char *))) == NULL) { 198198406Sjilles error = WRDE_NOSPACE; 199198406Sjilles goto cleanup; 200108288Stjr } 201108288Stjr we->we_wordv = nwv; 202108288Stjr if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 203198406Sjilles error = WRDE_NOSPACE; 204198406Sjilles goto cleanup; 205108288Stjr } 206108288Stjr for (i = 0; i < vofs; i++) 207108288Stjr if (we->we_wordv[i] != NULL) 208108288Stjr we->we_wordv[i] += nstrings - we->we_strings; 209108288Stjr we->we_strings = nstrings; 210108288Stjr 211198406Sjilles if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 212198406Sjilles error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; 213198406Sjilles serrno = errno; 214198406Sjilles goto cleanup; 215108288Stjr } 216108288Stjr 217198406Sjilles error = 0; 218198406Sjillescleanup: 219198406Sjilles _close(pdes[0]); 220198406Sjilles do 221198406Sjilles wpid = _waitpid(pid, &status, 0); 222198406Sjilles while (wpid < 0 && errno == EINTR); 223198406Sjilles (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 224198406Sjilles if (error != 0) { 225198406Sjilles errno = serrno; 226198406Sjilles return (error); 227198406Sjilles } 228198406Sjilles if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 229108288Stjr return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); 230108288Stjr 231108288Stjr /* 232108288Stjr * Break the null-terminated expanded word strings out into 233108288Stjr * the vector. 234108288Stjr */ 235131331Stjr if (vofs == 0 && flags & WRDE_DOOFFS) 236108288Stjr while (vofs < we->we_offs) 237108288Stjr we->we_wordv[vofs++] = NULL; 238108288Stjr p = we->we_strings + sofs; 239108288Stjr while (nwords-- != 0) { 240108288Stjr we->we_wordv[vofs++] = p; 241108288Stjr if ((np = memchr(p, '\0', nbytes)) == NULL) 242108288Stjr return (WRDE_NOSPACE); /* XXX */ 243108288Stjr nbytes -= np - p + 1; 244108288Stjr p = np + 1; 245108288Stjr } 246108288Stjr we->we_wordv[vofs] = NULL; 247108288Stjr 248108288Stjr return (0); 249108288Stjr} 250108288Stjr 251108288Stjr/* 252108288Stjr * we_check -- 253108288Stjr * Check that the string contains none of the following unquoted 254108288Stjr * special characters: <newline> |&;<>(){} 255108288Stjr * or command substitutions when WRDE_NOCMD is set in flags. 256108288Stjr */ 257108299Stjrstatic int 258108288Stjrwe_check(const char *words, int flags) 259108288Stjr{ 260108288Stjr char c; 261108288Stjr int dquote, level, quote, squote; 262108288Stjr 263108288Stjr quote = squote = dquote = 0; 264108288Stjr while ((c = *words++) != '\0') { 265108288Stjr switch (c) { 266108288Stjr case '\\': 267108288Stjr quote ^= 1; 268108641Stjr continue; 269108288Stjr case '\'': 270108288Stjr if (quote + dquote == 0) 271108288Stjr squote ^= 1; 272108288Stjr break; 273108288Stjr case '"': 274108288Stjr if (quote + squote == 0) 275108288Stjr dquote ^= 1; 276108288Stjr break; 277108288Stjr case '`': 278108288Stjr if (quote + squote == 0 && flags & WRDE_NOCMD) 279108288Stjr return (WRDE_CMDSUB); 280108288Stjr while ((c = *words++) != '\0' && c != '`') 281108288Stjr if (c == '\\' && (c = *words++) == '\0') 282108288Stjr break; 283108288Stjr if (c == '\0') 284108288Stjr return (WRDE_SYNTAX); 285108288Stjr break; 286108288Stjr case '|': case '&': case ';': case '<': case '>': 287108288Stjr case '{': case '}': case '(': case ')': case '\n': 288108288Stjr if (quote + squote + dquote == 0) 289108288Stjr return (WRDE_BADCHAR); 290108288Stjr break; 291108288Stjr case '$': 292108288Stjr if ((c = *words++) == '\0') 293108288Stjr break; 294108641Stjr else if (quote + squote == 0 && c == '(') { 295108641Stjr if (flags & WRDE_NOCMD && *words != '(') 296108288Stjr return (WRDE_CMDSUB); 297108288Stjr level = 1; 298108288Stjr while ((c = *words++) != '\0') { 299108288Stjr if (c == '\\') { 300108288Stjr if ((c = *words++) == '\0') 301108288Stjr break; 302108288Stjr } else if (c == '(') 303108288Stjr level++; 304108288Stjr else if (c == ')' && --level == 0) 305108288Stjr break; 306108288Stjr } 307108288Stjr if (c == '\0' || level != 0) 308108288Stjr return (WRDE_SYNTAX); 309108641Stjr } else if (quote + squote == 0 && c == '{') { 310108288Stjr level = 1; 311108288Stjr while ((c = *words++) != '\0') { 312108288Stjr if (c == '\\') { 313108288Stjr if ((c = *words++) == '\0') 314108288Stjr break; 315108288Stjr } else if (c == '{') 316108288Stjr level++; 317108288Stjr else if (c == '}' && --level == 0) 318108288Stjr break; 319108288Stjr } 320108288Stjr if (c == '\0' || level != 0) 321108288Stjr return (WRDE_SYNTAX); 322108641Stjr } else 323199784Swollman --words; 324108288Stjr break; 325108288Stjr default: 326108288Stjr break; 327108288Stjr } 328108641Stjr quote = 0; 329108288Stjr } 330108288Stjr if (quote + squote + dquote != 0) 331108288Stjr return (WRDE_SYNTAX); 332108288Stjr 333108288Stjr return (0); 334108288Stjr} 335108288Stjr 336108288Stjr/* 337108288Stjr * wordfree -- 338108288Stjr * Free the result of wordexp(). See wordexp(3). 339108288Stjr * 340108288Stjr * Specified by IEEE Std. 1003.1-2001. 341108288Stjr */ 342108288Stjrvoid 343108288Stjrwordfree(wordexp_t *we) 344108288Stjr{ 345108288Stjr 346108288Stjr if (we == NULL) 347108288Stjr return; 348108288Stjr free(we->we_wordv); 349108288Stjr free(we->we_strings); 350108288Stjr we->we_wordv = NULL; 351108288Stjr we->we_strings = NULL; 352108288Stjr we->we_nbytes = 0; 353108288Stjr we->we_wordc = 0; 354108288Stjr} 355