popen.c revision 1.10 1 /* $NetBSD: popen.c,v 1.10 2002/03/02 14:59:37 wiz Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)popen.c 8.1 (Berkeley) 6/6/93";
40 #else
41 __RCSID("$NetBSD: popen.c,v 1.10 2002/03/02 14:59:37 wiz Exp $");
42 #endif
43 #endif /* not lint */
44
45 #include "rcv.h"
46 #include "extern.h"
47
48 #define READ 0
49 #define WRITE 1
50
51 struct fp {
52 FILE *fp;
53 int pipe;
54 int pid;
55 struct fp *link;
56 };
57 static struct fp *fp_head;
58
59 struct child {
60 int pid;
61 char done;
62 char free;
63 int status;
64 struct child *link;
65 };
66 static struct child *child;
67 static struct child *findchild(int);
68 static void delchild(struct child *);
69 static int file_pid(FILE *);
70
71 FILE *
72 Fopen(char *file, char *mode)
73 {
74 FILE *fp;
75
76 if ((fp = fopen(file, mode)) != NULL) {
77 register_file(fp, 0, 0);
78 (void) fcntl(fileno(fp), F_SETFD, 1);
79 }
80 return fp;
81 }
82
83 FILE *
84 Fdopen(int fd, char *mode)
85 {
86 FILE *fp;
87
88 if ((fp = fdopen(fd, mode)) != NULL) {
89 register_file(fp, 0, 0);
90 (void) fcntl(fileno(fp), F_SETFD, 1);
91 }
92 return fp;
93 }
94
95 int
96 Fclose(FILE *fp)
97 {
98 unregister_file(fp);
99 return fclose(fp);
100 }
101
102 FILE *
103 Popen(char *cmd, char *mode)
104 {
105 int p[2];
106 int myside, hisside, fd0, fd1;
107 int pid;
108 sigset_t nset;
109 FILE *fp;
110
111 if (pipe(p) < 0)
112 return NULL;
113 (void) fcntl(p[READ], F_SETFD, 1);
114 (void) fcntl(p[WRITE], F_SETFD, 1);
115 if (*mode == 'r') {
116 myside = p[READ];
117 fd0 = -1;
118 hisside = fd1 = p[WRITE];
119 } else {
120 myside = p[WRITE];
121 hisside = fd0 = p[READ];
122 fd1 = -1;
123 }
124 sigemptyset(&nset);
125 if ((pid = start_command(cmd, &nset, fd0, fd1, NOSTR, NOSTR, NOSTR)) < 0) {
126 close(p[READ]);
127 close(p[WRITE]);
128 return NULL;
129 }
130 (void) close(hisside);
131 if ((fp = fdopen(myside, mode)) != NULL)
132 register_file(fp, 1, pid);
133 return fp;
134 }
135
136 int
137 Pclose(FILE *ptr)
138 {
139 int i;
140 sigset_t nset, oset;
141
142 i = file_pid(ptr);
143 unregister_file(ptr);
144 (void) fclose(ptr);
145 sigemptyset(&nset);
146 sigaddset(&nset, SIGINT);
147 sigaddset(&nset, SIGHUP);
148 sigprocmask(SIG_BLOCK, &nset, &oset);
149 i = wait_child(i);
150 sigprocmask(SIG_SETMASK, &oset, NULL);
151 return i;
152 }
153
154 void
155 close_all_files(void)
156 {
157
158 while (fp_head)
159 if (fp_head->pipe)
160 (void) Pclose(fp_head->fp);
161 else
162 (void) Fclose(fp_head->fp);
163 }
164
165 void
166 register_file(FILE *fp, int pipe, int pid)
167 {
168 struct fp *fpp;
169
170 if ((fpp = (struct fp *) malloc(sizeof *fpp)) == NULL)
171 errx(1, "Out of memory");
172 fpp->fp = fp;
173 fpp->pipe = pipe;
174 fpp->pid = pid;
175 fpp->link = fp_head;
176 fp_head = fpp;
177 }
178
179 void
180 unregister_file(FILE *fp)
181 {
182 struct fp **pp, *p;
183
184 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
185 if (p->fp == fp) {
186 *pp = p->link;
187 free((char *) p);
188 return;
189 }
190 errx(1, "Invalid file pointer");
191 }
192
193 static int
194 file_pid(FILE *fp)
195 {
196 struct fp *p;
197
198 for (p = fp_head; p; p = p->link)
199 if (p->fp == fp)
200 return (p->pid);
201 errx(1, "Invalid file pointer");
202 /*NOTREACHED*/
203 }
204
205 /*
206 * Run a command without a shell, with optional arguments and splicing
207 * of stdin and stdout. The command name can be a sequence of words.
208 * Signals must be handled by the caller.
209 * "Mask" contains the signals to ignore in the new process.
210 * SIGINT is enabled unless it's in the mask.
211 */
212 /*VARARGS4*/
213 int
214 run_command(char *cmd, sigset_t *mask, int infd, int outfd, char *a0,
215 char *a1, char *a2)
216 {
217 int pid;
218
219 if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
220 return -1;
221 return wait_command(pid);
222 }
223
224 /*VARARGS4*/
225 int
226 start_command(char *cmd, sigset_t *mask, int infd, int outfd,
227 char *a0, char *a1, char *a2)
228 {
229 int pid;
230
231 if ((pid = vfork()) < 0) {
232 perror("fork");
233 return -1;
234 }
235 if (pid == 0) {
236 char *argv[100];
237 int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv);
238
239 if ((argv[i++] = a0) != NOSTR &&
240 (argv[i++] = a1) != NOSTR &&
241 (argv[i++] = a2) != NOSTR)
242 argv[i] = NOSTR;
243 prepare_child(mask, infd, outfd);
244 execvp(argv[0], argv);
245 perror(argv[0]);
246 _exit(1);
247 }
248 return pid;
249 }
250
251 void
252 prepare_child(sigset_t *nset, int infd, int outfd)
253 {
254 int i;
255 sigset_t eset;
256
257 /*
258 * All file descriptors other than 0, 1, and 2 are supposed to be
259 * close-on-exec.
260 */
261 if (infd >= 0)
262 dup2(infd, 0);
263 if (outfd >= 0)
264 dup2(outfd, 1);
265 for (i = 1; i < NSIG; i++)
266 if (nset != NULL && sigismember(nset, i))
267 (void) signal(i, SIG_IGN);
268 if (nset == NULL || !sigismember(nset, SIGINT))
269 (void) signal(SIGINT, SIG_DFL);
270 sigemptyset(&eset);
271 (void) sigprocmask(SIG_SETMASK, &eset, NULL);
272 }
273
274 int
275 wait_command(int pid)
276 {
277
278 if (wait_child(pid) < 0) {
279 printf("Fatal error in process.\n");
280 return -1;
281 }
282 return 0;
283 }
284
285 static struct child *
286 findchild(int pid)
287 {
288 struct child **cpp;
289
290 for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
291 cpp = &(*cpp)->link)
292 ;
293 if (*cpp == NULL) {
294 *cpp = (struct child *) malloc(sizeof (struct child));
295 (*cpp)->pid = pid;
296 (*cpp)->done = (*cpp)->free = 0;
297 (*cpp)->link = NULL;
298 }
299 return *cpp;
300 }
301
302 static void
303 delchild(struct child *cp)
304 {
305 struct child **cpp;
306
307 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
308 ;
309 *cpp = cp->link;
310 free((char *) cp);
311 }
312
313 void
314 sigchild(int signo)
315 {
316 int pid;
317 int status;
318 struct child *cp;
319
320 while ((pid = wait3(&status, WNOHANG, NULL)) > 0) {
321 cp = findchild(pid);
322 if (cp->free)
323 delchild(cp);
324 else {
325 cp->done = 1;
326 cp->status = status;
327 }
328 }
329 }
330
331 int wait_status;
332
333 /*
334 * Wait for a specific child to die.
335 */
336 int
337 wait_child(int pid)
338 {
339 sigset_t nset, oset;
340 struct child *cp = findchild(pid);
341 sigemptyset(&nset);
342 sigaddset(&nset, SIGCHLD);
343 sigprocmask(SIG_BLOCK, &nset, &oset);
344
345 while (!cp->done)
346 sigsuspend(&oset);
347 wait_status = cp->status;
348 delchild(cp);
349 sigprocmask(SIG_SETMASK, &oset, NULL);
350 return wait_status ? -1 : 0;
351 }
352
353 /*
354 * Mark a child as don't care.
355 */
356 void
357 free_child(int pid)
358 {
359 sigset_t nset, oset;
360 struct child *cp = findchild(pid);
361 sigemptyset(&nset);
362 sigaddset(&nset, SIGCHLD);
363 sigprocmask(SIG_BLOCK, &nset, &oset);
364
365 if (cp->done)
366 delchild(cp);
367 else
368 cp->free = 1;
369 sigprocmask(SIG_SETMASK, &oset, NULL);
370 }
371