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