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 *np, *p; /* Handy pointers */ 118108288Stjr char *nstrings; /* Temporary for realloc() */ 119108288Stjr char **nwv; /* Temporary for realloc() */ 120198406Sjilles sigset_t newsigblock, oldsigblock; 121108288Stjr 122198406Sjilles serrno = errno; 123108288Stjr 124254977Sjilles if (pipe2(pdes, O_CLOEXEC) < 0) 125108288Stjr return (WRDE_NOSPACE); /* XXX */ 126198406Sjilles (void)sigemptyset(&newsigblock); 127198406Sjilles (void)sigaddset(&newsigblock, SIGCHLD); 128198406Sjilles (void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 129108288Stjr if ((pid = fork()) < 0) { 130198406Sjilles serrno = errno; 131108865Stjr _close(pdes[0]); 132108865Stjr _close(pdes[1]); 133198406Sjilles (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 134198406Sjilles errno = serrno; 135108288Stjr return (WRDE_NOSPACE); /* XXX */ 136108288Stjr } 137108288Stjr else if (pid == 0) { 138108288Stjr /* 139108288Stjr * We are the child; just get /bin/sh to run the wordexp 140108288Stjr * builtin on `words'. 141108288Stjr */ 142198406Sjilles (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 143254977Sjilles if ((pdes[1] != STDOUT_FILENO ? 144254977Sjilles _dup2(pdes[1], STDOUT_FILENO) : 145254977Sjilles _fcntl(pdes[1], F_SETFD, 0)) < 0) 146108288Stjr _exit(1); 147108288Stjr execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 148250406Sjilles "-c", "eval \"$1\";eval \"wordexp $2\"", "", 149250406Sjilles flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", words, 150250406Sjilles (char *)NULL); 151108288Stjr _exit(1); 152108288Stjr } 153108288Stjr 154108288Stjr /* 155108288Stjr * We are the parent; read the output of the shell wordexp function, 156108288Stjr * which is a 32-bit hexadecimal word count, a 32-bit hexadecimal 157108288Stjr * byte count (not including terminating null bytes), followed by 158108288Stjr * the expanded words separated by nulls. 159108288Stjr */ 160108865Stjr _close(pdes[1]); 161198406Sjilles if (we_read_fully(pdes[0], wbuf, 8) != 8 || 162198406Sjilles we_read_fully(pdes[0], bbuf, 8) != 8) { 163198406Sjilles error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; 164198406Sjilles serrno = errno; 165198406Sjilles goto cleanup; 166108288Stjr } 167108288Stjr wbuf[8] = bbuf[8] = '\0'; 168108288Stjr nwords = strtol(wbuf, NULL, 16); 169108288Stjr nbytes = strtol(bbuf, NULL, 16) + nwords; 170108288Stjr 171108288Stjr /* 172108288Stjr * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 173108288Stjr * and string storage buffers for the expanded words we're about to 174108288Stjr * read from the child. 175108288Stjr */ 176108288Stjr sofs = we->we_nbytes; 177108288Stjr vofs = we->we_wordc; 178131331Stjr if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) 179108641Stjr vofs += we->we_offs; 180108288Stjr we->we_wordc += nwords; 181108288Stjr we->we_nbytes += nbytes; 182108288Stjr if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + 183131331Stjr (flags & WRDE_DOOFFS ? we->we_offs : 0)) * 184108288Stjr sizeof(char *))) == NULL) { 185198406Sjilles error = WRDE_NOSPACE; 186198406Sjilles goto cleanup; 187108288Stjr } 188108288Stjr we->we_wordv = nwv; 189108288Stjr if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 190198406Sjilles error = WRDE_NOSPACE; 191198406Sjilles goto cleanup; 192108288Stjr } 193108288Stjr for (i = 0; i < vofs; i++) 194108288Stjr if (we->we_wordv[i] != NULL) 195108288Stjr we->we_wordv[i] += nstrings - we->we_strings; 196108288Stjr we->we_strings = nstrings; 197108288Stjr 198198406Sjilles if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 199198406Sjilles error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; 200198406Sjilles serrno = errno; 201198406Sjilles goto cleanup; 202108288Stjr } 203108288Stjr 204198406Sjilles error = 0; 205198406Sjillescleanup: 206198406Sjilles _close(pdes[0]); 207198406Sjilles do 208198406Sjilles wpid = _waitpid(pid, &status, 0); 209198406Sjilles while (wpid < 0 && errno == EINTR); 210198406Sjilles (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 211198406Sjilles if (error != 0) { 212198406Sjilles errno = serrno; 213198406Sjilles return (error); 214198406Sjilles } 215198406Sjilles if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 216108288Stjr return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); 217108288Stjr 218108288Stjr /* 219108288Stjr * Break the null-terminated expanded word strings out into 220108288Stjr * the vector. 221108288Stjr */ 222131331Stjr if (vofs == 0 && flags & WRDE_DOOFFS) 223108288Stjr while (vofs < we->we_offs) 224108288Stjr we->we_wordv[vofs++] = NULL; 225108288Stjr p = we->we_strings + sofs; 226108288Stjr while (nwords-- != 0) { 227108288Stjr we->we_wordv[vofs++] = p; 228108288Stjr if ((np = memchr(p, '\0', nbytes)) == NULL) 229108288Stjr return (WRDE_NOSPACE); /* XXX */ 230108288Stjr nbytes -= np - p + 1; 231108288Stjr p = np + 1; 232108288Stjr } 233108288Stjr we->we_wordv[vofs] = NULL; 234108288Stjr 235108288Stjr return (0); 236108288Stjr} 237108288Stjr 238108288Stjr/* 239108288Stjr * we_check -- 240108288Stjr * Check that the string contains none of the following unquoted 241108288Stjr * special characters: <newline> |&;<>(){} 242108288Stjr * or command substitutions when WRDE_NOCMD is set in flags. 243108288Stjr */ 244108299Stjrstatic int 245108288Stjrwe_check(const char *words, int flags) 246108288Stjr{ 247108288Stjr char c; 248108288Stjr int dquote, level, quote, squote; 249108288Stjr 250108288Stjr quote = squote = dquote = 0; 251108288Stjr while ((c = *words++) != '\0') { 252108288Stjr switch (c) { 253108288Stjr case '\\': 254253581Sjilles if (squote == 0) 255253581Sjilles quote ^= 1; 256108641Stjr continue; 257108288Stjr case '\'': 258108288Stjr if (quote + dquote == 0) 259108288Stjr squote ^= 1; 260108288Stjr break; 261108288Stjr case '"': 262108288Stjr if (quote + squote == 0) 263108288Stjr dquote ^= 1; 264108288Stjr break; 265108288Stjr case '`': 266108288Stjr if (quote + squote == 0 && flags & WRDE_NOCMD) 267108288Stjr return (WRDE_CMDSUB); 268108288Stjr while ((c = *words++) != '\0' && c != '`') 269108288Stjr if (c == '\\' && (c = *words++) == '\0') 270108288Stjr break; 271108288Stjr if (c == '\0') 272108288Stjr return (WRDE_SYNTAX); 273108288Stjr break; 274108288Stjr case '|': case '&': case ';': case '<': case '>': 275108288Stjr case '{': case '}': case '(': case ')': case '\n': 276108288Stjr if (quote + squote + dquote == 0) 277108288Stjr return (WRDE_BADCHAR); 278108288Stjr break; 279108288Stjr case '$': 280108288Stjr if ((c = *words++) == '\0') 281108288Stjr break; 282108641Stjr else if (quote + squote == 0 && c == '(') { 283108641Stjr if (flags & WRDE_NOCMD && *words != '(') 284108288Stjr return (WRDE_CMDSUB); 285108288Stjr level = 1; 286108288Stjr while ((c = *words++) != '\0') { 287108288Stjr if (c == '\\') { 288108288Stjr if ((c = *words++) == '\0') 289108288Stjr break; 290108288Stjr } else if (c == '(') 291108288Stjr level++; 292108288Stjr else if (c == ')' && --level == 0) 293108288Stjr break; 294108288Stjr } 295108288Stjr if (c == '\0' || level != 0) 296108288Stjr return (WRDE_SYNTAX); 297108641Stjr } else if (quote + squote == 0 && c == '{') { 298108288Stjr level = 1; 299108288Stjr while ((c = *words++) != '\0') { 300108288Stjr if (c == '\\') { 301108288Stjr if ((c = *words++) == '\0') 302108288Stjr break; 303108288Stjr } else if (c == '{') 304108288Stjr level++; 305108288Stjr else if (c == '}' && --level == 0) 306108288Stjr break; 307108288Stjr } 308108288Stjr if (c == '\0' || level != 0) 309108288Stjr return (WRDE_SYNTAX); 310108641Stjr } else 311199784Swollman --words; 312108288Stjr break; 313108288Stjr default: 314108288Stjr break; 315108288Stjr } 316108641Stjr quote = 0; 317108288Stjr } 318108288Stjr if (quote + squote + dquote != 0) 319108288Stjr return (WRDE_SYNTAX); 320108288Stjr 321108288Stjr return (0); 322108288Stjr} 323108288Stjr 324108288Stjr/* 325108288Stjr * wordfree -- 326108288Stjr * Free the result of wordexp(). See wordexp(3). 327108288Stjr * 328108288Stjr * Specified by IEEE Std. 1003.1-2001. 329108288Stjr */ 330108288Stjrvoid 331108288Stjrwordfree(wordexp_t *we) 332108288Stjr{ 333108288Stjr 334108288Stjr if (we == NULL) 335108288Stjr return; 336108288Stjr free(we->we_wordv); 337108288Stjr free(we->we_strings); 338108288Stjr we->we_wordv = NULL; 339108288Stjr we->we_strings = NULL; 340108288Stjr we->we_nbytes = 0; 341108288Stjr we->we_wordc = 0; 342108288Stjr} 343