1301169Slidl/*	$NetBSD: popenve.c,v 1.2 2015/01/22 03:10:50 christos Exp $	*/
2301169Slidl
3301169Slidl/*
4301169Slidl * Copyright (c) 1988, 1993
5301169Slidl *	The Regents of the University of California.  All rights reserved.
6301169Slidl *
7301169Slidl * This code is derived from software written by Ken Arnold and
8301169Slidl * published in UNIX Review, Vol. 6, No. 8.
9301169Slidl *
10301169Slidl * Redistribution and use in source and binary forms, with or without
11301169Slidl * modification, are permitted provided that the following conditions
12301169Slidl * are met:
13301169Slidl * 1. Redistributions of source code must retain the above copyright
14301169Slidl *    notice, this list of conditions and the following disclaimer.
15301169Slidl * 2. Redistributions in binary form must reproduce the above copyright
16301169Slidl *    notice, this list of conditions and the following disclaimer in the
17301169Slidl *    documentation and/or other materials provided with the distribution.
18301169Slidl * 3. Neither the name of the University nor the names of its contributors
19301169Slidl *    may be used to endorse or promote products derived from this software
20301169Slidl *    without specific prior written permission.
21301169Slidl *
22301169Slidl * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23301169Slidl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24301169Slidl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25301169Slidl * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26301169Slidl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27301169Slidl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28301169Slidl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29301169Slidl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30301169Slidl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31301169Slidl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32301169Slidl * SUCH DAMAGE.
33301169Slidl */
34301169Slidl
35301169Slidl#ifdef HAVE_CONFIG_H
36301169Slidl#include "config.h"
37301169Slidl#endif
38301169Slidl
39301169Slidl#include <sys/cdefs.h>
40301169Slidl#if defined(LIBC_SCCS) && !defined(lint)
41301169Slidl#if 0
42301169Slidlstatic char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
43301169Slidl#else
44301169Slidl__RCSID("$NetBSD: popenve.c,v 1.2 2015/01/22 03:10:50 christos Exp $");
45301169Slidl#endif
46301169Slidl#endif /* LIBC_SCCS and not lint */
47301169Slidl
48301169Slidl#include <sys/param.h>
49301169Slidl#include <sys/wait.h>
50301169Slidl#include <sys/socket.h>
51301169Slidl
52301169Slidl#include <assert.h>
53301169Slidl#include <errno.h>
54301169Slidl#include <paths.h>
55301169Slidl#include <signal.h>
56301169Slidl#include <stdio.h>
57301169Slidl#include <stdlib.h>
58301169Slidl#include <string.h>
59301169Slidl#include <unistd.h>
60301169Slidl#include <fcntl.h>
61301169Slidl
62301169Slidl#ifdef __weak_alias
63301169Slidl__weak_alias(popen,_popen)
64301169Slidl__weak_alias(pclose,_pclose)
65301169Slidl#endif
66301169Slidl
67301169Slidlstatic struct pid {
68301169Slidl	struct pid *next;
69301169Slidl	FILE *fp;
70301169Slidl#ifdef _REENTRANT
71301169Slidl	int fd;
72301169Slidl#endif
73301169Slidl	pid_t pid;
74301169Slidl} *pidlist;
75301169Slidl
76301169Slidl#ifdef _REENTRANT
77301169Slidlstatic rwlock_t pidlist_lock = RWLOCK_INITIALIZER;
78301169Slidl#endif
79301169Slidl
80301169Slidlstatic struct pid *
81301169Slidlpdes_get(int *pdes, const char **type)
82301169Slidl{
83301169Slidl	struct pid *cur;
84301169Slidl	int flags = strchr(*type, 'e') ? O_CLOEXEC : 0;
85301169Slidl	int serrno;
86301169Slidl
87301169Slidl	if (strchr(*type, '+')) {
88301169Slidl#ifndef SOCK_CLOEXEC
89301169Slidl#define SOCK_CLOEXEC 0
90301169Slidl#endif
91301169Slidl		int stype = flags ? (SOCK_STREAM | SOCK_CLOEXEC) : SOCK_STREAM;
92301169Slidl		*type = "r+";
93301169Slidl		if (socketpair(AF_LOCAL, stype, 0, pdes) < 0)
94301169Slidl			return NULL;
95301169Slidl#if SOCK_CLOEXEC == 0
96301169Slidl		fcntl(pdes[0], F_SETFD, FD_CLOEXEC);
97301169Slidl		fcntl(pdes[1], F_SETFD, FD_CLOEXEC);
98301169Slidl#endif
99301169Slidl	} else  {
100301169Slidl		*type = strrchr(*type, 'r') ? "r" : "w";
101301169Slidl#if SOCK_CLOEXEC != 0
102301169Slidl		if (pipe2(pdes, flags) == -1)
103301169Slidl			return NULL;
104301169Slidl#else
105301169Slidl		if (pipe(pdes) == -1)
106301169Slidl			return NULL;
107301169Slidl		fcntl(pdes[0], F_SETFL, fcntl(pdes[0], F_GETFL) | flags);
108301169Slidl		fcntl(pdes[1], F_SETFL, fcntl(pdes[1], F_GETFL) | flags);
109301169Slidl#endif
110301169Slidl	}
111301169Slidl
112301169Slidl	if ((cur = malloc(sizeof(*cur))) != NULL)
113301169Slidl		return cur;
114301169Slidl	serrno = errno;
115301169Slidl	(void)close(pdes[0]);
116301169Slidl	(void)close(pdes[1]);
117301169Slidl	errno = serrno;
118301169Slidl	return NULL;
119301169Slidl}
120301169Slidl
121301169Slidlstatic void
122301169Slidlpdes_child(int *pdes, const char *type)
123301169Slidl{
124301169Slidl	struct pid *old;
125301169Slidl
126301169Slidl	/* POSIX.2 B.3.2.2 "popen() shall ensure that any streams
127301169Slidl	   from previous popen() calls that remain open in the
128301169Slidl	   parent process are closed in the new child process. */
129301169Slidl	for (old = pidlist; old; old = old->next)
130301169Slidl#ifdef _REENTRANT
131301169Slidl		(void)close(old->fd); /* don't allow a flush */
132301169Slidl#else
133301169Slidl		(void)close(fileno(old->fp)); /* don't allow a flush */
134301169Slidl#endif
135301169Slidl
136301169Slidl	if (type[0] == 'r') {
137301169Slidl		(void)close(pdes[0]);
138301169Slidl		if (pdes[1] != STDOUT_FILENO) {
139301169Slidl			(void)dup2(pdes[1], STDOUT_FILENO);
140301169Slidl			(void)close(pdes[1]);
141301169Slidl		}
142301169Slidl		if (type[1] == '+')
143301169Slidl			(void)dup2(STDOUT_FILENO, STDIN_FILENO);
144301169Slidl	} else {
145301169Slidl		(void)close(pdes[1]);
146301169Slidl		if (pdes[0] != STDIN_FILENO) {
147301169Slidl			(void)dup2(pdes[0], STDIN_FILENO);
148301169Slidl			(void)close(pdes[0]);
149301169Slidl		}
150301169Slidl	}
151301169Slidl}
152301169Slidl
153301169Slidlstatic void
154301169Slidlpdes_parent(int *pdes, struct pid *cur, pid_t pid, const char *type)
155301169Slidl{
156301169Slidl	FILE *iop;
157301169Slidl
158301169Slidl	/* Parent; assume fdopen can't fail. */
159301169Slidl	if (*type == 'r') {
160301169Slidl		iop = fdopen(pdes[0], type);
161301169Slidl#ifdef _REENTRANT
162301169Slidl		cur->fd = pdes[0];
163301169Slidl#endif
164301169Slidl		(void)close(pdes[1]);
165301169Slidl	} else {
166301169Slidl		iop = fdopen(pdes[1], type);
167301169Slidl#ifdef _REENTRANT
168301169Slidl		cur->fd = pdes[1];
169301169Slidl#endif
170301169Slidl		(void)close(pdes[0]);
171301169Slidl	}
172301169Slidl
173301169Slidl	/* Link into list of file descriptors. */
174301169Slidl	cur->fp = iop;
175301169Slidl	cur->pid =  pid;
176301169Slidl	cur->next = pidlist;
177301169Slidl	pidlist = cur;
178301169Slidl}
179301169Slidl
180301169Slidlstatic void
181301169Slidlpdes_error(int *pdes, struct pid *cur)
182301169Slidl{
183301169Slidl	free(cur);
184301169Slidl	(void)close(pdes[0]);
185301169Slidl	(void)close(pdes[1]);
186301169Slidl}
187301169Slidl
188301169SlidlFILE *
189301169Slidlpopenve(const char *cmd, char *const *argv, char *const *envp, const char *type)
190301169Slidl{
191301169Slidl	struct pid *cur;
192301169Slidl	int pdes[2], serrno;
193301169Slidl	pid_t pid;
194301169Slidl
195301169Slidl	if ((cur = pdes_get(pdes, &type)) == NULL)
196301169Slidl		return NULL;
197301169Slidl
198301169Slidl#ifdef _REENTRANT
199301169Slidl	(void)rwlock_rdlock(&pidlist_lock);
200301169Slidl#endif
201301169Slidl	switch (pid = vfork()) {
202301169Slidl	case -1:			/* Error. */
203301169Slidl		serrno = errno;
204301169Slidl#ifdef _REENTRANT
205301169Slidl		(void)rwlock_unlock(&pidlist_lock);
206301169Slidl#endif
207301169Slidl		pdes_error(pdes, cur);
208301169Slidl		errno = serrno;
209301169Slidl		return NULL;
210301169Slidl		/* NOTREACHED */
211301169Slidl	case 0:				/* Child. */
212301169Slidl		pdes_child(pdes, type);
213301169Slidl		execve(cmd, argv, envp);
214301169Slidl		_exit(127);
215301169Slidl		/* NOTREACHED */
216301169Slidl	}
217301169Slidl
218301169Slidl	pdes_parent(pdes, cur, pid, type);
219301169Slidl
220301169Slidl#ifdef _REENTRANT
221301169Slidl	(void)rwlock_unlock(&pidlist_lock);
222301169Slidl#endif
223301169Slidl
224301169Slidl	return cur->fp;
225301169Slidl}
226301169Slidl
227301169Slidl/*
228301169Slidl * pclose --
229301169Slidl *	Pclose returns -1 if stream is not associated with a `popened' command,
230301169Slidl *	if already `pclosed', or waitpid returns an error.
231301169Slidl */
232301169Slidlint
233301169Slidlpcloseve(FILE *iop)
234301169Slidl{
235301169Slidl	struct pid *cur, *last;
236301169Slidl	int pstat;
237301169Slidl	pid_t pid;
238301169Slidl
239301169Slidl#ifdef _REENTRANT
240301169Slidl	rwlock_wrlock(&pidlist_lock);
241301169Slidl#endif
242301169Slidl
243301169Slidl	/* Find the appropriate file pointer. */
244301169Slidl	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
245301169Slidl		if (cur->fp == iop)
246301169Slidl			break;
247301169Slidl	if (cur == NULL) {
248301169Slidl#ifdef _REENTRANT
249301169Slidl		(void)rwlock_unlock(&pidlist_lock);
250301169Slidl#endif
251301169Slidl		errno = ESRCH;
252301169Slidl		return -1;
253301169Slidl	}
254301169Slidl
255301169Slidl	(void)fclose(iop);
256301169Slidl
257301169Slidl	/* Remove the entry from the linked list. */
258301169Slidl	if (last == NULL)
259301169Slidl		pidlist = cur->next;
260301169Slidl	else
261301169Slidl		last->next = cur->next;
262301169Slidl
263301169Slidl#ifdef _REENTRANT
264301169Slidl	(void)rwlock_unlock(&pidlist_lock);
265301169Slidl#endif
266301169Slidl
267301169Slidl	do {
268301169Slidl		pid = waitpid(cur->pid, &pstat, 0);
269301169Slidl	} while (pid == -1 && errno == EINTR);
270301169Slidl
271301169Slidl	free(cur);
272301169Slidl
273301169Slidl	return pid == -1 ? -1 : pstat;
274301169Slidl}
275