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