popen.c revision 1.23 1 /* $NetBSD: popen.c,v 1.23 1999/09/16 11:45:02 lukem Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software written by Ken Arnold and
8 * published in UNIX Review, Vol. 6, No. 8.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #if defined(LIBC_SCCS) && !defined(lint)
41 #if 0
42 static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 5/3/95";
43 #else
44 __RCSID("$NetBSD: popen.c,v 1.23 1999/09/16 11:45:02 lukem Exp $");
45 #endif
46 #endif /* LIBC_SCCS and not lint */
47
48 #include "namespace.h"
49 #include <sys/param.h>
50 #include <sys/wait.h>
51 #include <sys/socket.h>
52
53 #include <assert.h>
54 #include <errno.h>
55 #include <paths.h>
56 #include <signal.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 #ifdef __weak_alias
63 __weak_alias(popen,_popen);
64 __weak_alias(pclose,_pclose);
65 #endif
66
67 static struct pid {
68 struct pid *next;
69 FILE *fp;
70 pid_t pid;
71 } *pidlist;
72
73 FILE *
74 popen(command, type)
75 const char *command, *type;
76 {
77 struct pid *cur, *old;
78 FILE *iop;
79 int pdes[2], pid, twoway, serrno;
80
81 _DIAGASSERT(command != NULL);
82 _DIAGASSERT(type != NULL);
83 #ifdef _DIAGNOSTIC
84 if (command == NULL || type == NULL) {
85 errno = EFAULT;
86 return (NULL);
87 }
88 #endif
89
90 #ifdef __GNUC__
91 /* This outrageous construct just to shut up a GCC warning. */
92 (void) &cur; (void) &twoway; (void) &type;
93 #endif
94
95 if (strchr(type, '+')) {
96 twoway = 1;
97 type = "r+";
98 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pdes) < 0)
99 return (NULL);
100 } else {
101 twoway = 0;
102 if ((*type != 'r' && *type != 'w') || type[1] ||
103 (pipe(pdes) < 0)) {
104 errno = EINVAL;
105 return (NULL);
106 }
107 }
108
109 if ((cur = malloc(sizeof(struct pid))) == NULL) {
110 (void)close(pdes[0]);
111 (void)close(pdes[1]);
112 errno = ENOMEM;
113 return (NULL);
114 }
115
116 switch (pid = vfork()) {
117 case -1: /* Error. */
118 serrno = errno;
119 free(cur);
120 (void)close(pdes[0]);
121 (void)close(pdes[1]);
122 errno = serrno;
123 return (NULL);
124 /* NOTREACHED */
125 case 0: /* Child. */
126 /* POSIX.2 B.3.2.2 "popen() shall ensure that any streams
127 from previous popen() calls that remain open in the
128 parent process are closed in the new child process. */
129 for (old = pidlist; old; old = old->next)
130 close(fileno(old->fp)); /* don't allow a flush */
131
132 if (*type == 'r') {
133 (void)close(pdes[0]);
134 if (pdes[1] != STDOUT_FILENO) {
135 (void)dup2(pdes[1], STDOUT_FILENO);
136 (void)close(pdes[1]);
137 }
138 if (twoway)
139 (void)dup2(STDOUT_FILENO, STDIN_FILENO);
140 } else {
141 (void)close(pdes[1]);
142 if (pdes[0] != STDIN_FILENO) {
143 (void)dup2(pdes[0], STDIN_FILENO);
144 (void)close(pdes[0]);
145 }
146 }
147
148 execl(_PATH_BSHELL, "sh", "-c", command, NULL);
149 _exit(127);
150 /* NOTREACHED */
151 }
152
153 /* Parent; assume fdopen can't fail. */
154 if (*type == 'r') {
155 iop = fdopen(pdes[0], type);
156 (void)close(pdes[1]);
157 } else {
158 iop = fdopen(pdes[1], type);
159 (void)close(pdes[0]);
160 }
161
162 /* Link into list of file descriptors. */
163 cur->fp = iop;
164 cur->pid = pid;
165 cur->next = pidlist;
166 pidlist = cur;
167
168 return (iop);
169 }
170
171 /*
172 * pclose --
173 * Pclose returns -1 if stream is not associated with a `popened' command,
174 * if already `pclosed', or waitpid returns an error.
175 */
176 int
177 pclose(iop)
178 FILE *iop;
179 {
180 struct pid *cur, *last;
181 int pstat;
182 pid_t pid;
183
184 _DIAGASSERT(iop != NULL);
185 #ifdef _DIAGNOSTIC
186 if (iop == NULL) {
187 errno = EBADF;
188 return (-1);
189 }
190 #endif
191
192 /* Find the appropriate file pointer. */
193 for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
194 if (cur->fp == iop)
195 break;
196 if (cur == NULL)
197 return (-1);
198
199 (void)fclose(iop);
200
201 do {
202 pid = waitpid(cur->pid, &pstat, 0);
203 } while (pid == -1 && errno == EINTR);
204
205 /* Remove the entry from the linked list. */
206 if (last == NULL)
207 pidlist = cur->next;
208 else
209 last->next = cur->next;
210 free(cur);
211
212 return (pid == -1 ? -1 : pstat);
213 }
214