popen.c revision 40219
1218887Sdim/* 2218887Sdim * Copyright (c) 1988, 1993 3218887Sdim * The Regents of the University of California. All rights reserved. 4218887Sdim * 5218887Sdim * This code is derived from software written by Ken Arnold and 6218887Sdim * published in UNIX Review, Vol. 6, No. 8. 7218887Sdim * 8218887Sdim * Redistribution and use in source and binary forms, with or without 9218887Sdim * modification, are permitted provided that the following conditions 10218887Sdim * are met: 11218887Sdim * 1. Redistributions of source code must retain the above copyright 12218887Sdim * notice, this list of conditions and the following disclaimer. 13218887Sdim * 2. Redistributions in binary form must reproduce the above copyright 14218887Sdim * notice, this list of conditions and the following disclaimer in the 15218887Sdim * documentation and/or other materials provided with the distribution. 16239462Sdim * 3. All advertising materials mentioning features or use of this software 17218887Sdim * must display the following acknowledgement: 18221345Sdim * This product includes software developed by the University of 19218887Sdim * California, Berkeley and its contributors. 20218887Sdim * 4. Neither the name of the University nor the names of its contributors 21218887Sdim * may be used to endorse or promote products derived from this software 22218887Sdim * without specific prior written permission. 23218887Sdim * 24218887Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25218887Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26218887Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27218887Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28218887Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29218887Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30221345Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31226633Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32221345Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33218887Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34218887Sdim * SUCH DAMAGE. 35218887Sdim */ 36218887Sdim 37218887Sdim#if defined(LIBC_SCCS) && !defined(lint) 38218887Sdimstatic char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 5/3/95"; 39218887Sdim#endif /* LIBC_SCCS and not lint */ 40218887Sdim 41218887Sdim#include <sys/param.h> 42218887Sdim#include <sys/wait.h> 43218887Sdim 44218887Sdim#include <signal.h> 45218887Sdim#include <errno.h> 46218887Sdim#include <unistd.h> 47218887Sdim#include <stdio.h> 48218887Sdim#include <stdlib.h> 49218887Sdim#include <string.h> 50218887Sdim#include <paths.h> 51218887Sdim 52218887Sdimextern char **environ; 53218887Sdim 54218887Sdimstatic struct pid { 55218887Sdim struct pid *next; 56218887Sdim FILE *fp; 57218887Sdim pid_t pid; 58218887Sdim} *pidlist; 59218887Sdim 60218887SdimFILE * 61218887Sdimpopen(command, type) 62218887Sdim const char *command, *type; 63218887Sdim{ 64218887Sdim struct pid *cur; 65218887Sdim FILE *iop; 66218887Sdim int pdes[2], pid, twoway; 67218887Sdim char *argv[4]; 68218887Sdim 69218887Sdim /* 70218887Sdim * Lite2 introduced two-way popen() pipes using socketpair(). 71218887Sdim * FreeBSD's pipe() is bidirectional, so we use that. 72218887Sdim */ 73218887Sdim if (strchr(type, '+')) { 74226633Sdim twoway = 1; 75218887Sdim type = "r+"; 76218887Sdim } else { 77218887Sdim twoway = 0; 78218887Sdim if ((*type != 'r' && *type != 'w') || type[1]) 79218887Sdim return (NULL); 80218887Sdim } 81218887Sdim if (pipe(pdes) < 0) 82218887Sdim return (NULL); 83218887Sdim 84218887Sdim if ((cur = malloc(sizeof(struct pid))) == NULL) { 85218887Sdim (void)close(pdes[0]); 86218887Sdim (void)close(pdes[1]); 87218887Sdim return (NULL); 88218887Sdim } 89218887Sdim 90218887Sdim argv[0] = "sh"; 91218887Sdim argv[1] = "-c"; 92218887Sdim argv[2] = (char *)command; 93218887Sdim argv[3] = NULL; 94218887Sdim 95218887Sdim switch (pid = vfork()) { 96218887Sdim case -1: /* Error. */ 97218887Sdim (void)close(pdes[0]); 98218887Sdim (void)close(pdes[1]); 99218887Sdim free(cur); 100218887Sdim return (NULL); 101218887Sdim /* NOTREACHED */ 102218887Sdim case 0: /* Child. */ 103218887Sdim if (*type == 'r') { 104243830Sdim /* 105218887Sdim * The dup2() to STDIN_FILENO is repeated to avoid 106218887Sdim * writing to pdes[1], which might corrupt the 107218887Sdim * parent's copy. This isn't good enough in 108218887Sdim * general, since the _exit() is no return, so 109218887Sdim * the compiler is free to corrupt all the local 110218887Sdim * variables. 111218887Sdim */ 112218887Sdim (void) close(pdes[0]); 113226633Sdim if (pdes[1] != STDOUT_FILENO) { 114218887Sdim (void)dup2(pdes[1], STDOUT_FILENO); 115218887Sdim (void)close(pdes[1]); 116218887Sdim if (twoway) 117218887Sdim (void)dup2(STDOUT_FILENO, STDIN_FILENO); 118218887Sdim } else if (twoway && (pdes[1] != STDIN_FILENO)) 119218887Sdim (void)dup2(pdes[1], STDIN_FILENO); 120218887Sdim } else { 121218887Sdim if (pdes[0] != STDIN_FILENO) { 122218887Sdim (void)dup2(pdes[0], STDIN_FILENO); 123218887Sdim (void)close(pdes[0]); 124218887Sdim } 125218887Sdim (void)close(pdes[1]); 126218887Sdim } 127221345Sdim execve(_PATH_BSHELL, argv, environ); 128226633Sdim _exit(127); 129218887Sdim /* NOTREACHED */ 130218887Sdim } 131226633Sdim 132218887Sdim /* Parent; assume fdopen can't fail. */ 133218887Sdim if (*type == 'r') { 134218887Sdim iop = fdopen(pdes[0], type); 135218887Sdim (void)close(pdes[1]); 136218887Sdim } else { 137218887Sdim iop = fdopen(pdes[1], type); 138218887Sdim (void)close(pdes[0]); 139218887Sdim } 140218887Sdim 141218887Sdim /* Link into list of file descriptors. */ 142218887Sdim cur->fp = iop; 143218887Sdim cur->pid = pid; 144218887Sdim cur->next = pidlist; 145218887Sdim pidlist = cur; 146218887Sdim 147218887Sdim return (iop); 148218887Sdim} 149218887Sdim 150218887Sdim/* 151218887Sdim * pclose -- 152218887Sdim * Pclose returns -1 if stream is not associated with a `popened' command, 153218887Sdim * if already `pclosed', or waitpid returns an error. 154218887Sdim */ 155218887Sdimint 156218887Sdimpclose(iop) 157218887Sdim FILE *iop; 158218887Sdim{ 159218887Sdim register struct pid *cur, *last; 160218887Sdim int omask; 161218887Sdim int pstat; 162218887Sdim pid_t pid; 163218887Sdim 164218887Sdim /* Find the appropriate file pointer. */ 165218887Sdim for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next) 166218887Sdim if (cur->fp == iop) 167218887Sdim break; 168218887Sdim if (cur == NULL) 169218887Sdim return (-1); 170218887Sdim 171218887Sdim (void)fclose(iop); 172218887Sdim 173218887Sdim do { 174218887Sdim pid = waitpid(cur->pid, &pstat, 0); 175218887Sdim } while (pid == -1 && errno == EINTR); 176218887Sdim 177218887Sdim /* Remove the entry from the linked list. */ 178218887Sdim if (last == NULL) 179218887Sdim pidlist = cur->next; 180218887Sdim else 181218887Sdim last->next = cur->next; 182218887Sdim free(cur); 183218887Sdim 184218887Sdim return (pid == -1 ? -1 : pstat); 185218887Sdim} 186218887Sdim