popen.c revision 256802
159243Sobrien/*
259243Sobrien * Copyright (c) 1988, 1993
359243Sobrien *	The Regents of the University of California.  All rights reserved.
459243Sobrien *
559243Sobrien * This code is derived from software written by Ken Arnold and
659243Sobrien * published in UNIX Review, Vol. 6, No. 8.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
1159243Sobrien * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
1659243Sobrien * 4. Neither the name of the University nor the names of its contributors
1759243Sobrien *    may be used to endorse or promote products derived from this software
1859243Sobrien *    without specific prior written permission.
1959243Sobrien *
2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059243Sobrien * SUCH DAMAGE.
3159243Sobrien */
3259243Sobrien
3359243Sobrien#if defined(LIBC_SCCS) && !defined(lint)
3459243Sobrienstatic char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
3559243Sobrien#endif /* LIBC_SCCS and not lint */
3659243Sobrien#include <sys/cdefs.h>
3759243Sobrien__FBSDID("$FreeBSD: head/lib/libc/gen/popen.c 256802 2013-10-20 20:50:17Z jilles $");
3859243Sobrien
3959243Sobrien#include "namespace.h"
4059243Sobrien#include <sys/param.h>
4159243Sobrien#include <sys/queue.h>
4259243Sobrien#include <sys/wait.h>
4359243Sobrien
4459243Sobrien#include <signal.h>
4559243Sobrien#include <errno.h>
4659243Sobrien#include <fcntl.h>
4759243Sobrien#include <unistd.h>
4859243Sobrien#include <stdio.h>
4959243Sobrien#include <stdlib.h>
5059243Sobrien#include <string.h>
5159243Sobrien#include <paths.h>
5259243Sobrien#include <pthread.h>
5359243Sobrien#include "un-namespace.h"
5459243Sobrien#include "libc_private.h"
5559243Sobrien
5659243Sobrienextern char **environ;
5759243Sobrien
5859243Sobrienstruct pid {
5959243Sobrien	SLIST_ENTRY(pid) next;
6059243Sobrien	FILE *fp;
6159243Sobrien	pid_t pid;
6259243Sobrien};
6359243Sobrienstatic SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
6459243Sobrienstatic pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
6559243Sobrien
6659243Sobrien#define	THREAD_LOCK()	if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex)
6759243Sobrien#define	THREAD_UNLOCK()	if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex)
6859243Sobrien
6959243SobrienFILE *
7059243Sobrienpopen(command, type)
7159243Sobrien	const char *command, *type;
7259243Sobrien{
7359243Sobrien	struct pid *cur;
7459243Sobrien	FILE *iop;
7559243Sobrien	int pdes[2], pid, twoway, cloexec;
7659243Sobrien	char *argv[4];
7759243Sobrien	struct pid *p;
7859243Sobrien
7959243Sobrien	cloexec = strchr(type, 'e') != NULL;
8059243Sobrien	/*
8159243Sobrien	 * Lite2 introduced two-way popen() pipes using _socketpair().
8259243Sobrien	 * FreeBSD's pipe() is bidirectional, so we use that.
8359243Sobrien	 */
8459243Sobrien	if (strchr(type, '+')) {
8559243Sobrien		twoway = 1;
8659243Sobrien		type = "r+";
8759243Sobrien	} else  {
8859243Sobrien		twoway = 0;
8959243Sobrien		if ((*type != 'r' && *type != 'w') ||
9059243Sobrien		    (type[1] && (type[1] != 'e' || type[2])))
9159243Sobrien			return (NULL);
9259243Sobrien	}
9359243Sobrien	if (pipe2(pdes, O_CLOEXEC) < 0)
9459243Sobrien		return (NULL);
9559243Sobrien
9659243Sobrien	if ((cur = malloc(sizeof(struct pid))) == NULL) {
9759243Sobrien		(void)_close(pdes[0]);
9859243Sobrien		(void)_close(pdes[1]);
9959243Sobrien		return (NULL);
10059243Sobrien	}
10159243Sobrien
10259243Sobrien	argv[0] = "sh";
10359243Sobrien	argv[1] = "-c";
10459243Sobrien	argv[2] = (char *)command;
10559243Sobrien	argv[3] = NULL;
10659243Sobrien
10759243Sobrien	THREAD_LOCK();
10859243Sobrien	switch (pid = vfork()) {
10959243Sobrien	case -1:			/* Error. */
11059243Sobrien		THREAD_UNLOCK();
11159243Sobrien		(void)_close(pdes[0]);
11259243Sobrien		(void)_close(pdes[1]);
11359243Sobrien		free(cur);
11459243Sobrien		return (NULL);
11559243Sobrien		/* NOTREACHED */
11659243Sobrien	case 0:				/* Child. */
11759243Sobrien		if (*type == 'r') {
11859243Sobrien			/*
11959243Sobrien			 * The _dup2() to STDIN_FILENO is repeated to avoid
12059243Sobrien			 * writing to pdes[1], which might corrupt the
12159243Sobrien			 * parent's copy.  This isn't good enough in
12259243Sobrien			 * general, since the _exit() is no return, so
12359243Sobrien			 * the compiler is free to corrupt all the local
12459243Sobrien			 * variables.
12559243Sobrien			 */
12659243Sobrien			if (pdes[1] != STDOUT_FILENO) {
12759243Sobrien				(void)_dup2(pdes[1], STDOUT_FILENO);
12859243Sobrien				if (twoway)
12959243Sobrien					(void)_dup2(STDOUT_FILENO, STDIN_FILENO);
13059243Sobrien			} else if (twoway && (pdes[1] != STDIN_FILENO)) {
13159243Sobrien				(void)_dup2(pdes[1], STDIN_FILENO);
13259243Sobrien				(void)_fcntl(pdes[1], F_SETFD, 0);
13359243Sobrien			} else
13459243Sobrien				(void)_fcntl(pdes[1], F_SETFD, 0);
13559243Sobrien		} else {
13659243Sobrien			if (pdes[0] != STDIN_FILENO) {
13759243Sobrien				(void)_dup2(pdes[0], STDIN_FILENO);
13859243Sobrien			} else
13959243Sobrien				(void)_fcntl(pdes[0], F_SETFD, 0);
14059243Sobrien		}
14159243Sobrien		SLIST_FOREACH(p, &pidlist, next)
14259243Sobrien			(void)_close(fileno(p->fp));
14359243Sobrien		_execve(_PATH_BSHELL, argv, environ);
14459243Sobrien		_exit(127);
14559243Sobrien		/* NOTREACHED */
14659243Sobrien	}
14759243Sobrien	THREAD_UNLOCK();
14859243Sobrien
14959243Sobrien	/* Parent; assume fdopen can't fail. */
15059243Sobrien	if (*type == 'r') {
15159243Sobrien		iop = fdopen(pdes[0], type);
15259243Sobrien		(void)_close(pdes[1]);
15359243Sobrien	} else {
15459243Sobrien		iop = fdopen(pdes[1], type);
15559243Sobrien		(void)_close(pdes[0]);
15659243Sobrien	}
15759243Sobrien
15859243Sobrien	/* Link into list of file descriptors. */
15959243Sobrien	cur->fp = iop;
16059243Sobrien	cur->pid = pid;
16159243Sobrien	THREAD_LOCK();
16259243Sobrien	SLIST_INSERT_HEAD(&pidlist, cur, next);
16359243Sobrien	THREAD_UNLOCK();
16459243Sobrien
16559243Sobrien	/*
16659243Sobrien	 * To guard against undesired fd passing with concurrent calls,
16759243Sobrien	 * only clear the close-on-exec flag after linking the file into
16859243Sobrien	 * the list which will cause an explicit close.
16959243Sobrien	 */
170	if (!cloexec)
171		(void)_fcntl(*type == 'r' ? pdes[0] : pdes[1], F_SETFD, 0);
172
173	return (iop);
174}
175
176/*
177 * pclose --
178 *	Pclose returns -1 if stream is not associated with a `popened' command,
179 *	if already `pclosed', or waitpid returns an error.
180 */
181int
182pclose(iop)
183	FILE *iop;
184{
185	struct pid *cur, *last = NULL;
186	int pstat;
187	pid_t pid;
188
189	/*
190	 * Find the appropriate file pointer and remove it from the list.
191	 */
192	THREAD_LOCK();
193	SLIST_FOREACH(cur, &pidlist, next) {
194		if (cur->fp == iop)
195			break;
196		last = cur;
197	}
198	if (cur == NULL) {
199		THREAD_UNLOCK();
200		return (-1);
201	}
202	if (last == NULL)
203		SLIST_REMOVE_HEAD(&pidlist, next);
204	else
205		SLIST_REMOVE_AFTER(last, next);
206	THREAD_UNLOCK();
207
208	(void)fclose(iop);
209
210	do {
211		pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0);
212	} while (pid == -1 && errno == EINTR);
213
214	free(cur);
215
216	return (pid == -1 ? -1 : pstat);
217}
218