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