popen.c revision 92921
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3574769Smikeh#if 0
361590Srgrimesstatic char sccsid[] = "@(#)popen.c	8.1 (Berkeley) 6/6/93";
3774769Smikeh#endif
3874769Smikehstatic const char rcsid[] =
3974769Smikeh  "$FreeBSD: head/usr.bin/mail/popen.c 92921 2002-03-22 01:33:25Z imp $";
401590Srgrimes#endif /* not lint */
411590Srgrimes
421590Srgrimes#include "rcv.h"
431590Srgrimes#include <sys/wait.h>
441590Srgrimes#include <fcntl.h>
451590Srgrimes#include "extern.h"
461590Srgrimes
471590Srgrimes#define READ 0
481590Srgrimes#define WRITE 1
491590Srgrimes
501590Srgrimesstruct fp {
5177274Smikeh	FILE	*fp;
5277274Smikeh	int	pipe;
5377274Smikeh	int	pid;
5477274Smikeh	struct	fp *link;
551590Srgrimes};
561590Srgrimesstatic struct fp *fp_head;
571590Srgrimes
581590Srgrimesstruct child {
5977274Smikeh	int	pid;
6077274Smikeh	char	done;
6177274Smikeh	char	free;
6277274Smikeh	int	status;
6377274Smikeh	struct	child *link;
641590Srgrimes};
651590Srgrimesstatic struct child *child;
6692921Simpstatic struct child *findchild(int);
6792921Simpstatic void delchild(struct child *);
6892921Simpstatic int file_pid(FILE *);
691590Srgrimes
701590SrgrimesFILE *
7177274SmikehFopen(path, mode)
7277274Smikeh	const char *path, *mode;
731590Srgrimes{
741590Srgrimes	FILE *fp;
751590Srgrimes
7677274Smikeh	if ((fp = fopen(path, mode)) != NULL) {
771590Srgrimes		register_file(fp, 0, 0);
7877274Smikeh		(void)fcntl(fileno(fp), F_SETFD, 1);
791590Srgrimes	}
8077274Smikeh	return (fp);
811590Srgrimes}
821590Srgrimes
831590SrgrimesFILE *
841590SrgrimesFdopen(fd, mode)
851590Srgrimes	int fd;
8677274Smikeh	const char *mode;
871590Srgrimes{
881590Srgrimes	FILE *fp;
891590Srgrimes
901590Srgrimes	if ((fp = fdopen(fd, mode)) != NULL) {
911590Srgrimes		register_file(fp, 0, 0);
9277274Smikeh		(void)fcntl(fileno(fp), F_SETFD, 1);
931590Srgrimes	}
9477274Smikeh	return (fp);
951590Srgrimes}
961590Srgrimes
971590Srgrimesint
981590SrgrimesFclose(fp)
991590Srgrimes	FILE *fp;
1001590Srgrimes{
1011590Srgrimes	unregister_file(fp);
10277274Smikeh	return (fclose(fp));
1031590Srgrimes}
1041590Srgrimes
1051590SrgrimesFILE *
1061590SrgrimesPopen(cmd, mode)
1071590Srgrimes	char *cmd;
10877274Smikeh	const char *mode;
1091590Srgrimes{
1101590Srgrimes	int p[2];
1111590Srgrimes	int myside, hisside, fd0, fd1;
1121590Srgrimes	int pid;
11388150Smikeh	sigset_t nset;
1141590Srgrimes	FILE *fp;
1151590Srgrimes
1161590Srgrimes	if (pipe(p) < 0)
11777274Smikeh		return (NULL);
11877274Smikeh	(void)fcntl(p[READ], F_SETFD, 1);
11977274Smikeh	(void)fcntl(p[WRITE], F_SETFD, 1);
1201590Srgrimes	if (*mode == 'r') {
1211590Srgrimes		myside = p[READ];
1221590Srgrimes		fd0 = -1;
1231590Srgrimes		hisside = fd1 = p[WRITE];
1241590Srgrimes	} else {
1251590Srgrimes		myside = p[WRITE];
1261590Srgrimes		hisside = fd0 = p[READ];
1271590Srgrimes		fd1 = -1;
1281590Srgrimes	}
12988150Smikeh	(void)sigemptyset(&nset);
13088150Smikeh	if ((pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL)) < 0) {
13177274Smikeh		(void)close(p[READ]);
13277274Smikeh		(void)close(p[WRITE]);
13377274Smikeh		return (NULL);
1341590Srgrimes	}
13577274Smikeh	(void)close(hisside);
1361590Srgrimes	if ((fp = fdopen(myside, mode)) != NULL)
1371590Srgrimes		register_file(fp, 1, pid);
13877274Smikeh	return (fp);
1391590Srgrimes}
1401590Srgrimes
1411590Srgrimesint
1421590SrgrimesPclose(ptr)
1431590Srgrimes	FILE *ptr;
1441590Srgrimes{
1451590Srgrimes	int i;
14688150Smikeh	sigset_t nset, oset;
1471590Srgrimes
1481590Srgrimes	i = file_pid(ptr);
1491590Srgrimes	unregister_file(ptr);
15077274Smikeh	(void)fclose(ptr);
15188150Smikeh	(void)sigemptyset(&nset);
15288150Smikeh	(void)sigaddset(&nset, SIGINT);
15388150Smikeh	(void)sigaddset(&nset, SIGHUP);
15488150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
1551590Srgrimes	i = wait_child(i);
15688150Smikeh	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
15777274Smikeh	return (i);
1581590Srgrimes}
1591590Srgrimes
1601590Srgrimesvoid
1611590Srgrimesclose_all_files()
1621590Srgrimes{
1631590Srgrimes
16477274Smikeh	while (fp_head != NULL)
1651590Srgrimes		if (fp_head->pipe)
16677274Smikeh			(void)Pclose(fp_head->fp);
1671590Srgrimes		else
16877274Smikeh			(void)Fclose(fp_head->fp);
1691590Srgrimes}
1701590Srgrimes
1711590Srgrimesvoid
1721590Srgrimesregister_file(fp, pipe, pid)
1731590Srgrimes	FILE *fp;
1741590Srgrimes	int pipe, pid;
1751590Srgrimes{
1761590Srgrimes	struct fp *fpp;
1771590Srgrimes
17877274Smikeh	if ((fpp = malloc(sizeof(*fpp))) == NULL)
17974769Smikeh		err(1, "Out of memory");
1801590Srgrimes	fpp->fp = fp;
1811590Srgrimes	fpp->pipe = pipe;
1821590Srgrimes	fpp->pid = pid;
1831590Srgrimes	fpp->link = fp_head;
1841590Srgrimes	fp_head = fpp;
1851590Srgrimes}
1861590Srgrimes
1871590Srgrimesvoid
1881590Srgrimesunregister_file(fp)
1891590Srgrimes	FILE *fp;
1901590Srgrimes{
1911590Srgrimes	struct fp **pp, *p;
1921590Srgrimes
19377274Smikeh	for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
1941590Srgrimes		if (p->fp == fp) {
1951590Srgrimes			*pp = p->link;
19677274Smikeh			(void)free(p);
1971590Srgrimes			return;
1981590Srgrimes		}
19974769Smikeh	errx(1, "Invalid file pointer");
20074769Smikeh	/*NOTREACHED*/
2011590Srgrimes}
2021590Srgrimes
20374769Smikehint
2041590Srgrimesfile_pid(fp)
2051590Srgrimes	FILE *fp;
2061590Srgrimes{
2071590Srgrimes	struct fp *p;
2081590Srgrimes
20977274Smikeh	for (p = fp_head; p != NULL; p = p->link)
2101590Srgrimes		if (p->fp == fp)
2111590Srgrimes			return (p->pid);
21274769Smikeh	errx(1, "Invalid file pointer");
2131590Srgrimes	/*NOTREACHED*/
2141590Srgrimes}
2151590Srgrimes
2161590Srgrimes/*
2171590Srgrimes * Run a command without a shell, with optional arguments and splicing
2181590Srgrimes * of stdin and stdout.  The command name can be a sequence of words.
2191590Srgrimes * Signals must be handled by the caller.
2201590Srgrimes * "Mask" contains the signals to ignore in the new process.
2211590Srgrimes * SIGINT is enabled unless it's in the mask.
2221590Srgrimes */
2231590Srgrimes/*VARARGS4*/
2241590Srgrimesint
2251590Srgrimesrun_command(cmd, mask, infd, outfd, a0, a1, a2)
2261590Srgrimes	char *cmd;
22788150Smikeh	sigset_t *mask;
22888150Smikeh	int infd, outfd;
2291590Srgrimes	char *a0, *a1, *a2;
2301590Srgrimes{
2311590Srgrimes	int pid;
2321590Srgrimes
2331590Srgrimes	if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
23477274Smikeh		return (-1);
23577274Smikeh	return (wait_command(pid));
2361590Srgrimes}
2371590Srgrimes
2381590Srgrimes/*VARARGS4*/
2391590Srgrimesint
2401590Srgrimesstart_command(cmd, mask, infd, outfd, a0, a1, a2)
2411590Srgrimes	char *cmd;
24288150Smikeh	sigset_t *mask;
24388150Smikeh	int infd, outfd;
2441590Srgrimes	char *a0, *a1, *a2;
2451590Srgrimes{
2461590Srgrimes	int pid;
2471590Srgrimes
24840188Sbde	if ((pid = fork()) < 0) {
24974769Smikeh		warn("fork");
25077274Smikeh		return (-1);
2511590Srgrimes	}
2521590Srgrimes	if (pid == 0) {
2531590Srgrimes		char *argv[100];
25477274Smikeh		int i = getrawlist(cmd, argv, sizeof(argv) / sizeof(*argv));
2551590Srgrimes
25677274Smikeh		if ((argv[i++] = a0) != NULL &&
25777274Smikeh		    (argv[i++] = a1) != NULL &&
25877274Smikeh		    (argv[i++] = a2) != NULL)
25977274Smikeh			argv[i] = NULL;
2601590Srgrimes		prepare_child(mask, infd, outfd);
2611590Srgrimes		execvp(argv[0], argv);
26274769Smikeh		warn("%s", argv[0]);
2631590Srgrimes		_exit(1);
2641590Srgrimes	}
26577274Smikeh	return (pid);
2661590Srgrimes}
2671590Srgrimes
2681590Srgrimesvoid
26988150Smikehprepare_child(nset, infd, outfd)
27088150Smikeh	sigset_t *nset;
27188150Smikeh	int infd, outfd;
2721590Srgrimes{
2731590Srgrimes	int i;
27488150Smikeh	sigset_t eset;
2751590Srgrimes
2761590Srgrimes	/*
2771590Srgrimes	 * All file descriptors other than 0, 1, and 2 are supposed to be
2781590Srgrimes	 * close-on-exec.
2791590Srgrimes	 */
2801590Srgrimes	if (infd >= 0)
2811590Srgrimes		dup2(infd, 0);
2821590Srgrimes	if (outfd >= 0)
2831590Srgrimes		dup2(outfd, 1);
28488150Smikeh	for (i = 1; i < NSIG; i++)
28588150Smikeh		if (nset != NULL && sigismember(nset, i))
28677274Smikeh			(void)signal(i, SIG_IGN);
28788150Smikeh	if (nset == NULL || !sigismember(nset, SIGINT))
28877274Smikeh		(void)signal(SIGINT, SIG_DFL);
28988150Smikeh	(void)sigemptyset(&eset);
29088150Smikeh	(void)sigprocmask(SIG_SETMASK, &eset, NULL);
2911590Srgrimes}
2921590Srgrimes
2931590Srgrimesint
2941590Srgrimeswait_command(pid)
2951590Srgrimes	int pid;
2961590Srgrimes{
2971590Srgrimes
2981590Srgrimes	if (wait_child(pid) < 0) {
2991590Srgrimes		printf("Fatal error in process.\n");
30077274Smikeh		return (-1);
3011590Srgrimes	}
30277274Smikeh	return (0);
3031590Srgrimes}
3041590Srgrimes
3051590Srgrimesstatic struct child *
3061590Srgrimesfindchild(pid)
3071590Srgrimes	int pid;
3081590Srgrimes{
30977274Smikeh	struct child **cpp;
3101590Srgrimes
3111590Srgrimes	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
31277274Smikeh	    cpp = &(*cpp)->link)
3131590Srgrimes			;
3141590Srgrimes	if (*cpp == NULL) {
31577274Smikeh		*cpp = malloc(sizeof(struct child));
31674769Smikeh		if (*cpp == NULL)
31774769Smikeh			err(1, "Out of memory");
3181590Srgrimes		(*cpp)->pid = pid;
3191590Srgrimes		(*cpp)->done = (*cpp)->free = 0;
3201590Srgrimes		(*cpp)->link = NULL;
3211590Srgrimes	}
32277274Smikeh	return (*cpp);
3231590Srgrimes}
3241590Srgrimes
3251590Srgrimesstatic void
3261590Srgrimesdelchild(cp)
32777274Smikeh	struct child *cp;
3281590Srgrimes{
32977274Smikeh	struct child **cpp;
3301590Srgrimes
3311590Srgrimes	for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
3321590Srgrimes		;
3331590Srgrimes	*cpp = cp->link;
33477274Smikeh	(void)free(cp);
3351590Srgrimes}
3361590Srgrimes
33777274Smikeh/*ARGSUSED*/
3381590Srgrimesvoid
3391590Srgrimessigchild(signo)
3401590Srgrimes	int signo;
3411590Srgrimes{
3421590Srgrimes	int pid;
34374769Smikeh	int status;
34477274Smikeh	struct child *cp;
3451590Srgrimes
34674769Smikeh	while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
3471590Srgrimes		cp = findchild(pid);
3481590Srgrimes		if (cp->free)
3491590Srgrimes			delchild(cp);
3501590Srgrimes		else {
3511590Srgrimes			cp->done = 1;
3521590Srgrimes			cp->status = status;
3531590Srgrimes		}
3541590Srgrimes	}
3551590Srgrimes}
3561590Srgrimes
35774769Smikehint wait_status;
3581590Srgrimes
3591590Srgrimes/*
3601590Srgrimes * Wait for a specific child to die.
3611590Srgrimes */
3621590Srgrimesint
3631590Srgrimeswait_child(pid)
3641590Srgrimes	int pid;
3651590Srgrimes{
36688150Smikeh	sigset_t nset, oset;
36777274Smikeh	struct child *cp = findchild(pid);
3681590Srgrimes
36988150Smikeh	(void)sigemptyset(&nset);
37088150Smikeh	(void)sigaddset(&nset, SIGCHLD);
37188150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
37288150Smikeh
3731590Srgrimes	while (!cp->done)
37488150Smikeh		(void)sigsuspend(&oset);
3751590Srgrimes	wait_status = cp->status;
3761590Srgrimes	delchild(cp);
37788150Smikeh	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
37877274Smikeh	return ((WIFEXITED(wait_status) && WEXITSTATUS(wait_status)) ? -1 : 0);
3791590Srgrimes}
3801590Srgrimes
3811590Srgrimes/*
3821590Srgrimes * Mark a child as don't care.
3831590Srgrimes */
3841590Srgrimesvoid
3851590Srgrimesfree_child(pid)
3861590Srgrimes	int pid;
3871590Srgrimes{
38888150Smikeh	sigset_t nset, oset;
38977274Smikeh	struct child *cp = findchild(pid);
3901590Srgrimes
39188150Smikeh	(void)sigemptyset(&nset);
39288150Smikeh	(void)sigaddset(&nset, SIGCHLD);
39388150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
39488150Smikeh
3951590Srgrimes	if (cp->done)
3961590Srgrimes		delchild(cp);
3971590Srgrimes	else
3981590Srgrimes		cp->free = 1;
39988150Smikeh	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
4001590Srgrimes}
401