Author: jilles
Date: Fri Oct 23 14:50:11 2009
New Revision: 198406
URL: http://svn.freebsd.org/changeset/base/198406

Log:
  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

Modified:
  head/lib/libc/gen/wordexp.c
  head/tools/regression/lib/libc/gen/test-wordexp.c

Modified: head/lib/libc/gen/wordexp.c
==============================================================================
--- head/lib/libc/gen/wordexp.c Fri Oct 23 14:43:17 2009        (r198405)
+++ head/lib/libc/gen/wordexp.c Fri Oct 23 14:50:11 2009        (r198406)
@@ -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, w
        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
        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
                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
         * 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
        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

Modified: head/tools/regression/lib/libc/gen/test-wordexp.c
==============================================================================
--- head/tools/regression/lib/libc/gen/test-wordexp.c   Fri Oct 23 14:43:17 
2009        (r198405)
+++ head/tools/regression/lib/libc/gen/test-wordexp.c   Fri Oct 23 14:50:11 
2009        (r198406)
@@ -32,17 +32,36 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include <sys/wait.h>
+
 #include <assert.h>
+#include <errno.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <wordexp.h>
 
+static void
+chld_handler(int x)
+{
+       int status, serrno;
+
+       (void)x;
+       serrno = errno;
+       while (waitpid(-1, &status, WNOHANG) > 0)
+               ;
+       errno = serrno;
+}
+
 int
 main(int argc, char *argv[])
 {
+       struct sigaction sa;
        wordexp_t we;
        int r;
+       int i;
+       char longdata[6 * 10000 + 1];
 
        /* Test that the macros are there. */
        (void)(WRDE_APPEND + WRDE_DOOFFS + WRDE_NOCMD + WRDE_REUSE +
@@ -59,6 +78,15 @@ main(int argc, char *argv[])
        assert(we.we_wordv[2] == NULL);
        wordfree(&we);
 
+       /* Long output. */
+       for (i = 0; i < 10000; i++)
+               snprintf(longdata + 6 * i, 7, "%05d ", i);
+       r = wordexp(longdata, &we, 0);
+       assert(r == 0);
+       assert(we.we_wordc == 10000);
+       assert(we.we_wordv[10000] == NULL);
+       wordfree(&we);
+
        /* WRDE_DOOFFS */
        we.we_offs = 3;
        r = wordexp("hello world", &we, WRDE_DOOFFS);
@@ -167,6 +195,20 @@ main(int argc, char *argv[])
        r = wordexp("test } test", &we, 0);
        assert(r == WRDE_BADCHAR);
 
+       /* With a SIGCHLD handler that reaps all zombies. */
+       sa.sa_flags = 0;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_handler = chld_handler;
+       r = sigaction(SIGCHLD, &sa, NULL);
+       assert(r == 0);
+       r = wordexp("hello world", &we, 0);
+       assert(r == 0);
+       assert(we.we_wordc == 2);
+       assert(strcmp(we.we_wordv[0], "hello") == 0);
+       assert(strcmp(we.we_wordv[1], "world") == 0);
+       assert(we.we_wordv[2] == NULL);
+       wordfree(&we);
+
        printf("PASS wordexp()\n");
        printf("PASS wordfree()\n");
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to