popen.c revision 291114
1193323Sed/* 2193323Sed * Copyright (c) 1988, 1993 3193323Sed * The Regents of the University of California. All rights reserved. 4193323Sed * 5193323Sed * This code is derived from software written by Ken Arnold and 6193323Sed * published in UNIX Review, Vol. 6, No. 8. 7193323Sed * 8193323Sed * Redistribution and use in source and binary forms, with or without 9193323Sed * modification, are permitted provided that the following conditions 10193323Sed * are met: 11193323Sed * 1. Redistributions of source code must retain the above copyright 12193323Sed * notice, this list of conditions and the following disclaimer. 13193323Sed * 2. Redistributions in binary form must reproduce the above copyright 14193323Sed * notice, this list of conditions and the following disclaimer in the 15193323Sed * documentation and/or other materials provided with the distribution. 16193323Sed * 4. Neither the name of the University nor the names of its contributors 17193323Sed * may be used to endorse or promote products derived from this software 18193323Sed * without specific prior written permission. 19193323Sed * 20249423Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21249423Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23198090Srdivacky * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30193323Sed * SUCH DAMAGE. 31193323Sed */ 32193323Sed 33201360Srdivacky#if defined(LIBC_SCCS) && !defined(lint) 34193323Sedstatic char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 5/3/95"; 35206083Srdivacky#endif /* LIBC_SCCS and not lint */ 36201360Srdivacky#include <sys/cdefs.h> 37205218Srdivacky__FBSDID("$FreeBSD: head/lib/libc/gen/popen.c 291114 2015-11-20 22:36:41Z rpokala $"); 38201360Srdivacky 39208599Srdivacky#include "namespace.h" 40249423Sdim#include <sys/param.h> 41193323Sed#include <sys/queue.h> 42193323Sed#include <sys/wait.h> 43193323Sed 44198090Srdivacky#include <signal.h> 45193323Sed#include <errno.h> 46193323Sed#include <fcntl.h> 47193323Sed#include <unistd.h> 48193323Sed#include <stdio.h> 49193323Sed#include <stdlib.h> 50193323Sed#include <string.h> 51193323Sed#include <paths.h> 52193323Sed#include <pthread.h> 53193323Sed#include "un-namespace.h" 54193323Sed#include "libc_private.h" 55193323Sed 56234353Sdimextern char **environ; 57193323Sed 58193323Sedstruct pid { 59193323Sed SLIST_ENTRY(pid) next; 60193323Sed FILE *fp; 61193323Sed pid_t pid; 62205218Srdivacky}; 63205218Srdivackystatic SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist); 64206083Srdivackystatic pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER; 65206083Srdivacky 66206083Srdivacky#define THREAD_LOCK() if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex) 67207618Srdivacky#define THREAD_UNLOCK() if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex) 68207618Srdivacky 69207618SrdivackyFILE * 70207618Srdivackypopen(const char *command, const char *type) 71207618Srdivacky{ 72207618Srdivacky struct pid *cur; 73205218Srdivacky FILE *iop; 74206083Srdivacky int pdes[2], pid, twoway, cloexec; 75207618Srdivacky int pdes_unused_in_parent; 76206083Srdivacky char *argv[4]; 77205218Srdivacky struct pid *p; 78243830Sdim 79243830Sdim cloexec = strchr(type, 'e') != NULL; 80205218Srdivacky /* 81205218Srdivacky * Lite2 introduced two-way popen() pipes using _socketpair(). 82205218Srdivacky * FreeBSD's pipe() is bidirectional, so we use that. 83207618Srdivacky */ 84207618Srdivacky if (strchr(type, '+')) { 85207618Srdivacky twoway = 1; 86207618Srdivacky type = "r+"; 87206083Srdivacky } else { 88206083Srdivacky twoway = 0; 89205218Srdivacky if ((*type != 'r' && *type != 'w') || 90206083Srdivacky (type[1] && (type[1] != 'e' || type[2]))) 91205218Srdivacky return (NULL); 92206083Srdivacky } 93206083Srdivacky if (pipe2(pdes, O_CLOEXEC) < 0) 94207618Srdivacky return (NULL); 95205218Srdivacky 96206083Srdivacky if ((cur = malloc(sizeof(struct pid))) == NULL) { 97206083Srdivacky (void)_close(pdes[0]); 98207618Srdivacky (void)_close(pdes[1]); 99205218Srdivacky return (NULL); 100206083Srdivacky } 101224145Sdim 102224145Sdim if (*type == 'r') { 103224145Sdim iop = fdopen(pdes[0], type); 104224145Sdim pdes_unused_in_parent = pdes[1]; 105224145Sdim } else { 106224145Sdim iop = fdopen(pdes[1], type); 107206083Srdivacky pdes_unused_in_parent = pdes[0]; 108206083Srdivacky } 109206083Srdivacky if (iop == NULL) { 110206083Srdivacky (void)_close(pdes[0]); 111206083Srdivacky (void)_close(pdes[1]); 112207618Srdivacky free(cur); 113207618Srdivacky return (NULL); 114205218Srdivacky } 115205218Srdivacky 116202878Srdivacky argv[0] = "sh"; 117202878Srdivacky argv[1] = "-c"; 118202878Srdivacky argv[2] = (char *)command; 119202878Srdivacky argv[3] = NULL; 120193323Sed 121193323Sed THREAD_LOCK(); 122193323Sed switch (pid = vfork()) { 123193323Sed case -1: /* Error. */ 124193323Sed THREAD_UNLOCK(); 125193323Sed /* 126193323Sed * The _close() closes the unused end of pdes[], while 127193323Sed * the fclose() closes the used end of pdes[], *and* cleans 128193323Sed * up iop. 129193323Sed */ 130193323Sed (void)_close(pdes_unused_in_parent); 131193323Sed free(cur); 132207618Srdivacky (void)fclose(iop); 133207618Srdivacky return (NULL); 134208599Srdivacky /* NOTREACHED */ 135249423Sdim case 0: /* Child. */ 136193323Sed if (*type == 'r') { 137206274Srdivacky /* 138234353Sdim * The _dup2() to STDIN_FILENO is repeated to avoid 139193323Sed * writing to pdes[1], which might corrupt the 140193323Sed * parent's copy. This isn't good enough in 141193323Sed * general, since the _exit() is no return, so 142193323Sed * the compiler is free to corrupt all the local 143193323Sed * variables. 144193323Sed */ 145193323Sed if (pdes[1] != STDOUT_FILENO) { 146193323Sed (void)_dup2(pdes[1], STDOUT_FILENO); 147193323Sed if (twoway) 148193323Sed (void)_dup2(STDOUT_FILENO, STDIN_FILENO); 149193323Sed } else if (twoway && (pdes[1] != STDIN_FILENO)) { 150193323Sed (void)_dup2(pdes[1], STDIN_FILENO); 151193323Sed (void)_fcntl(pdes[1], F_SETFD, 0); 152193323Sed } else 153193323Sed (void)_fcntl(pdes[1], F_SETFD, 0); 154193323Sed } else { 155193323Sed if (pdes[0] != STDIN_FILENO) { 156193323Sed (void)_dup2(pdes[0], STDIN_FILENO); 157193323Sed } else 158193323Sed (void)_fcntl(pdes[0], F_SETFD, 0); 159198090Srdivacky } 160193323Sed SLIST_FOREACH(p, &pidlist, next) 161193323Sed (void)_close(fileno(p->fp)); 162193323Sed _execve(_PATH_BSHELL, argv, environ); 163193323Sed _exit(127); 164193323Sed /* NOTREACHED */ 165193323Sed } 166193323Sed THREAD_UNLOCK(); 167193323Sed 168193323Sed /* Parent. */ 169201360Srdivacky (void)_close(pdes_unused_in_parent); 170201360Srdivacky 171201360Srdivacky /* Link into list of file descriptors. */ 172200581Srdivacky cur->fp = iop; 173205218Srdivacky cur->pid = pid; 174205218Srdivacky THREAD_LOCK(); 175205218Srdivacky SLIST_INSERT_HEAD(&pidlist, cur, next); 176239462Sdim THREAD_UNLOCK(); 177239462Sdim 178239462Sdim /* 179239462Sdim * To guard against undesired fd passing with concurrent calls, 180239462Sdim * only clear the close-on-exec flag after linking the file into 181239462Sdim * the list which will cause an explicit close. 182239462Sdim */ 183239462Sdim if (!cloexec) 184239462Sdim (void)_fcntl(*type == 'r' ? pdes[0] : pdes[1], F_SETFD, 0); 185239462Sdim 186239462Sdim return (iop); 187239462Sdim} 188239462Sdim 189239462Sdim/* 190239462Sdim * pclose -- 191239462Sdim * Pclose returns -1 if stream is not associated with a `popened' command, 192239462Sdim * if already `pclosed', or waitpid returns an error. 193239462Sdim */ 194239462Sdimint 195239462Sdimpclose(FILE *iop) 196239462Sdim{ 197239462Sdim struct pid *cur, *last = NULL; 198239462Sdim int pstat; 199239462Sdim pid_t pid; 200239462Sdim 201239462Sdim /* 202239462Sdim * Find the appropriate file pointer and remove it from the list. 203239462Sdim */ 204239462Sdim THREAD_LOCK(); 205239462Sdim SLIST_FOREACH(cur, &pidlist, next) { 206239462Sdim if (cur->fp == iop) 207239462Sdim break; 208239462Sdim last = cur; 209239462Sdim } 210239462Sdim if (cur == NULL) { 211239462Sdim THREAD_UNLOCK(); 212239462Sdim return (-1); 213239462Sdim } 214193323Sed if (last == NULL) 215193323Sed SLIST_REMOVE_HEAD(&pidlist, next); 216193323Sed else 217193323Sed SLIST_REMOVE_AFTER(last, next); 218193323Sed THREAD_UNLOCK(); 219193323Sed 220193323Sed (void)fclose(iop); 221243830Sdim 222243830Sdim do { 223200581Srdivacky pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0); 224193323Sed } while (pid == -1 && errno == EINTR); 225234353Sdim 226193323Sed free(cur); 227193323Sed 228193323Sed return (pid == -1 ? -1 : pstat); 229193323Sed} 230193323Sed