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