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