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