xref: /minix3/lib/libc/gen/popen.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: popen.c,v 1.35 2015/02/02 22:07:05 christos Exp $	*/
22fe8fb19SBen Gras 
32fe8fb19SBen Gras /*
42fe8fb19SBen Gras  * Copyright (c) 1988, 1993
52fe8fb19SBen Gras  *	The Regents of the University of California.  All rights reserved.
62fe8fb19SBen Gras  *
72fe8fb19SBen Gras  * This code is derived from software written by Ken Arnold and
82fe8fb19SBen Gras  * published in UNIX Review, Vol. 6, No. 8.
92fe8fb19SBen Gras  *
102fe8fb19SBen Gras  * Redistribution and use in source and binary forms, with or without
112fe8fb19SBen Gras  * modification, are permitted provided that the following conditions
122fe8fb19SBen Gras  * are met:
132fe8fb19SBen Gras  * 1. Redistributions of source code must retain the above copyright
142fe8fb19SBen Gras  *    notice, this list of conditions and the following disclaimer.
152fe8fb19SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
162fe8fb19SBen Gras  *    notice, this list of conditions and the following disclaimer in the
172fe8fb19SBen Gras  *    documentation and/or other materials provided with the distribution.
182fe8fb19SBen Gras  * 3. Neither the name of the University nor the names of its contributors
192fe8fb19SBen Gras  *    may be used to endorse or promote products derived from this software
202fe8fb19SBen Gras  *    without specific prior written permission.
212fe8fb19SBen Gras  *
222fe8fb19SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
232fe8fb19SBen Gras  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
242fe8fb19SBen Gras  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
252fe8fb19SBen Gras  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
262fe8fb19SBen Gras  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
272fe8fb19SBen Gras  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
282fe8fb19SBen Gras  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
292fe8fb19SBen Gras  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
302fe8fb19SBen Gras  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
312fe8fb19SBen Gras  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
322fe8fb19SBen Gras  * SUCH DAMAGE.
332fe8fb19SBen Gras  */
342fe8fb19SBen Gras 
352fe8fb19SBen Gras #include <sys/cdefs.h>
362fe8fb19SBen Gras #if defined(LIBC_SCCS) && !defined(lint)
372fe8fb19SBen Gras #if 0
382fe8fb19SBen Gras static char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
392fe8fb19SBen Gras #else
40*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: popen.c,v 1.35 2015/02/02 22:07:05 christos Exp $");
412fe8fb19SBen Gras #endif
422fe8fb19SBen Gras #endif /* LIBC_SCCS and not lint */
432fe8fb19SBen Gras 
442fe8fb19SBen Gras #include "namespace.h"
452fe8fb19SBen Gras #include <sys/param.h>
462fe8fb19SBen Gras #include <sys/wait.h>
472fe8fb19SBen Gras #include <sys/socket.h>
482fe8fb19SBen Gras 
492fe8fb19SBen Gras #include <assert.h>
502fe8fb19SBen Gras #include <errno.h>
512fe8fb19SBen Gras #include <paths.h>
522fe8fb19SBen Gras #include <signal.h>
532fe8fb19SBen Gras #include <stdio.h>
542fe8fb19SBen Gras #include <stdlib.h>
552fe8fb19SBen Gras #include <string.h>
562fe8fb19SBen Gras #include <unistd.h>
57f14fb602SLionel Sambuc #include <fcntl.h>
582fe8fb19SBen Gras 
592fe8fb19SBen Gras #include "env.h"
602fe8fb19SBen Gras 
612fe8fb19SBen Gras #ifdef __weak_alias
622fe8fb19SBen Gras __weak_alias(popen,_popen)
632fe8fb19SBen Gras __weak_alias(pclose,_pclose)
642fe8fb19SBen Gras #endif
652fe8fb19SBen Gras 
662fe8fb19SBen Gras static struct pid {
672fe8fb19SBen Gras 	struct pid *next;
682fe8fb19SBen Gras 	FILE *fp;
692fe8fb19SBen Gras #ifdef _REENTRANT
702fe8fb19SBen Gras 	int fd;
712fe8fb19SBen Gras #endif
722fe8fb19SBen Gras 	pid_t pid;
732fe8fb19SBen Gras } *pidlist;
742fe8fb19SBen Gras 
752fe8fb19SBen Gras #ifdef _REENTRANT
762fe8fb19SBen Gras static rwlock_t pidlist_lock = RWLOCK_INITIALIZER;
772fe8fb19SBen Gras #endif
782fe8fb19SBen Gras 
79*0a6a1f1dSLionel Sambuc static struct pid *
pdes_get(int * pdes,const char ** type)80*0a6a1f1dSLionel Sambuc pdes_get(int *pdes, const char **type)
812fe8fb19SBen Gras {
82*0a6a1f1dSLionel Sambuc 	struct pid *cur;
83*0a6a1f1dSLionel Sambuc 	int flags = strchr(*type, 'e') ? O_CLOEXEC : 0;
84*0a6a1f1dSLionel Sambuc 	int serrno;
852fe8fb19SBen Gras 
86*0a6a1f1dSLionel Sambuc 	if (strchr(*type, '+')) {
87f14fb602SLionel Sambuc 		int stype = flags ? (SOCK_STREAM | SOCK_CLOEXEC) : SOCK_STREAM;
88*0a6a1f1dSLionel Sambuc 		*type = "r+";
89f14fb602SLionel Sambuc 		if (socketpair(AF_LOCAL, stype, 0, pdes) < 0)
90f14fb602SLionel Sambuc 			return NULL;
912fe8fb19SBen Gras 	} else  {
92*0a6a1f1dSLionel Sambuc 		*type = strrchr(*type, 'r') ? "r" : "w";
93f14fb602SLionel Sambuc 		if (pipe2(pdes, flags) == -1)
94f14fb602SLionel Sambuc 			return NULL;
952fe8fb19SBen Gras 	}
962fe8fb19SBen Gras 
97*0a6a1f1dSLionel Sambuc 	if ((cur = malloc(sizeof(*cur))) != NULL)
98*0a6a1f1dSLionel Sambuc 		return cur;
992fe8fb19SBen Gras 	serrno = errno;
1002fe8fb19SBen Gras 	(void)close(pdes[0]);
1012fe8fb19SBen Gras 	(void)close(pdes[1]);
1022fe8fb19SBen Gras 	errno = serrno;
103*0a6a1f1dSLionel Sambuc 	return NULL;
104*0a6a1f1dSLionel Sambuc }
105*0a6a1f1dSLionel Sambuc 
106*0a6a1f1dSLionel Sambuc static void
pdes_child(int * pdes,const char * type)107*0a6a1f1dSLionel Sambuc pdes_child(int *pdes, const char *type)
108*0a6a1f1dSLionel Sambuc {
109*0a6a1f1dSLionel Sambuc 	struct pid *old;
110*0a6a1f1dSLionel Sambuc 
1112fe8fb19SBen Gras 	/* POSIX.2 B.3.2.2 "popen() shall ensure that any streams
1122fe8fb19SBen Gras 	   from previous popen() calls that remain open in the
1132fe8fb19SBen Gras 	   parent process are closed in the new child process. */
1142fe8fb19SBen Gras 	for (old = pidlist; old; old = old->next)
1152fe8fb19SBen Gras #ifdef _REENTRANT
116*0a6a1f1dSLionel Sambuc 		(void)close(old->fd); /* don't allow a flush */
1172fe8fb19SBen Gras #else
118*0a6a1f1dSLionel Sambuc 		(void)close(fileno(old->fp)); /* don't allow a flush */
1192fe8fb19SBen Gras #endif
1202fe8fb19SBen Gras 
121*0a6a1f1dSLionel Sambuc 	if (type[0] == 'r') {
1222fe8fb19SBen Gras 		(void)close(pdes[0]);
1232fe8fb19SBen Gras 		if (pdes[1] != STDOUT_FILENO) {
1242fe8fb19SBen Gras 			(void)dup2(pdes[1], STDOUT_FILENO);
1252fe8fb19SBen Gras 			(void)close(pdes[1]);
1262fe8fb19SBen Gras 		}
127*0a6a1f1dSLionel Sambuc 		if (type[1] == '+')
1282fe8fb19SBen Gras 			(void)dup2(STDOUT_FILENO, STDIN_FILENO);
1292fe8fb19SBen Gras 	} else {
1302fe8fb19SBen Gras 		(void)close(pdes[1]);
1312fe8fb19SBen Gras 		if (pdes[0] != STDIN_FILENO) {
1322fe8fb19SBen Gras 			(void)dup2(pdes[0], STDIN_FILENO);
1332fe8fb19SBen Gras 			(void)close(pdes[0]);
1342fe8fb19SBen Gras 		}
1352fe8fb19SBen Gras 	}
1362fe8fb19SBen Gras }
137*0a6a1f1dSLionel Sambuc 
138*0a6a1f1dSLionel Sambuc static void
pdes_parent(int * pdes,struct pid * cur,pid_t pid,const char * type)139*0a6a1f1dSLionel Sambuc pdes_parent(int *pdes, struct pid *cur, pid_t pid, const char *type)
140*0a6a1f1dSLionel Sambuc {
141*0a6a1f1dSLionel Sambuc 	FILE *iop;
1422fe8fb19SBen Gras 
1432fe8fb19SBen Gras 	/* Parent; assume fdopen can't fail. */
144*0a6a1f1dSLionel Sambuc 	if (*type == 'r') {
145*0a6a1f1dSLionel Sambuc 		iop = fdopen(pdes[0], type);
1462fe8fb19SBen Gras #ifdef _REENTRANT
1472fe8fb19SBen Gras 		cur->fd = pdes[0];
1482fe8fb19SBen Gras #endif
1492fe8fb19SBen Gras 		(void)close(pdes[1]);
1502fe8fb19SBen Gras 	} else {
151*0a6a1f1dSLionel Sambuc 		iop = fdopen(pdes[1], type);
1522fe8fb19SBen Gras #ifdef _REENTRANT
1532fe8fb19SBen Gras 		cur->fd = pdes[1];
1542fe8fb19SBen Gras #endif
1552fe8fb19SBen Gras 		(void)close(pdes[0]);
1562fe8fb19SBen Gras 	}
1572fe8fb19SBen Gras 
1582fe8fb19SBen Gras 	/* Link into list of file descriptors. */
1592fe8fb19SBen Gras 	cur->fp = iop;
1602fe8fb19SBen Gras 	cur->pid =  pid;
1612fe8fb19SBen Gras 	cur->next = pidlist;
1622fe8fb19SBen Gras 	pidlist = cur;
163*0a6a1f1dSLionel Sambuc }
1642fe8fb19SBen Gras 
165*0a6a1f1dSLionel Sambuc static void
pdes_error(int * pdes,struct pid * cur)166*0a6a1f1dSLionel Sambuc pdes_error(int *pdes, struct pid *cur)
167*0a6a1f1dSLionel Sambuc {
168*0a6a1f1dSLionel Sambuc 	free(cur);
169*0a6a1f1dSLionel Sambuc 	(void)close(pdes[0]);
170*0a6a1f1dSLionel Sambuc 	(void)close(pdes[1]);
171*0a6a1f1dSLionel Sambuc }
172*0a6a1f1dSLionel Sambuc 
173*0a6a1f1dSLionel Sambuc FILE *
popen(const char * cmd,const char * type)174*0a6a1f1dSLionel Sambuc popen(const char *cmd, const char *type)
175*0a6a1f1dSLionel Sambuc {
176*0a6a1f1dSLionel Sambuc 	struct pid *cur;
177*0a6a1f1dSLionel Sambuc 	int pdes[2], serrno;
178*0a6a1f1dSLionel Sambuc 	pid_t pid;
179*0a6a1f1dSLionel Sambuc 
180*0a6a1f1dSLionel Sambuc 	_DIAGASSERT(cmd != NULL);
181*0a6a1f1dSLionel Sambuc 	_DIAGASSERT(type != NULL);
182*0a6a1f1dSLionel Sambuc 
183*0a6a1f1dSLionel Sambuc 	if ((cur = pdes_get(pdes, &type)) == NULL)
184*0a6a1f1dSLionel Sambuc 		return NULL;
185*0a6a1f1dSLionel Sambuc 
186*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
187*0a6a1f1dSLionel Sambuc 	(void)rwlock_rdlock(&pidlist_lock);
188*0a6a1f1dSLionel Sambuc #endif
189*0a6a1f1dSLionel Sambuc 	(void)__readlockenv();
190*0a6a1f1dSLionel Sambuc 	switch (pid = vfork()) {
191*0a6a1f1dSLionel Sambuc 	case -1:			/* Error. */
192*0a6a1f1dSLionel Sambuc 		serrno = errno;
193*0a6a1f1dSLionel Sambuc 		(void)__unlockenv();
194*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
195*0a6a1f1dSLionel Sambuc 		(void)rwlock_unlock(&pidlist_lock);
196*0a6a1f1dSLionel Sambuc #endif
197*0a6a1f1dSLionel Sambuc 		pdes_error(pdes, cur);
198*0a6a1f1dSLionel Sambuc 		errno = serrno;
199*0a6a1f1dSLionel Sambuc 		return NULL;
200*0a6a1f1dSLionel Sambuc 		/* NOTREACHED */
201*0a6a1f1dSLionel Sambuc 	case 0:				/* Child. */
202*0a6a1f1dSLionel Sambuc 		pdes_child(pdes, type);
203*0a6a1f1dSLionel Sambuc 		execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
204*0a6a1f1dSLionel Sambuc 		_exit(127);
205*0a6a1f1dSLionel Sambuc 		/* NOTREACHED */
206*0a6a1f1dSLionel Sambuc 	}
207*0a6a1f1dSLionel Sambuc 	(void)__unlockenv();
208*0a6a1f1dSLionel Sambuc 
209*0a6a1f1dSLionel Sambuc 	pdes_parent(pdes, cur, pid, type);
210*0a6a1f1dSLionel Sambuc 
211*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
212*0a6a1f1dSLionel Sambuc 	(void)rwlock_unlock(&pidlist_lock);
213*0a6a1f1dSLionel Sambuc #endif
214*0a6a1f1dSLionel Sambuc 
215*0a6a1f1dSLionel Sambuc 	return cur->fp;
216*0a6a1f1dSLionel Sambuc }
217*0a6a1f1dSLionel Sambuc 
218*0a6a1f1dSLionel Sambuc FILE *
popenve(const char * cmd,char * const * argv,char * const * envp,const char * type)219*0a6a1f1dSLionel Sambuc popenve(const char *cmd, char *const *argv, char *const *envp, const char *type)
220*0a6a1f1dSLionel Sambuc {
221*0a6a1f1dSLionel Sambuc 	struct pid *cur;
222*0a6a1f1dSLionel Sambuc 	int pdes[2], serrno;
223*0a6a1f1dSLionel Sambuc 	pid_t pid;
224*0a6a1f1dSLionel Sambuc 
225*0a6a1f1dSLionel Sambuc 	_DIAGASSERT(cmd != NULL);
226*0a6a1f1dSLionel Sambuc 	_DIAGASSERT(type != NULL);
227*0a6a1f1dSLionel Sambuc 
228*0a6a1f1dSLionel Sambuc 	if ((cur = pdes_get(pdes, &type)) == NULL)
229*0a6a1f1dSLionel Sambuc 		return NULL;
230*0a6a1f1dSLionel Sambuc 
231*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
232*0a6a1f1dSLionel Sambuc 	(void)rwlock_rdlock(&pidlist_lock);
233*0a6a1f1dSLionel Sambuc #endif
234*0a6a1f1dSLionel Sambuc 	switch (pid = vfork()) {
235*0a6a1f1dSLionel Sambuc 	case -1:			/* Error. */
236*0a6a1f1dSLionel Sambuc 		serrno = errno;
237*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
238*0a6a1f1dSLionel Sambuc 		(void)rwlock_unlock(&pidlist_lock);
239*0a6a1f1dSLionel Sambuc #endif
240*0a6a1f1dSLionel Sambuc 		pdes_error(pdes, cur);
241*0a6a1f1dSLionel Sambuc 		errno = serrno;
242*0a6a1f1dSLionel Sambuc 		return NULL;
243*0a6a1f1dSLionel Sambuc 		/* NOTREACHED */
244*0a6a1f1dSLionel Sambuc 	case 0:				/* Child. */
245*0a6a1f1dSLionel Sambuc 		pdes_child(pdes, type);
246*0a6a1f1dSLionel Sambuc 		execve(cmd, argv, envp);
247*0a6a1f1dSLionel Sambuc 		_exit(127);
248*0a6a1f1dSLionel Sambuc 		/* NOTREACHED */
249*0a6a1f1dSLionel Sambuc 	}
250*0a6a1f1dSLionel Sambuc 
251*0a6a1f1dSLionel Sambuc 	pdes_parent(pdes, cur, pid, type);
252*0a6a1f1dSLionel Sambuc 
253*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
254*0a6a1f1dSLionel Sambuc 	(void)rwlock_unlock(&pidlist_lock);
255*0a6a1f1dSLionel Sambuc #endif
256*0a6a1f1dSLionel Sambuc 
257*0a6a1f1dSLionel Sambuc 	return cur->fp;
2582fe8fb19SBen Gras }
2592fe8fb19SBen Gras 
2602fe8fb19SBen Gras /*
2612fe8fb19SBen Gras  * pclose --
2622fe8fb19SBen Gras  *	Pclose returns -1 if stream is not associated with a `popened' command,
2632fe8fb19SBen Gras  *	if already `pclosed', or waitpid returns an error.
2642fe8fb19SBen Gras  */
2652fe8fb19SBen Gras int
pclose(FILE * iop)266f14fb602SLionel Sambuc pclose(FILE *iop)
2672fe8fb19SBen Gras {
2682fe8fb19SBen Gras 	struct pid *cur, *last;
2692fe8fb19SBen Gras 	int pstat;
2702fe8fb19SBen Gras 	pid_t pid;
2712fe8fb19SBen Gras 
2722fe8fb19SBen Gras 	_DIAGASSERT(iop != NULL);
2732fe8fb19SBen Gras 
274*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
2752fe8fb19SBen Gras 	rwlock_wrlock(&pidlist_lock);
276*0a6a1f1dSLionel Sambuc #endif
2772fe8fb19SBen Gras 
2782fe8fb19SBen Gras 	/* Find the appropriate file pointer. */
2792fe8fb19SBen Gras 	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
2802fe8fb19SBen Gras 		if (cur->fp == iop)
2812fe8fb19SBen Gras 			break;
2822fe8fb19SBen Gras 	if (cur == NULL) {
283*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
2842fe8fb19SBen Gras 		(void)rwlock_unlock(&pidlist_lock);
285*0a6a1f1dSLionel Sambuc #endif
286*0a6a1f1dSLionel Sambuc 		errno = ESRCH;
287*0a6a1f1dSLionel Sambuc 		return -1;
2882fe8fb19SBen Gras 	}
2892fe8fb19SBen Gras 
2902fe8fb19SBen Gras 	(void)fclose(iop);
2912fe8fb19SBen Gras 
2922fe8fb19SBen Gras 	/* Remove the entry from the linked list. */
2932fe8fb19SBen Gras 	if (last == NULL)
2942fe8fb19SBen Gras 		pidlist = cur->next;
2952fe8fb19SBen Gras 	else
2962fe8fb19SBen Gras 		last->next = cur->next;
2972fe8fb19SBen Gras 
298*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
2992fe8fb19SBen Gras 	(void)rwlock_unlock(&pidlist_lock);
300*0a6a1f1dSLionel Sambuc #endif
3012fe8fb19SBen Gras 
3022fe8fb19SBen Gras 	do {
3032fe8fb19SBen Gras 		pid = waitpid(cur->pid, &pstat, 0);
3042fe8fb19SBen Gras 	} while (pid == -1 && errno == EINTR);
3052fe8fb19SBen Gras 
3062fe8fb19SBen Gras 	free(cur);
3072fe8fb19SBen Gras 
308*0a6a1f1dSLionel Sambuc 	return pid == -1 ? -1 : pstat;
3092fe8fb19SBen Gras }
310