diff options
author | Roger A. Faulkner <Roger.Faulkner@Sun.COM> | 2008-11-10 18:56:48 -0800 |
---|---|---|
committer | Roger A. Faulkner <Roger.Faulkner@Sun.COM> | 2008-11-10 18:56:48 -0800 |
commit | 1c7ddb37901fc37ef4e9ee39da9ce4ecbbab096d (patch) | |
tree | 3d8b8af2f7470d24f8637827512ea24870181c1e /usr/src/lib/libc/port/regex | |
parent | f9153c6b74bc45d0b068ec8e4abab0c60d441deb (diff) | |
download | illumos-joyent-1c7ddb37901fc37ef4e9ee39da9ce4ecbbab096d.tar.gz |
6597729 RFE: Alternative ksh93 version of libc::wordexp() should use posix_spawn()
Contributed by Roland Mainz <roland.mainz@nrubsig.org>
Diffstat (limited to 'usr/src/lib/libc/port/regex')
-rw-r--r-- | usr/src/lib/libc/port/regex/wordexp.c | 277 |
1 files changed, 170 insertions, 107 deletions
diff --git a/usr/src/lib/libc/port/regex/wordexp.c b/usr/src/lib/libc/port/regex/wordexp.c index 9c834b2b54..cbf9295fdb 100644 --- a/usr/src/lib/libc/port/regex/wordexp.c +++ b/usr/src/lib/libc/port/regex/wordexp.c @@ -40,11 +40,8 @@ * * Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved. * Modified by Roland Mainz <roland.mainz@nrubsig.org> to support ksh93. - * */ -#pragma ident "%Z%%M% %I% %E% SMI" - #pragma weak _wordexp = wordexp #pragma weak _wordfree = wordfree @@ -75,6 +72,12 @@ #define INITIAL 8 /* initial pathv allocation */ #define BUFSZ 256 /* allocation unit of the line buffer */ +/* + * Needs no locking if fetched only once. + * See getenv()/putenv()/setenv(). + */ +extern const char **_environ; + /* Local prototypes */ static int append(wordexp_t *, char *); @@ -97,25 +100,37 @@ mystpcpy(char *s1, const char *s2) /* * Do word expansion. - * We built a mini-script in |buff| which takes care of all details, + * We build a mini-script in |buff| which takes care of all details, * including stdin/stdout/stderr redirection, WRDE_NOCMD mode and * the word expansion itself. */ int wordexp(const char *word, wordexp_t *wp, int flags) { - char *args[10]; + const char *path = "/usr/bin/ksh93"; wordexp_t wptmp; size_t si; - int i; pid_t pid; - char *line, *eob, *cp; /* word from shell */ + char *line, *eob, *cp; /* word from shell */ int rv = WRDE_ERRNO; int status; - int pv[2]; /* pipe from shell stdout */ - FILE *fp; /* pipe read stream */ - int serrno, tmpalloc; + int pv[2]; /* pipe from shell stdout */ + FILE *fp; /* pipe read stream */ + int tmpalloc; + char *wd = NULL; + const char **env = NULL; + const char **envp; + const char *ev; + int n; + posix_spawnattr_t attr; + posix_spawn_file_actions_t fact; + int error; int cancel_state; + size_t bufflen; /* Length of |buff| */ + char *buff; + char *currbuffp; /* Current position of '\0' in |buff| */ + char *args[10]; + int i; /* * Do absolute minimum necessary for the REUSE flag. Eventually @@ -137,7 +152,7 @@ wordexp(const char *word, wordexp_t *wp, int flags) /* * Man page says: * 2. All of the calls must set WRDE_DOOFFS, or all must not - * set it. + * set it. * Therefore, if it's not set, we_offs will always be reset. */ if ((flags & WRDE_DOOFFS) == 0) @@ -145,14 +160,13 @@ wordexp(const char *word, wordexp_t *wp, int flags) /* * If we get APPEND|REUSE, how should we do? - * We allocate the buffer anyway to avoid segfault. + * allocating buffer anyway to avoid segfault. */ tmpalloc = 0; if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) { wptmp.we_wordc = 0; wptmp.we_wordn = wptmp.we_offs + INITIAL; - wptmp.we_wordv = (char **)malloc( - sizeof (char *) * wptmp.we_wordn); + wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn); if (wptmp.we_wordv == NULL) return (WRDE_NOSPACE); wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs; @@ -163,98 +177,139 @@ wordexp(const char *word, wordexp_t *wp, int flags) /* * The UNIX98 Posix conformance test suite requires - * wordexp() to not be a cancellation point. + * |wordexp()| to not be a cancellation point. */ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); /* - * Set up pipe from shell stdout to "fp" for us + * Make sure PWD is in the environment. */ - if (pipe(pv) < 0) - goto cleanup; + if ((envp = _environ) == NULL) { + /* can happen when processing a SunOS 4.x AOUT file */ + ev = NULL; + n = 0; + } else { + for (n = 0; (ev = envp[n]) != NULL; n++) { + if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0) + break; + } + } + if (ev == NULL) { /* PWD missing from the environment */ + /* allocate a new environment */ + if ((env = malloc((n + 2) * sizeof (char *))) == NULL || + (wd = malloc(PATH_MAX + 4)) == NULL) + goto cleanup; + for (i = 0; i < n; i++) + env[i] = envp[i]; + (void) strcpy(wd, "PWD="); + if (getcwd(&wd[4], PATH_MAX) == NULL) + (void) strcpy(&wd[4], "/"); + env[i] = wd; + env[i + 1] = NULL; + envp = env; + } /* - * Fork/exec shell + * Calculate size of required buffer (which is size of the + * input string (|word|) plus all string literals below; + * this value MUST be adjusted each time the literals are + * changed!!). */ + bufflen = 165 + strlen(word); + buff = alloca(bufflen); + i = 0; - if ((pid = fork()) == -1) { - serrno = errno; - (void) close(pv[0]); - (void) close(pv[1]); - errno = serrno; - goto cleanup; - } + /* Start filling the buffer */ + buff[0] = '\0'; + currbuffp = buff; - if (pid == 0) { /* child */ + if (flags & WRDE_UNDEF) + currbuffp = mystpcpy(currbuffp, "set -o nounset\n"); + if ((flags & WRDE_SHOWERR) == 0) { /* - * Calculate size of required buffer (which is size of the - * input string (|word|) plus all string literals below; - * this value MUST be adjusted each time the literals are - * changed!!!!). + * The newline ('\n') is neccesary to make sure that + * the redirection to /dev/null is already active in + * the case the printf below contains a syntax + * error... */ - size_t bufflen = 124+strlen(word); /* Length of |buff| */ - char *buff = alloca(bufflen); - char *currbuffp; /* Current position of '\0' in |buff| */ - int i; - const char *path; - - (void) dup2(pv[1], 1); - (void) close(pv[0]); - (void) close(pv[1]); - - path = "/usr/bin/ksh93"; - i = 0; - - /* Start filling the buffer */ - buff[0] = '\0'; - currbuffp = buff; - - if (flags & WRDE_UNDEF) - currbuffp = mystpcpy(currbuffp, "set -o nounset ; "); - if ((flags & WRDE_SHOWERR) == 0) { - /* - * The newline ('\n') is neccesary to make sure that - * the redirection to /dev/null is already active in - * the case the printf below contains a syntax - * error... - */ - currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n"); - } - /* Squish stdin */ - currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n"); - - if (flags & WRDE_NOCMD) { - /* - * Switch to restricted shell (rksh) mode here to - * put the word expansion into a "cage" which - * prevents users from executing external commands - * (outside those listed by ${PATH} (which we set - * explicitly to /usr/no/such/path/element/)). - */ - currbuffp = mystpcpy(currbuffp, "set -o restricted\n"); + currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n"); + } + /* Squish stdin */ + currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n"); - (void) putenv("PATH=/usr/no/such/path/element/"); + if (flags & WRDE_NOCMD) { + /* + * Switch to restricted shell (rksh) mode here to + * put the word expansion into a "cage" which + * prevents users from executing external commands + * (outside those listed by ${PATH} (which we set + * explicitly to /usr/no/such/path/element/)). + */ + currbuffp = mystpcpy(currbuffp, + "export PATH=/usr/no/such/path/element/ ; " + "set -o restricted\n"); + } - } + (void) snprintf(currbuffp, bufflen, + "print -f '%%s\\000' -- %s", word); - (void) snprintf(currbuffp, bufflen, - "print -f \"%%s\\000\" %s", word); + args[i++] = strrchr(path, '/') + 1; + args[i++] = "-c"; + args[i++] = buff; + args[i++] = NULL; - args[i++] = strrchr(path, '/') + 1; - args[i++] = "-c"; - args[i++] = buff; - args[i++] = NULL; + if ((error = posix_spawnattr_init(&attr)) != 0) { + errno = error; + goto cleanup; + } + if ((error = posix_spawn_file_actions_init(&fact)) != 0) { + (void) posix_spawnattr_destroy(&attr); + errno = error; + goto cleanup; + } - (void) execv(path, args); - _exit(127); + /* + * Set up pipe from shell stdout to "fp" for us + */ + if (pipe(pv) < 0) { + error = errno; + (void) posix_spawnattr_destroy(&attr); + (void) posix_spawn_file_actions_destroy(&fact); + errno = error; + goto cleanup; } + /* + * Spawn shell + */ + error = posix_spawnattr_setflags(&attr, + POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP); + if (error == 0) + error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1); + if (error == 0 && pv[0] != 1) + error = posix_spawn_file_actions_addclose(&fact, pv[0]); + if (error == 0 && pv[1] != 1) + error = posix_spawn_file_actions_addclose(&fact, pv[1]); + if (error == 0 && !(flags & WRDE_SHOWERR)) + error = posix_spawn_file_actions_addopen(&fact, 2, + "/dev/null", O_WRONLY, 0); + + if (error == 0) + error = posix_spawn(&pid, path, &fact, &attr, + (char *const *)args, (char *const *)envp); + (void) posix_spawnattr_destroy(&attr); + (void) posix_spawn_file_actions_destroy(&fact); (void) close(pv[1]); + if (error) { + (void) close(pv[0]); + errno = error; + goto cleanup; + } if ((fp = fdopen(pv[0], "rF")) == NULL) { - serrno = errno; + error = errno; (void) close(pv[0]); - errno = serrno; + errno = error; goto wait_cleanup; } @@ -265,14 +320,16 @@ wordexp(const char *word, wordexp_t *wp, int flags) */ cp = line = malloc(BUFSZ); if (line == NULL) { + error = errno; (void) fclose(fp); - rv = WRDE_NOSPACE; + errno = error; goto wait_cleanup; } eob = line + BUFSZ; rv = 0; - while ((i = getc(fp)) != EOF) { + flockfile(fp); + while ((i = getc_unlocked(fp)) != EOF) { *cp++ = (char)i; if (i == '\0') { cp = line; @@ -293,6 +350,7 @@ wordexp(const char *word, wordexp_t *wp, int flags) eob = cp + BUFSZ; } } + funlockfile(fp); wptmp.we_wordp[wptmp.we_wordc] = NULL; @@ -300,28 +358,37 @@ wordexp(const char *word, wordexp_t *wp, int flags) (void) fclose(fp); /* kill shell if still writing */ wait_cleanup: - if (waitpid(pid, &status, 0) == -1) - rv = WRDE_ERRNO; - else if (rv == 0) + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) { + if (rv == 0) + rv = WRDE_ERRNO; + break; + } + } + if (rv == 0) rv = WEXITSTATUS(status); /* shell WRDE_* status */ cleanup: if (rv == 0) *wp = wptmp; - else { - if (tmpalloc) - wordfree(&wptmp); - } + else if (tmpalloc) + wordfree(&wptmp); + if (env) + free(env); + if (wd) + free(wd); /* - * Map ksh errors to wordexp() errors + * Map ksh93 errors to |wordexp()| errors */ - if (rv == 4) - rv = WRDE_CMDSUB; - else if (rv == 5) - rv = WRDE_BADVAL; - else if (rv == 6) - rv = WRDE_SYNTAX; + switch (rv) { + case 1: + rv = WRDE_BADVAL; + break; + case 127: + rv = WRDE_BADCHAR; + break; + } (void) pthread_setcancelstate(cancel_state, NULL); return (rv); @@ -332,12 +399,6 @@ cleanup: extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ /* - * Needs no locking if fetched only once. - * See getenv()/putenv()/setenv(). - */ -extern const char **_environ; - -/* * Do word expansion. * We just pass our arguments to shell with -E option. Note that the * underlying shell must recognize the -E option, and do the right thing @@ -374,7 +435,7 @@ wordexp(const char *word, wordexp_t *wp, int flags) static const char *xpg4_path = "/usr/xpg4/bin/sh"; /* - * Do absolute minimum neccessary for the REUSE flag. Eventually + * Do absolute minimum necessary for the REUSE flag. Eventually * want to be able to actually avoid excessive malloc calls. */ if (flags & WRDE_REUSE) @@ -535,7 +596,8 @@ wordexp(const char *word, wordexp_t *wp, int flags) eob = line + BUFSZ; rv = 0; - while ((i = getc(fp)) != EOF) { + flockfile(fp); + while ((i = getc_unlocked(fp)) != EOF) { *cp++ = (char)i; if (i == '\0') { cp = line; @@ -556,6 +618,7 @@ wordexp(const char *word, wordexp_t *wp, int flags) eob = cp + BUFSZ; } } + funlockfile(fp); wptmp.we_wordp[wptmp.we_wordc] = NULL; |