popen.c revision 256802
159243Sobrien/* 259243Sobrien * Copyright (c) 1988, 1993 359243Sobrien * The Regents of the University of California. All rights reserved. 459243Sobrien * 559243Sobrien * This code is derived from software written by Ken Arnold and 659243Sobrien * published in UNIX Review, Vol. 6, No. 8. 759243Sobrien * 859243Sobrien * Redistribution and use in source and binary forms, with or without 959243Sobrien * modification, are permitted provided that the following conditions 1059243Sobrien * are met: 1159243Sobrien * 1. Redistributions of source code must retain the above copyright 1259243Sobrien * notice, this list of conditions and the following disclaimer. 1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1459243Sobrien * notice, this list of conditions and the following disclaimer in the 1559243Sobrien * documentation and/or other materials provided with the distribution. 1659243Sobrien * 4. Neither the name of the University nor the names of its contributors 1759243Sobrien * may be used to endorse or promote products derived from this software 1859243Sobrien * without specific prior written permission. 1959243Sobrien * 2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2359243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3059243Sobrien * SUCH DAMAGE. 3159243Sobrien */ 3259243Sobrien 3359243Sobrien#if defined(LIBC_SCCS) && !defined(lint) 3459243Sobrienstatic char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 5/3/95"; 3559243Sobrien#endif /* LIBC_SCCS and not lint */ 3659243Sobrien#include <sys/cdefs.h> 3759243Sobrien__FBSDID("$FreeBSD: head/lib/libc/gen/popen.c 256802 2013-10-20 20:50:17Z jilles $"); 3859243Sobrien 3959243Sobrien#include "namespace.h" 4059243Sobrien#include <sys/param.h> 4159243Sobrien#include <sys/queue.h> 4259243Sobrien#include <sys/wait.h> 4359243Sobrien 4459243Sobrien#include <signal.h> 4559243Sobrien#include <errno.h> 4659243Sobrien#include <fcntl.h> 4759243Sobrien#include <unistd.h> 4859243Sobrien#include <stdio.h> 4959243Sobrien#include <stdlib.h> 5059243Sobrien#include <string.h> 5159243Sobrien#include <paths.h> 5259243Sobrien#include <pthread.h> 5359243Sobrien#include "un-namespace.h" 5459243Sobrien#include "libc_private.h" 5559243Sobrien 5659243Sobrienextern char **environ; 5759243Sobrien 5859243Sobrienstruct pid { 5959243Sobrien SLIST_ENTRY(pid) next; 6059243Sobrien FILE *fp; 6159243Sobrien pid_t pid; 6259243Sobrien}; 6359243Sobrienstatic SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist); 6459243Sobrienstatic pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER; 6559243Sobrien 6659243Sobrien#define THREAD_LOCK() if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex) 6759243Sobrien#define THREAD_UNLOCK() if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex) 6859243Sobrien 6959243SobrienFILE * 7059243Sobrienpopen(command, type) 7159243Sobrien const char *command, *type; 7259243Sobrien{ 7359243Sobrien struct pid *cur; 7459243Sobrien FILE *iop; 7559243Sobrien int pdes[2], pid, twoway, cloexec; 7659243Sobrien char *argv[4]; 7759243Sobrien struct pid *p; 7859243Sobrien 7959243Sobrien cloexec = strchr(type, 'e') != NULL; 8059243Sobrien /* 8159243Sobrien * Lite2 introduced two-way popen() pipes using _socketpair(). 8259243Sobrien * FreeBSD's pipe() is bidirectional, so we use that. 8359243Sobrien */ 8459243Sobrien if (strchr(type, '+')) { 8559243Sobrien twoway = 1; 8659243Sobrien type = "r+"; 8759243Sobrien } else { 8859243Sobrien twoway = 0; 8959243Sobrien if ((*type != 'r' && *type != 'w') || 9059243Sobrien (type[1] && (type[1] != 'e' || type[2]))) 9159243Sobrien return (NULL); 9259243Sobrien } 9359243Sobrien if (pipe2(pdes, O_CLOEXEC) < 0) 9459243Sobrien return (NULL); 9559243Sobrien 9659243Sobrien if ((cur = malloc(sizeof(struct pid))) == NULL) { 9759243Sobrien (void)_close(pdes[0]); 9859243Sobrien (void)_close(pdes[1]); 9959243Sobrien return (NULL); 10059243Sobrien } 10159243Sobrien 10259243Sobrien argv[0] = "sh"; 10359243Sobrien argv[1] = "-c"; 10459243Sobrien argv[2] = (char *)command; 10559243Sobrien argv[3] = NULL; 10659243Sobrien 10759243Sobrien THREAD_LOCK(); 10859243Sobrien switch (pid = vfork()) { 10959243Sobrien case -1: /* Error. */ 11059243Sobrien THREAD_UNLOCK(); 11159243Sobrien (void)_close(pdes[0]); 11259243Sobrien (void)_close(pdes[1]); 11359243Sobrien free(cur); 11459243Sobrien return (NULL); 11559243Sobrien /* NOTREACHED */ 11659243Sobrien case 0: /* Child. */ 11759243Sobrien if (*type == 'r') { 11859243Sobrien /* 11959243Sobrien * The _dup2() to STDIN_FILENO is repeated to avoid 12059243Sobrien * writing to pdes[1], which might corrupt the 12159243Sobrien * parent's copy. This isn't good enough in 12259243Sobrien * general, since the _exit() is no return, so 12359243Sobrien * the compiler is free to corrupt all the local 12459243Sobrien * variables. 12559243Sobrien */ 12659243Sobrien if (pdes[1] != STDOUT_FILENO) { 12759243Sobrien (void)_dup2(pdes[1], STDOUT_FILENO); 12859243Sobrien if (twoway) 12959243Sobrien (void)_dup2(STDOUT_FILENO, STDIN_FILENO); 13059243Sobrien } else if (twoway && (pdes[1] != STDIN_FILENO)) { 13159243Sobrien (void)_dup2(pdes[1], STDIN_FILENO); 13259243Sobrien (void)_fcntl(pdes[1], F_SETFD, 0); 13359243Sobrien } else 13459243Sobrien (void)_fcntl(pdes[1], F_SETFD, 0); 13559243Sobrien } else { 13659243Sobrien if (pdes[0] != STDIN_FILENO) { 13759243Sobrien (void)_dup2(pdes[0], STDIN_FILENO); 13859243Sobrien } else 13959243Sobrien (void)_fcntl(pdes[0], F_SETFD, 0); 14059243Sobrien } 14159243Sobrien SLIST_FOREACH(p, &pidlist, next) 14259243Sobrien (void)_close(fileno(p->fp)); 14359243Sobrien _execve(_PATH_BSHELL, argv, environ); 14459243Sobrien _exit(127); 14559243Sobrien /* NOTREACHED */ 14659243Sobrien } 14759243Sobrien THREAD_UNLOCK(); 14859243Sobrien 14959243Sobrien /* Parent; assume fdopen can't fail. */ 15059243Sobrien if (*type == 'r') { 15159243Sobrien iop = fdopen(pdes[0], type); 15259243Sobrien (void)_close(pdes[1]); 15359243Sobrien } else { 15459243Sobrien iop = fdopen(pdes[1], type); 15559243Sobrien (void)_close(pdes[0]); 15659243Sobrien } 15759243Sobrien 15859243Sobrien /* Link into list of file descriptors. */ 15959243Sobrien cur->fp = iop; 16059243Sobrien cur->pid = pid; 16159243Sobrien THREAD_LOCK(); 16259243Sobrien SLIST_INSERT_HEAD(&pidlist, cur, next); 16359243Sobrien THREAD_UNLOCK(); 16459243Sobrien 16559243Sobrien /* 16659243Sobrien * To guard against undesired fd passing with concurrent calls, 16759243Sobrien * only clear the close-on-exec flag after linking the file into 16859243Sobrien * the list which will cause an explicit close. 16959243Sobrien */ 170 if (!cloexec) 171 (void)_fcntl(*type == 'r' ? pdes[0] : pdes[1], F_SETFD, 0); 172 173 return (iop); 174} 175 176/* 177 * pclose -- 178 * Pclose returns -1 if stream is not associated with a `popened' command, 179 * if already `pclosed', or waitpid returns an error. 180 */ 181int 182pclose(iop) 183 FILE *iop; 184{ 185 struct pid *cur, *last = NULL; 186 int pstat; 187 pid_t pid; 188 189 /* 190 * Find the appropriate file pointer and remove it from the list. 191 */ 192 THREAD_LOCK(); 193 SLIST_FOREACH(cur, &pidlist, next) { 194 if (cur->fp == iop) 195 break; 196 last = cur; 197 } 198 if (cur == NULL) { 199 THREAD_UNLOCK(); 200 return (-1); 201 } 202 if (last == NULL) 203 SLIST_REMOVE_HEAD(&pidlist, next); 204 else 205 SLIST_REMOVE_AFTER(last, next); 206 THREAD_UNLOCK(); 207 208 (void)fclose(iop); 209 210 do { 211 pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0); 212 } while (pid == -1 && errno == EINTR); 213 214 free(cur); 215 216 return (pid == -1 ? -1 : pstat); 217} 218