popen.c revision 291114
1193323Sed/*
2193323Sed * Copyright (c) 1988, 1993
3193323Sed *	The Regents of the University of California.  All rights reserved.
4193323Sed *
5193323Sed * This code is derived from software written by Ken Arnold and
6193323Sed * published in UNIX Review, Vol. 6, No. 8.
7193323Sed *
8193323Sed * Redistribution and use in source and binary forms, with or without
9193323Sed * modification, are permitted provided that the following conditions
10193323Sed * are met:
11193323Sed * 1. Redistributions of source code must retain the above copyright
12193323Sed *    notice, this list of conditions and the following disclaimer.
13193323Sed * 2. Redistributions in binary form must reproduce the above copyright
14193323Sed *    notice, this list of conditions and the following disclaimer in the
15193323Sed *    documentation and/or other materials provided with the distribution.
16193323Sed * 4. Neither the name of the University nor the names of its contributors
17193323Sed *    may be used to endorse or promote products derived from this software
18193323Sed *    without specific prior written permission.
19193323Sed *
20249423Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21249423Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23198090Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30193323Sed * SUCH DAMAGE.
31193323Sed */
32193323Sed
33201360Srdivacky#if defined(LIBC_SCCS) && !defined(lint)
34193323Sedstatic char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
35206083Srdivacky#endif /* LIBC_SCCS and not lint */
36201360Srdivacky#include <sys/cdefs.h>
37205218Srdivacky__FBSDID("$FreeBSD: head/lib/libc/gen/popen.c 291114 2015-11-20 22:36:41Z rpokala $");
38201360Srdivacky
39208599Srdivacky#include "namespace.h"
40249423Sdim#include <sys/param.h>
41193323Sed#include <sys/queue.h>
42193323Sed#include <sys/wait.h>
43193323Sed
44198090Srdivacky#include <signal.h>
45193323Sed#include <errno.h>
46193323Sed#include <fcntl.h>
47193323Sed#include <unistd.h>
48193323Sed#include <stdio.h>
49193323Sed#include <stdlib.h>
50193323Sed#include <string.h>
51193323Sed#include <paths.h>
52193323Sed#include <pthread.h>
53193323Sed#include "un-namespace.h"
54193323Sed#include "libc_private.h"
55193323Sed
56234353Sdimextern char **environ;
57193323Sed
58193323Sedstruct pid {
59193323Sed	SLIST_ENTRY(pid) next;
60193323Sed	FILE *fp;
61193323Sed	pid_t pid;
62205218Srdivacky};
63205218Srdivackystatic SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
64206083Srdivackystatic pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
65206083Srdivacky
66206083Srdivacky#define	THREAD_LOCK()	if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex)
67207618Srdivacky#define	THREAD_UNLOCK()	if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex)
68207618Srdivacky
69207618SrdivackyFILE *
70207618Srdivackypopen(const char *command, const char *type)
71207618Srdivacky{
72207618Srdivacky	struct pid *cur;
73205218Srdivacky	FILE *iop;
74206083Srdivacky	int pdes[2], pid, twoway, cloexec;
75207618Srdivacky	int pdes_unused_in_parent;
76206083Srdivacky	char *argv[4];
77205218Srdivacky	struct pid *p;
78243830Sdim
79243830Sdim	cloexec = strchr(type, 'e') != NULL;
80205218Srdivacky	/*
81205218Srdivacky	 * Lite2 introduced two-way popen() pipes using _socketpair().
82205218Srdivacky	 * FreeBSD's pipe() is bidirectional, so we use that.
83207618Srdivacky	 */
84207618Srdivacky	if (strchr(type, '+')) {
85207618Srdivacky		twoway = 1;
86207618Srdivacky		type = "r+";
87206083Srdivacky	} else  {
88206083Srdivacky		twoway = 0;
89205218Srdivacky		if ((*type != 'r' && *type != 'w') ||
90206083Srdivacky		    (type[1] && (type[1] != 'e' || type[2])))
91205218Srdivacky			return (NULL);
92206083Srdivacky	}
93206083Srdivacky	if (pipe2(pdes, O_CLOEXEC) < 0)
94207618Srdivacky		return (NULL);
95205218Srdivacky
96206083Srdivacky	if ((cur = malloc(sizeof(struct pid))) == NULL) {
97206083Srdivacky		(void)_close(pdes[0]);
98207618Srdivacky		(void)_close(pdes[1]);
99205218Srdivacky		return (NULL);
100206083Srdivacky	}
101224145Sdim
102224145Sdim	if (*type == 'r') {
103224145Sdim		iop = fdopen(pdes[0], type);
104224145Sdim		pdes_unused_in_parent = pdes[1];
105224145Sdim	} else {
106224145Sdim		iop = fdopen(pdes[1], type);
107206083Srdivacky		pdes_unused_in_parent = pdes[0];
108206083Srdivacky	}
109206083Srdivacky	if (iop == NULL) {
110206083Srdivacky		(void)_close(pdes[0]);
111206083Srdivacky		(void)_close(pdes[1]);
112207618Srdivacky		free(cur);
113207618Srdivacky		return (NULL);
114205218Srdivacky	}
115205218Srdivacky
116202878Srdivacky	argv[0] = "sh";
117202878Srdivacky	argv[1] = "-c";
118202878Srdivacky	argv[2] = (char *)command;
119202878Srdivacky	argv[3] = NULL;
120193323Sed
121193323Sed	THREAD_LOCK();
122193323Sed	switch (pid = vfork()) {
123193323Sed	case -1:			/* Error. */
124193323Sed		THREAD_UNLOCK();
125193323Sed		/*
126193323Sed		 * The _close() closes the unused end of pdes[], while
127193323Sed		 * the fclose() closes the used end of pdes[], *and* cleans
128193323Sed		 * up iop.
129193323Sed		 */
130193323Sed		(void)_close(pdes_unused_in_parent);
131193323Sed		free(cur);
132207618Srdivacky		(void)fclose(iop);
133207618Srdivacky		return (NULL);
134208599Srdivacky		/* NOTREACHED */
135249423Sdim	case 0:				/* Child. */
136193323Sed		if (*type == 'r') {
137206274Srdivacky			/*
138234353Sdim			 * The _dup2() to STDIN_FILENO is repeated to avoid
139193323Sed			 * writing to pdes[1], which might corrupt the
140193323Sed			 * parent's copy.  This isn't good enough in
141193323Sed			 * general, since the _exit() is no return, so
142193323Sed			 * the compiler is free to corrupt all the local
143193323Sed			 * variables.
144193323Sed			 */
145193323Sed			if (pdes[1] != STDOUT_FILENO) {
146193323Sed				(void)_dup2(pdes[1], STDOUT_FILENO);
147193323Sed				if (twoway)
148193323Sed					(void)_dup2(STDOUT_FILENO, STDIN_FILENO);
149193323Sed			} else if (twoway && (pdes[1] != STDIN_FILENO)) {
150193323Sed				(void)_dup2(pdes[1], STDIN_FILENO);
151193323Sed				(void)_fcntl(pdes[1], F_SETFD, 0);
152193323Sed			} else
153193323Sed				(void)_fcntl(pdes[1], F_SETFD, 0);
154193323Sed		} else {
155193323Sed			if (pdes[0] != STDIN_FILENO) {
156193323Sed				(void)_dup2(pdes[0], STDIN_FILENO);
157193323Sed			} else
158193323Sed				(void)_fcntl(pdes[0], F_SETFD, 0);
159198090Srdivacky		}
160193323Sed		SLIST_FOREACH(p, &pidlist, next)
161193323Sed			(void)_close(fileno(p->fp));
162193323Sed		_execve(_PATH_BSHELL, argv, environ);
163193323Sed		_exit(127);
164193323Sed		/* NOTREACHED */
165193323Sed	}
166193323Sed	THREAD_UNLOCK();
167193323Sed
168193323Sed	/* Parent. */
169201360Srdivacky	(void)_close(pdes_unused_in_parent);
170201360Srdivacky
171201360Srdivacky	/* Link into list of file descriptors. */
172200581Srdivacky	cur->fp = iop;
173205218Srdivacky	cur->pid = pid;
174205218Srdivacky	THREAD_LOCK();
175205218Srdivacky	SLIST_INSERT_HEAD(&pidlist, cur, next);
176239462Sdim	THREAD_UNLOCK();
177239462Sdim
178239462Sdim	/*
179239462Sdim	 * To guard against undesired fd passing with concurrent calls,
180239462Sdim	 * only clear the close-on-exec flag after linking the file into
181239462Sdim	 * the list which will cause an explicit close.
182239462Sdim	 */
183239462Sdim	if (!cloexec)
184239462Sdim		(void)_fcntl(*type == 'r' ? pdes[0] : pdes[1], F_SETFD, 0);
185239462Sdim
186239462Sdim	return (iop);
187239462Sdim}
188239462Sdim
189239462Sdim/*
190239462Sdim * pclose --
191239462Sdim *	Pclose returns -1 if stream is not associated with a `popened' command,
192239462Sdim *	if already `pclosed', or waitpid returns an error.
193239462Sdim */
194239462Sdimint
195239462Sdimpclose(FILE *iop)
196239462Sdim{
197239462Sdim	struct pid *cur, *last = NULL;
198239462Sdim	int pstat;
199239462Sdim	pid_t pid;
200239462Sdim
201239462Sdim	/*
202239462Sdim	 * Find the appropriate file pointer and remove it from the list.
203239462Sdim	 */
204239462Sdim	THREAD_LOCK();
205239462Sdim	SLIST_FOREACH(cur, &pidlist, next) {
206239462Sdim		if (cur->fp == iop)
207239462Sdim			break;
208239462Sdim		last = cur;
209239462Sdim	}
210239462Sdim	if (cur == NULL) {
211239462Sdim		THREAD_UNLOCK();
212239462Sdim		return (-1);
213239462Sdim	}
214193323Sed	if (last == NULL)
215193323Sed		SLIST_REMOVE_HEAD(&pidlist, next);
216193323Sed	else
217193323Sed		SLIST_REMOVE_AFTER(last, next);
218193323Sed	THREAD_UNLOCK();
219193323Sed
220193323Sed	(void)fclose(iop);
221243830Sdim
222243830Sdim	do {
223200581Srdivacky		pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0);
224193323Sed	} while (pid == -1 && errno == EINTR);
225234353Sdim
226193323Sed	free(cur);
227193323Sed
228193323Sed	return (pid == -1 ? -1 : pstat);
229193323Sed}
230193323Sed