]> git.saurik.com Git - apple/libc.git/blobdiff - gen/FreeBSD/popen.c
Libc-1439.100.3.tar.gz
[apple/libc.git] / gen / FreeBSD / popen.c
index 0ed56578cc9385930842ae57ea921a12e87cd672..d4aea86409645ab5dd415de1a0a1eb8e954b80d5 100644 (file)
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  * SUCH DAMAGE.
  */
 
+#ifdef VARIANT_DARWINEXTSN
+#define _DARWIN_UNLIMITED_STREAMS
+#endif /* VARIANT_DARWINEXTSN */
+
 #if defined(LIBC_SCCS) && !defined(lint)
 static char sccsid[] = "@(#)popen.c    8.3 (Berkeley) 5/3/95";
 #endif /* LIBC_SCCS and not lint */
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libc/gen/popen.c,v 1.18 2003/01/04 00:15:15 tjr Exp $");
+__FBSDID("$FreeBSD: src/lib/libc/gen/popen.c,v 1.21 2009/05/27 19:28:04 ed Exp $");
 
 #include "namespace.h"
 #include <sys/param.h>
+#include <sys/queue.h>
 #include <sys/wait.h>
-
+#include <sys/socket.h>
+#include <wchar.h>             /* fwide() */
 #include <signal.h>
 #include <errno.h>
 #include <unistd.h>
@@ -52,17 +54,39 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/popen.c,v 1.18 2003/01/04 00:15:15 tjr Exp
 #include <string.h>
 #include <paths.h>
 #include <pthread.h>
+#include <spawn.h>
 #include "un-namespace.h"
 #include "libc_private.h"
 
-extern char **environ;
+#include <crt_externs.h>
+#define environ (*_NSGetEnviron())
+
+/* Our queue.h doesn't have SLIST_REMOVE_AFTER in it yet
+ * <rdar://problem/7431558> API: Add SLIST_REMOVE_AFTER to sys/queue.h (from FreeBSD)
+ */
+#ifndef SLIST_REMOVE_AFTER
+#define SLIST_REMOVE_AFTER(elm, field) do {                             \
+        SLIST_NEXT(elm, field) =                                        \
+            SLIST_NEXT(SLIST_NEXT(elm, field), field);                  \
+} while (0)
+#endif
 
-static struct pid {
-       struct pid *next;
+/* 3516149 - store file descriptor and use that to close to prevent blocking */
+struct pid {
+       SLIST_ENTRY(pid) next;
        FILE *fp;
+       int fd;
        pid_t pid;
-} *pidlist;
-static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
+};
+#define pidlist                __popen_pidlist
+#define pidlist_mutex  __popen_pidlist_mutex
+#ifndef BUILDING_VARIANT
+__private_extern__ SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
+__private_extern__ pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
+#else /* BUILDING_VARIANT */
+extern SLIST_HEAD(, pid) pidlist;
+extern pthread_mutex_t pidlist_mutex;
+#endif /* !BUILDING_VARIANT */
 
 #define        THREAD_LOCK()   if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex)
 #define        THREAD_UNLOCK() if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex)
@@ -73,85 +97,108 @@ popen(command, type)
 {
        struct pid *cur;
        FILE *iop;
-       int pdes[2], pid, twoway;
+       int pdes[2], pid, twoway, other;
        char *argv[4];
        struct pid *p;
+       posix_spawn_file_actions_t file_actions;
+       int err;
 
-       /*
-        * Lite2 introduced two-way popen() pipes using _socketpair().
-        * FreeBSD's pipe() is bidirectional, so we use that.
-        */
-       if (strchr(type, '+')) {
+       if (type == NULL) {
+               errno = EINVAL;
+               return (NULL);
+       }
+       if (strcmp(type, "r+") == 0) {
                twoway = 1;
                type = "r+";
+               if (socketpair(AF_UNIX, SOCK_STREAM, 0, pdes) < 0)
+                       return (NULL);
        } else  {
                twoway = 0;
-               if ((*type != 'r' && *type != 'w') || type[1])
+               if ((*type != 'r' && *type != 'w') || type[1]) {
+                       errno = EINVAL;
+                       return (NULL);
+               }
+               if (pipe(pdes) < 0)
                        return (NULL);
        }
-       if (pipe(pdes) < 0)
-               return (NULL);
 
-       if ((cur = malloc(sizeof(struct pid))) == NULL) {
+       /* fdopen can now fail */
+       if (*type == 'r') {
+               iop = fdopen(pdes[0], type);
+               other = pdes[1];
+       } else {
+               iop = fdopen(pdes[1], type);
+               other = pdes[0];
+       }
+       if (iop == NULL) {
                (void)_close(pdes[0]);
                (void)_close(pdes[1]);
                return (NULL);
        }
 
+       if ((cur = malloc(sizeof(struct pid))) == NULL) {
+               (void)fclose(iop);
+               (void)_close(other);
+               return (NULL);
+       }
+
+       if ((err = posix_spawn_file_actions_init(&file_actions)) != 0) {
+               (void)fclose(iop);
+               (void)_close(other);
+               free(cur);
+               errno = err;
+               return (NULL);
+       }
+       if (*type == 'r') {
+               /*
+                * The dup2() to STDIN_FILENO is repeated to avoid
+                * writing to pdes[1], which might corrupt the
+                * parent's copy.  This isn't good enough in
+                * general, since the _exit() is no return, so
+                * the compiler is free to corrupt all the local
+                * variables.
+                */
+               (void)posix_spawn_file_actions_addclose(&file_actions, pdes[0]);
+               if (pdes[1] != STDOUT_FILENO) {
+                       (void)posix_spawn_file_actions_adddup2(&file_actions, pdes[1], STDOUT_FILENO);
+                       (void)posix_spawn_file_actions_addclose(&file_actions, pdes[1]);
+                       if (twoway)
+                               (void)posix_spawn_file_actions_adddup2(&file_actions, STDOUT_FILENO, STDIN_FILENO);
+               } else if (twoway && (pdes[1] != STDIN_FILENO))
+                       (void)posix_spawn_file_actions_adddup2(&file_actions, pdes[1], STDIN_FILENO);
+       } else {
+               if (pdes[0] != STDIN_FILENO) {
+                       (void)posix_spawn_file_actions_adddup2(&file_actions, pdes[0], STDIN_FILENO);
+                       (void)posix_spawn_file_actions_addclose(&file_actions, pdes[0]);
+               }
+               (void)posix_spawn_file_actions_addclose(&file_actions, pdes[1]);
+       }
+       SLIST_FOREACH(p, &pidlist, next)
+               (void)posix_spawn_file_actions_addclose(&file_actions, p->fd);
+
        argv[0] = "sh";
        argv[1] = "-c";
        argv[2] = (char *)command;
        argv[3] = NULL;
 
-       THREAD_LOCK();
-       switch (pid = vfork()) {
-       case -1:                        /* Error. */
-               THREAD_UNLOCK();
-               (void)_close(pdes[0]);
-               (void)_close(pdes[1]);
+       err = posix_spawn(&pid, _PATH_BSHELL, &file_actions, NULL, argv, environ);
+       posix_spawn_file_actions_destroy(&file_actions);
+
+       if (err == ENOMEM || err == EAGAIN) { /* as if fork failed */
+               (void)fclose(iop);
+               (void)_close(other);
                free(cur);
+               errno = err;
                return (NULL);
-               /* NOTREACHED */
-       case 0:                         /* Child. */
-               if (*type == 'r') {
-                       /*
-                        * The _dup2() to STDIN_FILENO is repeated to avoid
-                        * writing to pdes[1], which might corrupt the
-                        * parent's copy.  This isn't good enough in
-                        * general, since the _exit() is no return, so
-                        * the compiler is free to corrupt all the local
-                        * variables.
-                        */
-                       (void)_close(pdes[0]);
-                       if (pdes[1] != STDOUT_FILENO) {
-                               (void)_dup2(pdes[1], STDOUT_FILENO);
-                               (void)_close(pdes[1]);
-                               if (twoway)
-                                       (void)_dup2(STDOUT_FILENO, STDIN_FILENO);
-                       } else if (twoway && (pdes[1] != STDIN_FILENO))
-                               (void)_dup2(pdes[1], STDIN_FILENO);
-               } else {
-                       if (pdes[0] != STDIN_FILENO) {
-                               (void)_dup2(pdes[0], STDIN_FILENO);
-                               (void)_close(pdes[0]);
-                       }
-                       (void)_close(pdes[1]);
-               }
-               for (p = pidlist; p; p = p->next) {
-                       (void)_close(fileno(p->fp));
-               }
-               _execve(_PATH_BSHELL, argv, environ);
-               _exit(127);
-               /* NOTREACHED */
+       } else if (err != 0) { /* couldn't exec the shell */
+               pid = -1;
        }
-       THREAD_UNLOCK();
 
-       /* Parent; assume fdopen can't fail. */
        if (*type == 'r') {
-               iop = fdopen(pdes[0], type);
+               cur->fd = pdes[0];
                (void)_close(pdes[1]);
        } else {
-               iop = fdopen(pdes[1], type);
+               cur->fd = pdes[1];
                (void)_close(pdes[0]);
        }
 
@@ -159,13 +206,13 @@ popen(command, type)
        cur->fp = iop;
        cur->pid = pid;
        THREAD_LOCK();
-       cur->next = pidlist;
-       pidlist = cur;
+       SLIST_INSERT_HEAD(&pidlist, cur, next);
        THREAD_UNLOCK();
-
+       fwide(iop, -1);         /* byte stream */
        return (iop);
 }
 
+#ifndef BUILDING_VARIANT
 /*
  * pclose --
  *     Pclose returns -1 if stream is not associated with a `popened' command,
@@ -175,7 +222,7 @@ int
 pclose(iop)
        FILE *iop;
 {
-       struct pid *cur, *last;
+       struct pid *cur, *last = NULL;
        int pstat;
        pid_t pid;
 
@@ -183,21 +230,27 @@ pclose(iop)
         * Find the appropriate file pointer and remove it from the list.
         */
        THREAD_LOCK();
-       for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
+       SLIST_FOREACH(cur, &pidlist, next) {
                if (cur->fp == iop)
                        break;
+               last = cur;
+       }
        if (cur == NULL) {
                THREAD_UNLOCK();
                return (-1);
        }
        if (last == NULL)
-               pidlist = cur->next;
+               SLIST_REMOVE_HEAD(&pidlist, next);
        else
-               last->next = cur->next;
+               SLIST_REMOVE_AFTER(last, next);
        THREAD_UNLOCK();
 
        (void)fclose(iop);
 
+       if (cur->pid < 0) {
+               free(cur);
+               return W_EXITCODE(127, 0);
+       }
        do {
                pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0);
        } while (pid == -1 && errno == EINTR);
@@ -206,3 +259,4 @@ pclose(iop)
 
        return (pid == -1 ? -1 : pstat);
 }
+#endif /* !BUILDING_VARIANT */