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