summaryrefslogtreecommitdiff
path: root/usr/src/lib/libc/port/regex
diff options
context:
space:
mode:
authorRoger A. Faulkner <Roger.Faulkner@Sun.COM>2008-11-10 18:56:48 -0800
committerRoger A. Faulkner <Roger.Faulkner@Sun.COM>2008-11-10 18:56:48 -0800
commit1c7ddb37901fc37ef4e9ee39da9ce4ecbbab096d (patch)
tree3d8b8af2f7470d24f8637827512ea24870181c1e /usr/src/lib/libc/port/regex
parentf9153c6b74bc45d0b068ec8e4abab0c60d441deb (diff)
downloadillumos-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.c277
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;