xref: /dragonfly/usr.bin/mail/popen.c (revision 1d1731fa)
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.3 2003/10/04 20:36:48 hmp 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 		(void)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 		(void)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 	(void)fcntl(p[READ], F_SETFD, 1);
109 	(void)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 	(void)sigemptyset(&nset);
120 	if ((pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL)) < 0) {
121 		(void)close(p[READ]);
122 		(void)close(p[WRITE]);
123 		return (NULL);
124 	}
125 	(void)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 	(void)fclose(ptr);
140 	(void)sigemptyset(&nset);
141 	(void)sigaddset(&nset, SIGINT);
142 	(void)sigaddset(&nset, SIGHUP);
143 	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
144 	i = wait_child(i);
145 	(void)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 	(void)sigemptyset(&eset);
267 	(void)sigprocmask(SIG_SETMASK, &eset, NULL);
268 }
269 
270 int
271 wait_command(int pid)
272 {
273 
274 	if (wait_child(pid) < 0) {
275 		printf("Fatal error in process.\n");
276 		return (-1);
277 	}
278 	return (0);
279 }
280 
281 static struct child *
282 findchild(int pid)
283 {
284 	struct child **cpp;
285 
286 	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
287 	    cpp = &(*cpp)->link)
288 			;
289 	if (*cpp == NULL) {
290 		*cpp = malloc(sizeof(struct child));
291 		if (*cpp == NULL)
292 			err(1, "Out of memory");
293 		(*cpp)->pid = pid;
294 		(*cpp)->done = (*cpp)->free = 0;
295 		(*cpp)->link = NULL;
296 	}
297 	return (*cpp);
298 }
299 
300 static void
301 delchild(struct child *cp)
302 {
303 	struct child **cpp;
304 
305 	for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
306 		;
307 	*cpp = cp->link;
308 	(void)free(cp);
309 }
310 
311 /*ARGSUSED*/
312 void
313 sigchild(int signo)
314 {
315 	int pid;
316 	int status;
317 	struct child *cp;
318 
319 	while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
320 		cp = findchild(pid);
321 		if (cp->free)
322 			delchild(cp);
323 		else {
324 			cp->done = 1;
325 			cp->status = status;
326 		}
327 	}
328 }
329 
330 int wait_status;
331 
332 /*
333  * Wait for a specific child to die.
334  */
335 int
336 wait_child(int pid)
337 {
338 	sigset_t nset, oset;
339 	struct child *cp = findchild(pid);
340 
341 	(void)sigemptyset(&nset);
342 	(void)sigaddset(&nset, SIGCHLD);
343 	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
344 
345 	while (!cp->done)
346 		(void)sigsuspend(&oset);
347 	wait_status = cp->status;
348 	delchild(cp);
349 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
350 	return ((WIFEXITED(wait_status) && WEXITSTATUS(wait_status)) ? -1 : 0);
351 }
352 
353 /*
354  * Mark a child as don't care.
355  */
356 void
357 free_child(int pid)
358 {
359 	sigset_t nset, oset;
360 	struct child *cp = findchild(pid);
361 
362 	(void)sigemptyset(&nset);
363 	(void)sigaddset(&nset, SIGCHLD);
364 	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
365 
366 	if (cp->done)
367 		delchild(cp);
368 	else
369 		cp->free = 1;
370 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
371 }
372