popen.c revision 1.25 1 /* $NetBSD: popen.c,v 1.25 2000/01/22 22:19:11 mycroft 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.25 2000/01/22 22:19:11 mycroft 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
84 #ifdef __GNUC__
85 /* This outrageous construct just to shut up a GCC warning. */
86 (void) &cur; (void) &twoway; (void) &type;
87 #endif
88
89 if (strchr(type, '+')) {
90 twoway = 1;
91 type = "r+";
92 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pdes) < 0)
93 return (NULL);
94 } else {
95 twoway = 0;
96 if ((*type != 'r' && *type != 'w') || type[1] ||
97 (pipe(pdes) < 0)) {
98 errno = EINVAL;
99 return (NULL);
100 }
101 }
102
103 if ((cur = malloc(sizeof(struct pid))) == NULL) {
104 (void)close(pdes[0]);
105 (void)close(pdes[1]);
106 errno = ENOMEM;
107 return (NULL);
108 }
109
110 switch (pid = vfork()) {
111 case -1: /* Error. */
112 serrno = errno;
113 free(cur);
114 (void)close(pdes[0]);
115 (void)close(pdes[1]);
116 errno = serrno;
117 return (NULL);
118 /* NOTREACHED */
119 case 0: /* Child. */
120 /* POSIX.2 B.3.2.2 "popen() shall ensure that any streams
121 from previous popen() calls that remain open in the
122 parent process are closed in the new child process. */
123 for (old = pidlist; old; old = old->next)
124 close(fileno(old->fp)); /* don't allow a flush */
125
126 if (*type == 'r') {
127 (void)close(pdes[0]);
128 if (pdes[1] != STDOUT_FILENO) {
129 (void)dup2(pdes[1], STDOUT_FILENO);
130 (void)close(pdes[1]);
131 }
132 if (twoway)
133 (void)dup2(STDOUT_FILENO, STDIN_FILENO);
134 } else {
135 (void)close(pdes[1]);
136 if (pdes[0] != STDIN_FILENO) {
137 (void)dup2(pdes[0], STDIN_FILENO);
138 (void)close(pdes[0]);
139 }
140 }
141
142 execl(_PATH_BSHELL, "sh", "-c", command, NULL);
143 _exit(127);
144 /* NOTREACHED */
145 }
146
147 /* Parent; assume fdopen can't fail. */
148 if (*type == 'r') {
149 iop = fdopen(pdes[0], type);
150 (void)close(pdes[1]);
151 } else {
152 iop = fdopen(pdes[1], type);
153 (void)close(pdes[0]);
154 }
155
156 /* Link into list of file descriptors. */
157 cur->fp = iop;
158 cur->pid = pid;
159 cur->next = pidlist;
160 pidlist = cur;
161
162 return (iop);
163 }
164
165 /*
166 * pclose --
167 * Pclose returns -1 if stream is not associated with a `popened' command,
168 * if already `pclosed', or waitpid returns an error.
169 */
170 int
171 pclose(iop)
172 FILE *iop;
173 {
174 struct pid *cur, *last;
175 int pstat;
176 pid_t pid;
177
178 _DIAGASSERT(iop != NULL);
179
180 /* Find the appropriate file pointer. */
181 for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
182 if (cur->fp == iop)
183 break;
184 if (cur == NULL)
185 return (-1);
186
187 (void)fclose(iop);
188
189 do {
190 pid = waitpid(cur->pid, &pstat, 0);
191 } while (pid == -1 && errno == EINTR);
192
193 /* Remove the entry from the linked list. */
194 if (last == NULL)
195 pidlist = cur->next;
196 else
197 last->next = cur->next;
198 free(cur);
199
200 return (pid == -1 ? -1 : pstat);
201 }
202