11573Srgrimes/*
21573Srgrimes * Copyright (c) 1988, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * This code is derived from software written by Ken Arnold and
61573Srgrimes * published in UNIX Review, Vol. 6, No. 8.
71573Srgrimes *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
161573Srgrimes * 4. Neither the name of the University nor the names of its contributors
171573Srgrimes *    may be used to endorse or promote products derived from this software
181573Srgrimes *    without specific prior written permission.
191573Srgrimes *
201573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301573Srgrimes * SUCH DAMAGE.
311573Srgrimes */
321573Srgrimes
331573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
3423668Speterstatic char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
351573Srgrimes#endif /* LIBC_SCCS and not lint */
3690041Sobrien#include <sys/cdefs.h>
3790041Sobrien__FBSDID("$FreeBSD$");
381573Srgrimes
3971579Sdeischen#include "namespace.h"
401573Srgrimes#include <sys/param.h>
41180963Sed#include <sys/queue.h>
421573Srgrimes#include <sys/wait.h>
431573Srgrimes
441573Srgrimes#include <signal.h>
451573Srgrimes#include <errno.h>
46250827Sjilles#include <fcntl.h>
471573Srgrimes#include <unistd.h>
481573Srgrimes#include <stdio.h>
491573Srgrimes#include <stdlib.h>
501573Srgrimes#include <string.h>
511573Srgrimes#include <paths.h>
52108580Stjr#include <pthread.h>
5371579Sdeischen#include "un-namespace.h"
54108580Stjr#include "libc_private.h"
551573Srgrimes
5640219Speterextern char **environ;
5740219Speter
58180963Sedstruct pid {
59180963Sed	SLIST_ENTRY(pid) next;
601573Srgrimes	FILE *fp;
611573Srgrimes	pid_t pid;
62180963Sed};
63180963Sedstatic SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
64108580Stjrstatic pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
658870Srgrimes
66108580Stjr#define	THREAD_LOCK()	if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex)
67108580Stjr#define	THREAD_UNLOCK()	if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex)
68108580Stjr
691573SrgrimesFILE *
7023668Speterpopen(command, type)
7123668Speter	const char *command, *type;
721573Srgrimes{
731573Srgrimes	struct pid *cur;
741573Srgrimes	FILE *iop;
75250827Sjilles	int pdes[2], pid, twoway, cloexec;
7640219Speter	char *argv[4];
7740367Smsmith	struct pid *p;
781573Srgrimes
79250827Sjilles	cloexec = strchr(type, 'e') != NULL;
8023668Speter	/*
8171579Sdeischen	 * Lite2 introduced two-way popen() pipes using _socketpair().
8223668Speter	 * FreeBSD's pipe() is bidirectional, so we use that.
8323668Speter	 */
8423668Speter	if (strchr(type, '+')) {
8523668Speter		twoway = 1;
8623668Speter		type = "r+";
8723668Speter	} else  {
8823668Speter		twoway = 0;
89250827Sjilles		if ((*type != 'r' && *type != 'w') ||
90250827Sjilles		    (type[1] && (type[1] != 'e' || type[2])))
9123668Speter			return (NULL);
9223668Speter	}
93250827Sjilles	if ((cloexec ? pipe2(pdes, O_CLOEXEC) : pipe(pdes)) < 0)
941573Srgrimes		return (NULL);
951573Srgrimes
9623734Sbde	if ((cur = malloc(sizeof(struct pid))) == NULL) {
9756698Sjasone		(void)_close(pdes[0]);
9856698Sjasone		(void)_close(pdes[1]);
991573Srgrimes		return (NULL);
10023734Sbde	}
1011573Srgrimes
10240219Speter	argv[0] = "sh";
10340219Speter	argv[1] = "-c";
10440219Speter	argv[2] = (char *)command;
10540219Speter	argv[3] = NULL;
10640219Speter
107108580Stjr	THREAD_LOCK();
10840219Speter	switch (pid = vfork()) {
1091573Srgrimes	case -1:			/* Error. */
110108580Stjr		THREAD_UNLOCK();
11156698Sjasone		(void)_close(pdes[0]);
11256698Sjasone		(void)_close(pdes[1]);
1139272Shsu		free(cur);
1141573Srgrimes		return (NULL);
1151573Srgrimes		/* NOTREACHED */
1161573Srgrimes	case 0:				/* Child. */
1171573Srgrimes		if (*type == 'r') {
11824975Sdyson			/*
11971579Sdeischen			 * The _dup2() to STDIN_FILENO is repeated to avoid
12025063Sdyson			 * writing to pdes[1], which might corrupt the
12125063Sdyson			 * parent's copy.  This isn't good enough in
12225063Sdyson			 * general, since the _exit() is no return, so
12325063Sdyson			 * the compiler is free to corrupt all the local
12425063Sdyson			 * variables.
12524975Sdyson			 */
126250827Sjilles			if (!cloexec)
127250827Sjilles				(void)_close(pdes[0]);
12825063Sdyson			if (pdes[1] != STDOUT_FILENO) {
12971579Sdeischen				(void)_dup2(pdes[1], STDOUT_FILENO);
130250827Sjilles				if (!cloexec)
131250827Sjilles					(void)_close(pdes[1]);
13225084Sbde				if (twoway)
13371579Sdeischen					(void)_dup2(STDOUT_FILENO, STDIN_FILENO);
134250827Sjilles			} else if (twoway && (pdes[1] != STDIN_FILENO)) {
13571579Sdeischen				(void)_dup2(pdes[1], STDIN_FILENO);
136250827Sjilles				if (cloexec)
137250827Sjilles					(void)_fcntl(pdes[1], F_SETFD, 0);
138250827Sjilles			} else if (cloexec)
139250827Sjilles				(void)_fcntl(pdes[1], F_SETFD, 0);
1401573Srgrimes		} else {
1411573Srgrimes			if (pdes[0] != STDIN_FILENO) {
14271579Sdeischen				(void)_dup2(pdes[0], STDIN_FILENO);
143250827Sjilles				if (!cloexec)
144250827Sjilles					(void)_close(pdes[0]);
145250827Sjilles			} else if (cloexec)
146250827Sjilles				(void)_fcntl(pdes[0], F_SETFD, 0);
147250827Sjilles			if (!cloexec)
148250827Sjilles				(void)_close(pdes[1]);
1491573Srgrimes		}
150180963Sed		SLIST_FOREACH(p, &pidlist, next)
15156698Sjasone			(void)_close(fileno(p->fp));
15271579Sdeischen		_execve(_PATH_BSHELL, argv, environ);
1531573Srgrimes		_exit(127);
1541573Srgrimes		/* NOTREACHED */
1551573Srgrimes	}
156108580Stjr	THREAD_UNLOCK();
1571573Srgrimes
1581573Srgrimes	/* Parent; assume fdopen can't fail. */
1591573Srgrimes	if (*type == 'r') {
1601573Srgrimes		iop = fdopen(pdes[0], type);
16156698Sjasone		(void)_close(pdes[1]);
1621573Srgrimes	} else {
1631573Srgrimes		iop = fdopen(pdes[1], type);
16456698Sjasone		(void)_close(pdes[0]);
1651573Srgrimes	}
1661573Srgrimes
1671573Srgrimes	/* Link into list of file descriptors. */
1681573Srgrimes	cur->fp = iop;
169108580Stjr	cur->pid = pid;
170108580Stjr	THREAD_LOCK();
171180963Sed	SLIST_INSERT_HEAD(&pidlist, cur, next);
172108580Stjr	THREAD_UNLOCK();
1731573Srgrimes
1741573Srgrimes	return (iop);
1751573Srgrimes}
1761573Srgrimes
1771573Srgrimes/*
1781573Srgrimes * pclose --
1791573Srgrimes *	Pclose returns -1 if stream is not associated with a `popened' command,
1801573Srgrimes *	if already `pclosed', or waitpid returns an error.
1811573Srgrimes */
1821573Srgrimesint
1831573Srgrimespclose(iop)
1841573Srgrimes	FILE *iop;
1851573Srgrimes{
186180963Sed	struct pid *cur, *last = NULL;
18723668Speter	int pstat;
1881573Srgrimes	pid_t pid;
1891573Srgrimes
190108580Stjr	/*
191108580Stjr	 * Find the appropriate file pointer and remove it from the list.
192108580Stjr	 */
193108580Stjr	THREAD_LOCK();
194180963Sed	SLIST_FOREACH(cur, &pidlist, next) {
1951573Srgrimes		if (cur->fp == iop)
1961573Srgrimes			break;
197180963Sed		last = cur;
198180963Sed	}
199108580Stjr	if (cur == NULL) {
200108580Stjr		THREAD_UNLOCK();
2011573Srgrimes		return (-1);
202108580Stjr	}
203108580Stjr	if (last == NULL)
204180963Sed		SLIST_REMOVE_HEAD(&pidlist, next);
205108580Stjr	else
206192926Sed		SLIST_REMOVE_AFTER(last, next);
207108580Stjr	THREAD_UNLOCK();
2081573Srgrimes
20923668Speter	(void)fclose(iop);
21023668Speter
2111573Srgrimes	do {
21256698Sjasone		pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0);
2131573Srgrimes	} while (pid == -1 && errno == EINTR);
2141573Srgrimes
2151573Srgrimes	free(cur);
2168870Srgrimes
21723668Speter	return (pid == -1 ? -1 : pstat);
2181573Srgrimes}
219