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