xref: /netbsd-src/usr.bin/mail/popen.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*	$NetBSD: popen.c,v 1.5 1996/10/29 00:02:01 christos 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 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)popen.c	8.1 (Berkeley) 6/6/93";
39 #else
40 static char rcsid[] = "$NetBSD: popen.c,v 1.5 1996/10/29 00:02:01 christos Exp $";
41 #endif
42 #endif /* not lint */
43 
44 #include "rcv.h"
45 #include <sys/wait.h>
46 #include <fcntl.h>
47 #include "extern.h"
48 
49 #define READ 0
50 #define WRITE 1
51 
52 struct fp {
53 	FILE *fp;
54 	int pipe;
55 	int pid;
56 	struct fp *link;
57 };
58 static struct fp *fp_head;
59 
60 struct child {
61 	int pid;
62 	char done;
63 	char free;
64 	union wait status;
65 	struct child *link;
66 };
67 static struct child *child;
68 static struct child *findchild __P((int));
69 static void delchild __P((struct child *));
70 static int file_pid __P((FILE *));
71 
72 FILE *
73 Fopen(file, mode)
74 	char *file, *mode;
75 {
76 	FILE *fp;
77 
78 	if ((fp = fopen(file, mode)) != NULL) {
79 		register_file(fp, 0, 0);
80 		(void) fcntl(fileno(fp), F_SETFD, 1);
81 	}
82 	return fp;
83 }
84 
85 FILE *
86 Fdopen(fd, mode)
87 	int fd;
88 	char *mode;
89 {
90 	FILE *fp;
91 
92 	if ((fp = fdopen(fd, mode)) != NULL) {
93 		register_file(fp, 0, 0);
94 		(void) fcntl(fileno(fp), F_SETFD, 1);
95 	}
96 	return fp;
97 }
98 
99 int
100 Fclose(fp)
101 	FILE *fp;
102 {
103 	unregister_file(fp);
104 	return fclose(fp);
105 }
106 
107 FILE *
108 Popen(cmd, mode)
109 	char *cmd;
110 	char *mode;
111 {
112 	int p[2];
113 	int myside, hisside, fd0, fd1;
114 	int pid;
115 	sigset_t nset;
116 	FILE *fp;
117 
118 	if (pipe(p) < 0)
119 		return NULL;
120 	(void) fcntl(p[READ], F_SETFD, 1);
121 	(void) fcntl(p[WRITE], F_SETFD, 1);
122 	if (*mode == 'r') {
123 		myside = p[READ];
124 		fd0 = -1;
125 		hisside = fd1 = p[WRITE];
126 	} else {
127 		myside = p[WRITE];
128 		hisside = fd0 = p[READ];
129 		fd1 = -1;
130 	}
131 	sigemptyset(&nset);
132 	if ((pid = start_command(cmd, &nset, fd0, fd1, NOSTR, NOSTR, NOSTR)) < 0) {
133 		close(p[READ]);
134 		close(p[WRITE]);
135 		return NULL;
136 	}
137 	(void) close(hisside);
138 	if ((fp = fdopen(myside, mode)) != NULL)
139 		register_file(fp, 1, pid);
140 	return fp;
141 }
142 
143 int
144 Pclose(ptr)
145 	FILE *ptr;
146 {
147 	int i;
148 	sigset_t nset, oset;
149 
150 	i = file_pid(ptr);
151 	unregister_file(ptr);
152 	(void) fclose(ptr);
153 	sigemptyset(&nset);
154 	sigaddset(&nset, SIGINT);
155 	sigaddset(&nset, SIGHUP);
156 	sigprocmask(SIG_BLOCK, &nset, &oset);
157 	i = wait_child(i);
158 	sigprocmask(SIG_SETMASK, &oset, NULL);
159 	return i;
160 }
161 
162 void
163 close_all_files()
164 {
165 
166 	while (fp_head)
167 		if (fp_head->pipe)
168 			(void) Pclose(fp_head->fp);
169 		else
170 			(void) Fclose(fp_head->fp);
171 }
172 
173 void
174 register_file(fp, pipe, pid)
175 	FILE *fp;
176 	int pipe, pid;
177 {
178 	struct fp *fpp;
179 
180 	if ((fpp = (struct fp *) malloc(sizeof *fpp)) == NULL)
181 		panic("Out of memory");
182 	fpp->fp = fp;
183 	fpp->pipe = pipe;
184 	fpp->pid = pid;
185 	fpp->link = fp_head;
186 	fp_head = fpp;
187 }
188 
189 void
190 unregister_file(fp)
191 	FILE *fp;
192 {
193 	struct fp **pp, *p;
194 
195 	for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
196 		if (p->fp == fp) {
197 			*pp = p->link;
198 			free((char *) p);
199 			return;
200 		}
201 	panic("Invalid file pointer");
202 }
203 
204 static int
205 file_pid(fp)
206 	FILE *fp;
207 {
208 	struct fp *p;
209 
210 	for (p = fp_head; p; p = p->link)
211 		if (p->fp == fp)
212 			return (p->pid);
213 	panic("Invalid file pointer");
214 	/*NOTREACHED*/
215 }
216 
217 /*
218  * Run a command without a shell, with optional arguments and splicing
219  * of stdin and stdout.  The command name can be a sequence of words.
220  * Signals must be handled by the caller.
221  * "Mask" contains the signals to ignore in the new process.
222  * SIGINT is enabled unless it's in the mask.
223  */
224 /*VARARGS4*/
225 int
226 run_command(cmd, mask, infd, outfd, a0, a1, a2)
227 	char *cmd;
228 	sigset_t *mask;
229 	int infd, outfd;
230 	char *a0, *a1, *a2;
231 {
232 	int pid;
233 
234 	if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
235 		return -1;
236 	return wait_command(pid);
237 }
238 
239 /*VARARGS4*/
240 int
241 start_command(cmd, mask, infd, outfd, a0, a1, a2)
242 	char *cmd;
243 	sigset_t *mask;
244 	int infd, outfd;
245 	char *a0, *a1, *a2;
246 {
247 	int pid;
248 
249 	if ((pid = vfork()) < 0) {
250 		perror("fork");
251 		return -1;
252 	}
253 	if (pid == 0) {
254 		char *argv[100];
255 		int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv);
256 
257 		if ((argv[i++] = a0) != NOSTR &&
258 		    (argv[i++] = a1) != NOSTR &&
259 		    (argv[i++] = a2) != NOSTR)
260 			argv[i] = NOSTR;
261 		prepare_child(mask, infd, outfd);
262 		execvp(argv[0], argv);
263 		perror(argv[0]);
264 		_exit(1);
265 	}
266 	return pid;
267 }
268 
269 void
270 prepare_child(nset, infd, outfd)
271 	sigset_t *nset;
272 	int infd, outfd;
273 {
274 	int i;
275 	sigset_t fset;
276 
277 	/*
278 	 * All file descriptors other than 0, 1, and 2 are supposed to be
279 	 * close-on-exec.
280 	 */
281 	if (infd >= 0)
282 		dup2(infd, 0);
283 	if (outfd >= 0)
284 		dup2(outfd, 1);
285 	if (nset == NULL)
286 		return;
287 	if (nset != NULL) {
288 		for (i = 1; i <= NSIG; i++)
289 			if (sigismember(nset, i))
290 				(void) signal(i, SIG_IGN);
291 	}
292 	if (nset == NULL || !sigismember(nset, SIGINT))
293 		(void) signal(SIGINT, SIG_DFL);
294 	sigfillset(&fset);
295 	(void) sigprocmask(SIG_UNBLOCK, &fset, NULL);
296 }
297 
298 int
299 wait_command(pid)
300 	int pid;
301 {
302 
303 	if (wait_child(pid) < 0) {
304 		printf("Fatal error in process.\n");
305 		return -1;
306 	}
307 	return 0;
308 }
309 
310 static struct child *
311 findchild(pid)
312 	int pid;
313 {
314 	register struct child **cpp;
315 
316 	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
317 	     cpp = &(*cpp)->link)
318 			;
319 	if (*cpp == NULL) {
320 		*cpp = (struct child *) malloc(sizeof (struct child));
321 		(*cpp)->pid = pid;
322 		(*cpp)->done = (*cpp)->free = 0;
323 		(*cpp)->link = NULL;
324 	}
325 	return *cpp;
326 }
327 
328 static void
329 delchild(cp)
330 	register struct child *cp;
331 {
332 	register struct child **cpp;
333 
334 	for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
335 		;
336 	*cpp = cp->link;
337 	free((char *) cp);
338 }
339 
340 void
341 sigchild(signo)
342 	int signo;
343 {
344 	int pid;
345 	union wait status;
346 	register struct child *cp;
347 
348 	while ((pid =
349 	    wait3((int *)&status, WNOHANG, (struct rusage *)0)) > 0) {
350 		cp = findchild(pid);
351 		if (cp->free)
352 			delchild(cp);
353 		else {
354 			cp->done = 1;
355 			cp->status = status;
356 		}
357 	}
358 }
359 
360 union wait wait_status;
361 
362 /*
363  * Wait for a specific child to die.
364  */
365 int
366 wait_child(pid)
367 	int pid;
368 {
369 	sigset_t nset, oset;
370 	register struct child *cp = findchild(pid);
371 	sigemptyset(&nset);
372 	sigaddset(&nset, SIGCHLD);
373 	sigprocmask(SIG_BLOCK, &nset, &oset);
374 
375 	while (!cp->done)
376 		sigsuspend(&oset);
377 	wait_status = cp->status;
378 	delchild(cp);
379 	sigprocmask(SIG_SETMASK, &oset, NULL);
380 	return wait_status.w_status ? -1 : 0;
381 }
382 
383 /*
384  * Mark a child as don't care.
385  */
386 void
387 free_child(pid)
388 	int pid;
389 {
390 	sigset_t nset, oset;
391 	register struct child *cp = findchild(pid);
392 	sigemptyset(&nset);
393 	sigaddset(&nset, SIGCHLD);
394 	sigprocmask(SIG_BLOCK, &nset, &oset);
395 
396 	if (cp->done)
397 		delchild(cp);
398 	else
399 		cp->free = 1;
400 	sigprocmask(SIG_SETMASK, &oset, NULL);
401 }
402