xref: /netbsd-src/usr.bin/mail/popen.c (revision 1f2744e6e4915c9da2a3f980279398c4cf7d5e6d)
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[] = "from: @(#)popen.c	8.1 (Berkeley) 6/6/93";
36 static char rcsid[] = "$Id: popen.c,v 1.3 1994/06/29 05:09:38 deraadt Exp $";
37 #endif /* not lint */
38 
39 #include "rcv.h"
40 #include <sys/wait.h>
41 #include <fcntl.h>
42 #include "extern.h"
43 
44 #define READ 0
45 #define WRITE 1
46 
47 struct fp {
48 	FILE *fp;
49 	int pipe;
50 	int pid;
51 	struct fp *link;
52 };
53 static struct fp *fp_head;
54 
55 struct child {
56 	int pid;
57 	char done;
58 	char free;
59 	union wait status;
60 	struct child *link;
61 };
62 static struct child *child;
63 static struct child *findchild __P((int));
64 static void delchild __P((struct child *));
65 
66 FILE *
67 Fopen(file, mode)
68 	char *file, *mode;
69 {
70 	FILE *fp;
71 
72 	if ((fp = fopen(file, mode)) != NULL) {
73 		register_file(fp, 0, 0);
74 		(void) fcntl(fileno(fp), F_SETFD, 1);
75 	}
76 	return fp;
77 }
78 
79 FILE *
80 Fdopen(fd, mode)
81 	int fd;
82 	char *mode;
83 {
84 	FILE *fp;
85 
86 	if ((fp = fdopen(fd, mode)) != NULL) {
87 		register_file(fp, 0, 0);
88 		(void) fcntl(fileno(fp), F_SETFD, 1);
89 	}
90 	return fp;
91 }
92 
93 int
94 Fclose(fp)
95 	FILE *fp;
96 {
97 	unregister_file(fp);
98 	return fclose(fp);
99 }
100 
101 FILE *
102 Popen(cmd, mode)
103 	char *cmd;
104 	char *mode;
105 {
106 	int p[2];
107 	int myside, hisside, fd0, fd1;
108 	int pid;
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 	if ((pid = start_command(cmd, 0, fd0, fd1, NOSTR, NOSTR, NOSTR)) < 0) {
125 		close(p[READ]);
126 		close(p[WRITE]);
127 		return NULL;
128 	}
129 	(void) close(hisside);
130 	if ((fp = fdopen(myside, mode)) != NULL)
131 		register_file(fp, 1, pid);
132 	return fp;
133 }
134 
135 int
136 Pclose(ptr)
137 	FILE *ptr;
138 {
139 	int i;
140 	int omask;
141 
142 	i = file_pid(ptr);
143 	unregister_file(ptr);
144 	(void) fclose(ptr);
145 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGHUP));
146 	i = wait_child(i);
147 	sigsetmask(omask);
148 	return i;
149 }
150 
151 void
152 close_all_files()
153 {
154 
155 	while (fp_head)
156 		if (fp_head->pipe)
157 			(void) Pclose(fp_head->fp);
158 		else
159 			(void) Fclose(fp_head->fp);
160 }
161 
162 void
163 register_file(fp, pipe, pid)
164 	FILE *fp;
165 	int pipe, pid;
166 {
167 	struct fp *fpp;
168 
169 	if ((fpp = (struct fp *) malloc(sizeof *fpp)) == NULL)
170 		panic("Out of memory");
171 	fpp->fp = fp;
172 	fpp->pipe = pipe;
173 	fpp->pid = pid;
174 	fpp->link = fp_head;
175 	fp_head = fpp;
176 }
177 
178 void
179 unregister_file(fp)
180 	FILE *fp;
181 {
182 	struct fp **pp, *p;
183 
184 	for (pp = &fp_head; p = *pp; pp = &p->link)
185 		if (p->fp == fp) {
186 			*pp = p->link;
187 			free((char *) p);
188 			return;
189 		}
190 	panic("Invalid file pointer");
191 }
192 
193 file_pid(fp)
194 	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 	panic("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(cmd, mask, infd, outfd, a0, a1, a2)
215 	char *cmd;
216 	int mask, infd, outfd;
217 	char *a0, *a1, *a2;
218 {
219 	int pid;
220 
221 	if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
222 		return -1;
223 	return wait_command(pid);
224 }
225 
226 /*VARARGS4*/
227 int
228 start_command(cmd, mask, infd, outfd, a0, a1, a2)
229 	char *cmd;
230 	int mask, infd, outfd;
231 	char *a0, *a1, *a2;
232 {
233 	int pid;
234 
235 	if ((pid = vfork()) < 0) {
236 		perror("fork");
237 		return -1;
238 	}
239 	if (pid == 0) {
240 		char *argv[100];
241 		int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv);
242 
243 		if ((argv[i++] = a0) != NOSTR &&
244 		    (argv[i++] = a1) != NOSTR &&
245 		    (argv[i++] = a2) != NOSTR)
246 			argv[i] = NOSTR;
247 		prepare_child(mask, infd, outfd);
248 		execvp(argv[0], argv);
249 		perror(argv[0]);
250 		_exit(1);
251 	}
252 	return pid;
253 }
254 
255 void
256 prepare_child(mask, infd, outfd)
257 	int mask, infd, outfd;
258 {
259 	int i;
260 
261 	/*
262 	 * All file descriptors other than 0, 1, and 2 are supposed to be
263 	 * close-on-exec.
264 	 */
265 	if (infd >= 0)
266 		dup2(infd, 0);
267 	if (outfd >= 0)
268 		dup2(outfd, 1);
269 	for (i = 1; i <= NSIG; i++)
270 		if (mask & sigmask(i))
271 			(void) signal(i, SIG_IGN);
272 	if ((mask & sigmask(SIGINT)) == 0)
273 		(void) signal(SIGINT, SIG_DFL);
274 	(void) sigsetmask(0);
275 }
276 
277 int
278 wait_command(pid)
279 	int pid;
280 {
281 
282 	if (wait_child(pid) < 0) {
283 		printf("Fatal error in process.\n");
284 		return -1;
285 	}
286 	return 0;
287 }
288 
289 static struct child *
290 findchild(pid)
291 	int pid;
292 {
293 	register struct child **cpp;
294 
295 	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
296 	     cpp = &(*cpp)->link)
297 			;
298 	if (*cpp == NULL) {
299 		*cpp = (struct child *) malloc(sizeof (struct child));
300 		(*cpp)->pid = pid;
301 		(*cpp)->done = (*cpp)->free = 0;
302 		(*cpp)->link = NULL;
303 	}
304 	return *cpp;
305 }
306 
307 static void
308 delchild(cp)
309 	register struct child *cp;
310 {
311 	register struct child **cpp;
312 
313 	for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
314 		;
315 	*cpp = cp->link;
316 	free((char *) cp);
317 }
318 
319 void
320 sigchild(signo)
321 	int signo;
322 {
323 	int pid;
324 	union wait status;
325 	register struct child *cp;
326 
327 	while ((pid =
328 	    wait3((int *)&status, WNOHANG, (struct rusage *)0)) > 0) {
329 		cp = findchild(pid);
330 		if (cp->free)
331 			delchild(cp);
332 		else {
333 			cp->done = 1;
334 			cp->status = status;
335 		}
336 	}
337 }
338 
339 union wait wait_status;
340 
341 /*
342  * Wait for a specific child to die.
343  */
344 int
345 wait_child(pid)
346 	int pid;
347 {
348 	int mask = sigblock(sigmask(SIGCHLD));
349 	register struct child *cp = findchild(pid);
350 
351 	while (!cp->done)
352 		sigpause(mask);
353 	wait_status = cp->status;
354 	delchild(cp);
355 	sigsetmask(mask);
356 	return wait_status.w_status ? -1 : 0;
357 }
358 
359 /*
360  * Mark a child as don't care.
361  */
362 void
363 free_child(pid)
364 	int pid;
365 {
366 	int mask = sigblock(sigmask(SIGCHLD));
367 	register struct child *cp = findchild(pid);
368 
369 	if (cp->done)
370 		delchild(cp);
371 	else
372 		cp->free = 1;
373 	sigsetmask(mask);
374 }
375