popen.c revision 165903
1323530Savg/*
2323530Savg * Copyright (c) 1988, 1993
3323530Savg *	The Regents of the University of California.  All rights reserved.
4323530Savg *
5323530Savg * This code is derived from software written by Ken Arnold and
6323530Savg * published in UNIX Review, Vol. 6, No. 8.
7323530Savg *
8323530Savg * Redistribution and use in source and binary forms, with or without
9323530Savg * modification, are permitted provided that the following conditions
10323530Savg * are met:
11323530Savg * 1. Redistributions of source code must retain the above copyright
12323530Savg *    notice, this list of conditions and the following disclaimer.
13323530Savg * 2. Redistributions in binary form must reproduce the above copyright
14323530Savg *    notice, this list of conditions and the following disclaimer in the
15323530Savg *    documentation and/or other materials provided with the distribution.
16323530Savg * 4. Neither the name of the University nor the names of its contributors
17323530Savg *    may be used to endorse or promote products derived from this software
18323530Savg *    without specific prior written permission.
19323530Savg *
20323530Savg * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21323530Savg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22323530Savg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23323530Savg * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24323530Savg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25323530Savg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26323530Savg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27323530Savg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28323530Savg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29323530Savg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30323530Savg * SUCH DAMAGE.
31323530Savg */
32323530Savg
33323530Savg#if defined(LIBC_SCCS) && !defined(lint)
34323530Savgstatic char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
35323530Savg#endif /* LIBC_SCCS and not lint */
36323530Savg#include <sys/cdefs.h>
37323530Savg__FBSDID("$FreeBSD: head/lib/libc/gen/popen.c 165903 2007-01-09 00:28:16Z imp $");
38323530Savg
39323530Savg#include "namespace.h"
40323530Savg#include <sys/param.h>
41323530Savg#include <sys/wait.h>
42323530Savg
43323530Savg#include <signal.h>
44323530Savg#include <errno.h>
45323530Savg#include <unistd.h>
46323530Savg#include <stdio.h>
47323530Savg#include <stdlib.h>
48323530Savg#include <string.h>
49323530Savg#include <paths.h>
50323530Savg#include <pthread.h>
51323530Savg#include "un-namespace.h"
52323530Savg#include "libc_private.h"
53323530Savg
54323530Savgextern char **environ;
55323530Savg
56323530Savgstatic struct pid {
57323530Savg	struct pid *next;
58323530Savg	FILE *fp;
59323530Savg	pid_t pid;
60323530Savg} *pidlist;
61323530Savgstatic pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
62323530Savg
63323530Savg#define	THREAD_LOCK()	if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex)
64323530Savg#define	THREAD_UNLOCK()	if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex)
65323530Savg
66323530SavgFILE *
67323530Savgpopen(command, type)
68323530Savg	const char *command, *type;
69323530Savg{
70323530Savg	struct pid *cur;
71323530Savg	FILE *iop;
72323530Savg	int pdes[2], pid, twoway;
73323530Savg	char *argv[4];
74323530Savg	struct pid *p;
75323530Savg
76323530Savg	/*
77323530Savg	 * Lite2 introduced two-way popen() pipes using _socketpair().
78323530Savg	 * FreeBSD's pipe() is bidirectional, so we use that.
79323530Savg	 */
80323530Savg	if (strchr(type, '+')) {
81323530Savg		twoway = 1;
82323530Savg		type = "r+";
83323530Savg	} else  {
84323530Savg		twoway = 0;
85323530Savg		if ((*type != 'r' && *type != 'w') || type[1])
86323530Savg			return (NULL);
87323530Savg	}
88323530Savg	if (pipe(pdes) < 0)
89323530Savg		return (NULL);
90323530Savg
91323530Savg	if ((cur = malloc(sizeof(struct pid))) == NULL) {
92323530Savg		(void)_close(pdes[0]);
93323530Savg		(void)_close(pdes[1]);
94323530Savg		return (NULL);
95323530Savg	}
96323530Savg
97323530Savg	argv[0] = "sh";
98323530Savg	argv[1] = "-c";
99323530Savg	argv[2] = (char *)command;
100323530Savg	argv[3] = NULL;
101323530Savg
102323530Savg	THREAD_LOCK();
103323530Savg	switch (pid = vfork()) {
104323530Savg	case -1:			/* Error. */
105323530Savg		THREAD_UNLOCK();
106323530Savg		(void)_close(pdes[0]);
107323530Savg		(void)_close(pdes[1]);
108323530Savg		free(cur);
109323530Savg		return (NULL);
110323530Savg		/* NOTREACHED */
111323530Savg	case 0:				/* Child. */
112323530Savg		if (*type == 'r') {
113323530Savg			/*
114323530Savg			 * The _dup2() to STDIN_FILENO is repeated to avoid
115323530Savg			 * writing to pdes[1], which might corrupt the
116323530Savg			 * parent's copy.  This isn't good enough in
117323530Savg			 * general, since the _exit() is no return, so
118323530Savg			 * the compiler is free to corrupt all the local
119323530Savg			 * variables.
120323530Savg			 */
121323530Savg			(void)_close(pdes[0]);
122323530Savg			if (pdes[1] != STDOUT_FILENO) {
123323530Savg				(void)_dup2(pdes[1], STDOUT_FILENO);
124323530Savg				(void)_close(pdes[1]);
125323530Savg				if (twoway)
126323530Savg					(void)_dup2(STDOUT_FILENO, STDIN_FILENO);
127323530Savg			} else if (twoway && (pdes[1] != STDIN_FILENO))
128323530Savg				(void)_dup2(pdes[1], STDIN_FILENO);
129323530Savg		} else {
130323530Savg			if (pdes[0] != STDIN_FILENO) {
131323530Savg				(void)_dup2(pdes[0], STDIN_FILENO);
132323530Savg				(void)_close(pdes[0]);
133323530Savg			}
134323530Savg			(void)_close(pdes[1]);
135323530Savg		}
136323530Savg		for (p = pidlist; p; p = p->next) {
137323530Savg			(void)_close(fileno(p->fp));
138323530Savg		}
139323530Savg		_execve(_PATH_BSHELL, argv, environ);
140323530Savg		_exit(127);
141323530Savg		/* NOTREACHED */
142323530Savg	}
143323530Savg	THREAD_UNLOCK();
144323530Savg
145323530Savg	/* Parent; assume fdopen can't fail. */
146323530Savg	if (*type == 'r') {
147323530Savg		iop = fdopen(pdes[0], type);
148323530Savg		(void)_close(pdes[1]);
149323530Savg	} else {
150323530Savg		iop = fdopen(pdes[1], type);
151323530Savg		(void)_close(pdes[0]);
152323530Savg	}
153323530Savg
154323530Savg	/* Link into list of file descriptors. */
155323530Savg	cur->fp = iop;
156323530Savg	cur->pid = pid;
157323530Savg	THREAD_LOCK();
158323530Savg	cur->next = pidlist;
159323530Savg	pidlist = cur;
160323530Savg	THREAD_UNLOCK();
161323530Savg
162323530Savg	return (iop);
163323530Savg}
164323530Savg
165323530Savg/*
166323530Savg * pclose --
167323530Savg *	Pclose returns -1 if stream is not associated with a `popened' command,
168323530Savg *	if already `pclosed', or waitpid returns an error.
169323530Savg */
170323530Savgint
171323530Savgpclose(iop)
172323530Savg	FILE *iop;
173323530Savg{
174323530Savg	struct pid *cur, *last;
175323530Savg	int pstat;
176323530Savg	pid_t pid;
177323530Savg
178323530Savg	/*
179323530Savg	 * Find the appropriate file pointer and remove it from the list.
180323530Savg	 */
181323530Savg	THREAD_LOCK();
182323530Savg	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
183323530Savg		if (cur->fp == iop)
184323530Savg			break;
185323530Savg	if (cur == NULL) {
186323530Savg		THREAD_UNLOCK();
187323530Savg		return (-1);
188323530Savg	}
189323530Savg	if (last == NULL)
190323530Savg		pidlist = cur->next;
191323530Savg	else
192323530Savg		last->next = cur->next;
193323530Savg	THREAD_UNLOCK();
194323530Savg
195323530Savg	(void)fclose(iop);
196323530Savg
197323530Savg	do {
198323530Savg		pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0);
199323530Savg	} while (pid == -1 && errno == EINTR);
200323530Savg
201323530Savg	free(cur);
202323530Savg
203323530Savg	return (pid == -1 ? -1 : pstat);
204323530Savg}
205323530Savg