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