popen.c revision 40219
1218887Sdim/*
2218887Sdim * Copyright (c) 1988, 1993
3218887Sdim *	The Regents of the University of California.  All rights reserved.
4218887Sdim *
5218887Sdim * This code is derived from software written by Ken Arnold and
6218887Sdim * published in UNIX Review, Vol. 6, No. 8.
7218887Sdim *
8218887Sdim * Redistribution and use in source and binary forms, with or without
9218887Sdim * modification, are permitted provided that the following conditions
10218887Sdim * are met:
11218887Sdim * 1. Redistributions of source code must retain the above copyright
12218887Sdim *    notice, this list of conditions and the following disclaimer.
13218887Sdim * 2. Redistributions in binary form must reproduce the above copyright
14218887Sdim *    notice, this list of conditions and the following disclaimer in the
15218887Sdim *    documentation and/or other materials provided with the distribution.
16239462Sdim * 3. All advertising materials mentioning features or use of this software
17218887Sdim *    must display the following acknowledgement:
18221345Sdim *	This product includes software developed by the University of
19218887Sdim *	California, Berkeley and its contributors.
20218887Sdim * 4. Neither the name of the University nor the names of its contributors
21218887Sdim *    may be used to endorse or promote products derived from this software
22218887Sdim *    without specific prior written permission.
23218887Sdim *
24218887Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25218887Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26218887Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27218887Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28218887Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29218887Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30221345Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31226633Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32221345Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33218887Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34218887Sdim * SUCH DAMAGE.
35218887Sdim */
36218887Sdim
37218887Sdim#if defined(LIBC_SCCS) && !defined(lint)
38218887Sdimstatic char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
39218887Sdim#endif /* LIBC_SCCS and not lint */
40218887Sdim
41218887Sdim#include <sys/param.h>
42218887Sdim#include <sys/wait.h>
43218887Sdim
44218887Sdim#include <signal.h>
45218887Sdim#include <errno.h>
46218887Sdim#include <unistd.h>
47218887Sdim#include <stdio.h>
48218887Sdim#include <stdlib.h>
49218887Sdim#include <string.h>
50218887Sdim#include <paths.h>
51218887Sdim
52218887Sdimextern char **environ;
53218887Sdim
54218887Sdimstatic struct pid {
55218887Sdim	struct pid *next;
56218887Sdim	FILE *fp;
57218887Sdim	pid_t pid;
58218887Sdim} *pidlist;
59218887Sdim
60218887SdimFILE *
61218887Sdimpopen(command, type)
62218887Sdim	const char *command, *type;
63218887Sdim{
64218887Sdim	struct pid *cur;
65218887Sdim	FILE *iop;
66218887Sdim	int pdes[2], pid, twoway;
67218887Sdim	char *argv[4];
68218887Sdim
69218887Sdim	/*
70218887Sdim	 * Lite2 introduced two-way popen() pipes using socketpair().
71218887Sdim	 * FreeBSD's pipe() is bidirectional, so we use that.
72218887Sdim	 */
73218887Sdim	if (strchr(type, '+')) {
74226633Sdim		twoway = 1;
75218887Sdim		type = "r+";
76218887Sdim	} else  {
77218887Sdim		twoway = 0;
78218887Sdim		if ((*type != 'r' && *type != 'w') || type[1])
79218887Sdim			return (NULL);
80218887Sdim	}
81218887Sdim	if (pipe(pdes) < 0)
82218887Sdim		return (NULL);
83218887Sdim
84218887Sdim	if ((cur = malloc(sizeof(struct pid))) == NULL) {
85218887Sdim		(void)close(pdes[0]);
86218887Sdim		(void)close(pdes[1]);
87218887Sdim		return (NULL);
88218887Sdim	}
89218887Sdim
90218887Sdim	argv[0] = "sh";
91218887Sdim	argv[1] = "-c";
92218887Sdim	argv[2] = (char *)command;
93218887Sdim	argv[3] = NULL;
94218887Sdim
95218887Sdim	switch (pid = vfork()) {
96218887Sdim	case -1:			/* Error. */
97218887Sdim		(void)close(pdes[0]);
98218887Sdim		(void)close(pdes[1]);
99218887Sdim		free(cur);
100218887Sdim		return (NULL);
101218887Sdim		/* NOTREACHED */
102218887Sdim	case 0:				/* Child. */
103218887Sdim		if (*type == 'r') {
104243830Sdim			/*
105218887Sdim			 * The dup2() to STDIN_FILENO is repeated to avoid
106218887Sdim			 * writing to pdes[1], which might corrupt the
107218887Sdim			 * parent's copy.  This isn't good enough in
108218887Sdim			 * general, since the _exit() is no return, so
109218887Sdim			 * the compiler is free to corrupt all the local
110218887Sdim			 * variables.
111218887Sdim			 */
112218887Sdim			(void) close(pdes[0]);
113226633Sdim			if (pdes[1] != STDOUT_FILENO) {
114218887Sdim				(void)dup2(pdes[1], STDOUT_FILENO);
115218887Sdim				(void)close(pdes[1]);
116218887Sdim				if (twoway)
117218887Sdim					(void)dup2(STDOUT_FILENO, STDIN_FILENO);
118218887Sdim			} else if (twoway && (pdes[1] != STDIN_FILENO))
119218887Sdim				(void)dup2(pdes[1], STDIN_FILENO);
120218887Sdim		} else {
121218887Sdim			if (pdes[0] != STDIN_FILENO) {
122218887Sdim				(void)dup2(pdes[0], STDIN_FILENO);
123218887Sdim				(void)close(pdes[0]);
124218887Sdim			}
125218887Sdim			(void)close(pdes[1]);
126218887Sdim		}
127221345Sdim		execve(_PATH_BSHELL, argv, environ);
128226633Sdim		_exit(127);
129218887Sdim		/* NOTREACHED */
130218887Sdim	}
131226633Sdim
132218887Sdim	/* Parent; assume fdopen can't fail. */
133218887Sdim	if (*type == 'r') {
134218887Sdim		iop = fdopen(pdes[0], type);
135218887Sdim		(void)close(pdes[1]);
136218887Sdim	} else {
137218887Sdim		iop = fdopen(pdes[1], type);
138218887Sdim		(void)close(pdes[0]);
139218887Sdim	}
140218887Sdim
141218887Sdim	/* Link into list of file descriptors. */
142218887Sdim	cur->fp = iop;
143218887Sdim	cur->pid =  pid;
144218887Sdim	cur->next = pidlist;
145218887Sdim	pidlist = cur;
146218887Sdim
147218887Sdim	return (iop);
148218887Sdim}
149218887Sdim
150218887Sdim/*
151218887Sdim * pclose --
152218887Sdim *	Pclose returns -1 if stream is not associated with a `popened' command,
153218887Sdim *	if already `pclosed', or waitpid returns an error.
154218887Sdim */
155218887Sdimint
156218887Sdimpclose(iop)
157218887Sdim	FILE *iop;
158218887Sdim{
159218887Sdim	register struct pid *cur, *last;
160218887Sdim	int omask;
161218887Sdim	int pstat;
162218887Sdim	pid_t pid;
163218887Sdim
164218887Sdim	/* Find the appropriate file pointer. */
165218887Sdim	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
166218887Sdim		if (cur->fp == iop)
167218887Sdim			break;
168218887Sdim	if (cur == NULL)
169218887Sdim		return (-1);
170218887Sdim
171218887Sdim	(void)fclose(iop);
172218887Sdim
173218887Sdim	do {
174218887Sdim		pid = waitpid(cur->pid, &pstat, 0);
175218887Sdim	} while (pid == -1 && errno == EINTR);
176218887Sdim
177218887Sdim	/* Remove the entry from the linked list. */
178218887Sdim	if (last == NULL)
179218887Sdim		pidlist = cur->next;
180218887Sdim	else
181218887Sdim		last->next = cur->next;
182218887Sdim	free(cur);
183218887Sdim
184218887Sdim	return (pid == -1 ? -1 : pstat);
185218887Sdim}
186218887Sdim