1 /* $OpenBSD: popen.c,v 1.17 2005/08/08 08:05:34 espie Exp $ */ 2 /* 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * This code is derived from software written by Ken Arnold and 7 * published in UNIX Review, Vol. 6, No. 8. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/wait.h> 36 37 #include <signal.h> 38 #include <errno.h> 39 #include <unistd.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <paths.h> 44 45 static struct pid { 46 struct pid *next; 47 FILE *fp; 48 pid_t pid; 49 } *pidlist; 50 51 extern char **environ; 52 53 FILE * 54 popen(const char *program, const char *type) 55 { 56 struct pid * volatile cur; 57 FILE *iop; 58 int pdes[2]; 59 pid_t pid; 60 char *argp[] = {"sh", "-c", NULL, NULL}; 61 62 if ((*type != 'r' && *type != 'w') || type[1] != '\0') { 63 errno = EINVAL; 64 return (NULL); 65 } 66 67 if ((cur = malloc(sizeof(struct pid))) == NULL) 68 return (NULL); 69 70 if (pipe(pdes) < 0) { 71 free(cur); 72 return (NULL); 73 } 74 75 switch (pid = fork()) { 76 case -1: /* Error. */ 77 (void)close(pdes[0]); 78 (void)close(pdes[1]); 79 free(cur); 80 return (NULL); 81 /* NOTREACHED */ 82 case 0: /* Child. */ 83 { 84 struct pid *pcur; 85 /* 86 * We fork()'d, we got our own copy of the list, no 87 * contention. 88 */ 89 for (pcur = pidlist; pcur; pcur = pcur->next) 90 close(fileno(pcur->fp)); 91 92 if (*type == 'r') { 93 (void) close(pdes[0]); 94 if (pdes[1] != STDOUT_FILENO) { 95 (void)dup2(pdes[1], STDOUT_FILENO); 96 (void)close(pdes[1]); 97 } 98 } else { 99 (void)close(pdes[1]); 100 if (pdes[0] != STDIN_FILENO) { 101 (void)dup2(pdes[0], STDIN_FILENO); 102 (void)close(pdes[0]); 103 } 104 } 105 argp[2] = (char *)program; 106 execve(_PATH_BSHELL, argp, environ); 107 _exit(127); 108 /* NOTREACHED */ 109 } 110 } 111 112 /* Parent; assume fdopen can't fail. */ 113 if (*type == 'r') { 114 iop = fdopen(pdes[0], type); 115 (void)close(pdes[1]); 116 } else { 117 iop = fdopen(pdes[1], type); 118 (void)close(pdes[0]); 119 } 120 121 /* Link into list of file descriptors. */ 122 cur->fp = iop; 123 cur->pid = pid; 124 cur->next = pidlist; 125 pidlist = cur; 126 127 return (iop); 128 } 129 130 /* 131 * pclose -- 132 * Pclose returns -1 if stream is not associated with a `popened' command, 133 * if already `pclosed', or waitpid returns an error. 134 */ 135 int 136 pclose(FILE *iop) 137 { 138 struct pid *cur, *last; 139 int pstat; 140 pid_t pid; 141 142 /* Find the appropriate file pointer. */ 143 for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next) 144 if (cur->fp == iop) 145 break; 146 147 if (cur == NULL) 148 return (-1); 149 150 (void)fclose(iop); 151 152 do { 153 pid = waitpid(cur->pid, &pstat, 0); 154 } while (pid == -1 && errno == EINTR); 155 156 /* Remove the entry from the linked list. */ 157 if (last == NULL) 158 pidlist = cur->next; 159 else 160 last->next = cur->next; 161 free(cur); 162 163 return (pid == -1 ? -1 : pstat); 164 } 165