diff options
author | jilles <jilles@FreeBSD.org> | 2009-10-23 14:50:11 +0000 |
---|---|---|
committer | jilles <jilles@FreeBSD.org> | 2009-10-23 14:50:11 +0000 |
commit | 2dcc53599c6a7af58c845d0d3796bcde7e21b437 (patch) | |
tree | 536a76c5ea65f8afdc1489d4092d72ddfa15073f /lib | |
parent | 8f8a90522cbb3bb03782bc7bf40d03dc744c7572 (diff) | |
download | FreeBSD-src-2dcc53599c6a7af58c845d0d3796bcde7e21b437.zip FreeBSD-src-2dcc53599c6a7af58c845d0d3796bcde7e21b437.tar.gz |
wordexp(3): fix some bugs with signals and long outputs
* retry various system calls on EINTR
* retry the rest after a short read (common if there is more than about 1K
of output)
* block SIGCHLD like system(3) does (note that this does not and cannot
work fully in threaded programs, they will need to be careful with wait
functions)
PR: 90580
MFC after: 1 month
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libc/gen/wordexp.c | 76 |
1 files changed, 57 insertions, 19 deletions
diff --git a/lib/libc/gen/wordexp.c b/lib/libc/gen/wordexp.c index a437543..06abdc7 100644 --- a/lib/libc/gen/wordexp.c +++ b/lib/libc/gen/wordexp.c @@ -28,8 +28,10 @@ #include <sys/cdefs.h> #include <sys/types.h> #include <sys/wait.h> +#include <errno.h> #include <fcntl.h> #include <paths.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -73,6 +75,24 @@ wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) return (0); } +static size_t +we_read_fully(int fd, char *buffer, size_t len) +{ + size_t done; + ssize_t nread; + + done = 0; + do { + nread = _read(fd, buffer + done, len - done); + if (nread == -1 && errno == EINTR) + continue; + if (nread <= 0) + break; + done += nread; + } while (done != len); + return done; +} + /* * we_askshell -- * Use the `wordexp' /bin/sh builtin function to do most of the work @@ -90,20 +110,31 @@ we_askshell(const char *words, wordexp_t *we, int flags) size_t sofs; /* Offset into we->we_strings */ size_t vofs; /* Offset into we->we_wordv */ pid_t pid; /* Process ID of child */ + pid_t wpid; /* waitpid return value */ int status; /* Child exit status */ + int error; /* Our return value */ + int serrno; /* errno to return */ char *ifs; /* IFS env. var. */ char *np, *p; /* Handy pointers */ char *nstrings; /* Temporary for realloc() */ char **nwv; /* Temporary for realloc() */ + sigset_t newsigblock, oldsigblock; + serrno = errno; if ((ifs = getenv("IFS")) == NULL) ifs = " \t\n"; if (pipe(pdes) < 0) return (WRDE_NOSPACE); /* XXX */ + (void)sigemptyset(&newsigblock); + (void)sigaddset(&newsigblock, SIGCHLD); + (void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); if ((pid = fork()) < 0) { + serrno = errno; _close(pdes[0]); _close(pdes[1]); + (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); + errno = serrno; return (WRDE_NOSPACE); /* XXX */ } else if (pid == 0) { @@ -114,6 +145,7 @@ we_askshell(const char *words, wordexp_t *we, int flags) int devnull; char *cmd; + (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); _close(pdes[0]); if (_dup2(pdes[1], STDOUT_FILENO) < 0) _exit(1); @@ -139,10 +171,11 @@ we_askshell(const char *words, wordexp_t *we, int flags) * the expanded words separated by nulls. */ _close(pdes[1]); - if (_read(pdes[0], wbuf, 8) != 8 || _read(pdes[0], bbuf, 8) != 8) { - _close(pdes[0]); - _waitpid(pid, &status, 0); - return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); + if (we_read_fully(pdes[0], wbuf, 8) != 8 || + we_read_fully(pdes[0], bbuf, 8) != 8) { + error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; + serrno = errno; + goto cleanup; } wbuf[8] = bbuf[8] = '\0'; nwords = strtol(wbuf, NULL, 16); @@ -162,33 +195,38 @@ we_askshell(const char *words, wordexp_t *we, int flags) if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + (flags & WRDE_DOOFFS ? we->we_offs : 0)) * sizeof(char *))) == NULL) { - _close(pdes[0]); - _waitpid(pid, &status, 0); - return (WRDE_NOSPACE); + error = WRDE_NOSPACE; + goto cleanup; } we->we_wordv = nwv; if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { - _close(pdes[0]); - _waitpid(pid, &status, 0); - return (WRDE_NOSPACE); + error = WRDE_NOSPACE; + goto cleanup; } for (i = 0; i < vofs; i++) if (we->we_wordv[i] != NULL) we->we_wordv[i] += nstrings - we->we_strings; we->we_strings = nstrings; - if (_read(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { - _close(pdes[0]); - _waitpid(pid, &status, 0); - return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); + if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { + error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; + serrno = errno; + goto cleanup; } - if (_waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) || - WEXITSTATUS(status) != 0) { - _close(pdes[0]); - return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); - } + error = 0; +cleanup: _close(pdes[0]); + do + wpid = _waitpid(pid, &status, 0); + while (wpid < 0 && errno == EINTR); + (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); + if (error != 0) { + errno = serrno; + return (error); + } + if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) + return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); /* * Break the null-terminated expanded word strings out into |