Home | History | Annotate | Line # | Download | only in gen
popen.c revision 1.35.14.1
      1  1.35.14.1  pgoyette /*	$NetBSD: popen.c,v 1.35.14.1 2019/01/26 21:59:57 pgoyette Exp $	*/
      2       1.10       cgd 
      3        1.1       cgd /*
      4        1.7       jtc  * Copyright (c) 1988, 1993
      5        1.7       jtc  *	The Regents of the University of California.  All rights reserved.
      6        1.1       cgd  *
      7        1.1       cgd  * This code is derived from software written by Ken Arnold and
      8        1.1       cgd  * published in UNIX Review, Vol. 6, No. 8.
      9        1.1       cgd  *
     10        1.1       cgd  * Redistribution and use in source and binary forms, with or without
     11        1.1       cgd  * modification, are permitted provided that the following conditions
     12        1.1       cgd  * are met:
     13        1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     14        1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     15        1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     16        1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     17        1.1       cgd  *    documentation and/or other materials provided with the distribution.
     18       1.27       agc  * 3. Neither the name of the University nor the names of its contributors
     19        1.1       cgd  *    may be used to endorse or promote products derived from this software
     20        1.1       cgd  *    without specific prior written permission.
     21        1.1       cgd  *
     22        1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23        1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24        1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25        1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26        1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27        1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28        1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29        1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30        1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31        1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32        1.1       cgd  * SUCH DAMAGE.
     33        1.1       cgd  */
     34        1.1       cgd 
     35       1.13  christos #include <sys/cdefs.h>
     36        1.1       cgd #if defined(LIBC_SCCS) && !defined(lint)
     37       1.10       cgd #if 0
     38       1.16     perry static char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
     39       1.10       cgd #else
     40  1.35.14.1  pgoyette __RCSID("$NetBSD: popen.c,v 1.35.14.1 2019/01/26 21:59:57 pgoyette Exp $");
     41       1.10       cgd #endif
     42        1.1       cgd #endif /* LIBC_SCCS and not lint */
     43        1.1       cgd 
     44       1.14       jtc #include "namespace.h"
     45        1.1       cgd #include <sys/param.h>
     46        1.1       cgd #include <sys/wait.h>
     47       1.16     perry #include <sys/socket.h>
     48        1.7       jtc 
     49       1.23     lukem #include <assert.h>
     50       1.23     lukem #include <errno.h>
     51       1.23     lukem #include <paths.h>
     52        1.7       jtc #include <signal.h>
     53        1.1       cgd #include <stdio.h>
     54        1.1       cgd #include <stdlib.h>
     55        1.1       cgd #include <string.h>
     56       1.23     lukem #include <unistd.h>
     57       1.31  christos #include <fcntl.h>
     58       1.30      tron 
     59       1.30      tron #include "env.h"
     60       1.14       jtc 
     61       1.14       jtc #ifdef __weak_alias
     62       1.25   mycroft __weak_alias(popen,_popen)
     63       1.25   mycroft __weak_alias(pclose,_pclose)
     64       1.14       jtc #endif
     65        1.1       cgd 
     66        1.7       jtc static struct pid {
     67        1.7       jtc 	struct pid *next;
     68        1.7       jtc 	FILE *fp;
     69       1.28        cl #ifdef _REENTRANT
     70       1.28        cl 	int fd;
     71       1.28        cl #endif
     72        1.7       jtc 	pid_t pid;
     73        1.7       jtc } *pidlist;
     74        1.7       jtc 
     75       1.28        cl #ifdef _REENTRANT
     76  1.35.14.1  pgoyette static  mutex_t pidlist_mutex = MUTEX_INITIALIZER;
     77  1.35.14.1  pgoyette # define MUTEX_LOCK() \
     78  1.35.14.1  pgoyette     do { \
     79  1.35.14.1  pgoyette 	    if (__isthreaded) \
     80  1.35.14.1  pgoyette 		    mutex_lock(&pidlist_mutex); \
     81  1.35.14.1  pgoyette     } while (/*CONSTCOND*/0)
     82  1.35.14.1  pgoyette # define MUTEX_UNLOCK() \
     83  1.35.14.1  pgoyette     do { \
     84  1.35.14.1  pgoyette 	    if (__isthreaded) \
     85  1.35.14.1  pgoyette 		    mutex_unlock(&pidlist_mutex); \
     86  1.35.14.1  pgoyette     } while (/*CONSTCOND*/0)
     87  1.35.14.1  pgoyette #else
     88  1.35.14.1  pgoyette # define MUTEX_LOCK() __nothing
     89  1.35.14.1  pgoyette # define MUTEX_UNLOCK() __nothing
     90       1.28        cl #endif
     91       1.28        cl 
     92       1.33  christos static struct pid *
     93       1.33  christos pdes_get(int *pdes, const char **type)
     94        1.1       cgd {
     95       1.33  christos 	struct pid *cur;
     96       1.33  christos 	int flags = strchr(*type, 'e') ? O_CLOEXEC : 0;
     97       1.33  christos 	int serrno;
     98       1.23     lukem 
     99       1.33  christos 	if (strchr(*type, '+')) {
    100       1.31  christos 		int stype = flags ? (SOCK_STREAM | SOCK_CLOEXEC) : SOCK_STREAM;
    101       1.33  christos 		*type = "r+";
    102       1.31  christos 		if (socketpair(AF_LOCAL, stype, 0, pdes) < 0)
    103       1.31  christos 			return NULL;
    104       1.16     perry 	} else  {
    105       1.33  christos 		*type = strrchr(*type, 'r') ? "r" : "w";
    106       1.31  christos 		if (pipe2(pdes, flags) == -1)
    107       1.31  christos 			return NULL;
    108        1.9       jtc 	}
    109        1.1       cgd 
    110       1.33  christos 	if ((cur = malloc(sizeof(*cur))) != NULL)
    111       1.33  christos 		return cur;
    112       1.33  christos 	serrno = errno;
    113       1.33  christos 	(void)close(pdes[0]);
    114       1.33  christos 	(void)close(pdes[1]);
    115       1.33  christos 	errno = serrno;
    116       1.33  christos 	return NULL;
    117       1.33  christos }
    118       1.33  christos 
    119       1.33  christos static void
    120       1.33  christos pdes_child(int *pdes, const char *type)
    121       1.33  christos {
    122       1.33  christos 	struct pid *old;
    123        1.7       jtc 
    124       1.33  christos 	/* POSIX.2 B.3.2.2 "popen() shall ensure that any streams
    125       1.33  christos 	   from previous popen() calls that remain open in the
    126       1.33  christos 	   parent process are closed in the new child process. */
    127       1.33  christos 	for (old = pidlist; old; old = old->next)
    128       1.28        cl #ifdef _REENTRANT
    129       1.33  christos 		(void)close(old->fd); /* don't allow a flush */
    130       1.28        cl #else
    131       1.33  christos 		(void)close(fileno(old->fp)); /* don't allow a flush */
    132       1.28        cl #endif
    133       1.21        tv 
    134       1.33  christos 	if (type[0] == 'r') {
    135       1.33  christos 		(void)close(pdes[0]);
    136       1.33  christos 		if (pdes[1] != STDOUT_FILENO) {
    137       1.33  christos 			(void)dup2(pdes[1], STDOUT_FILENO);
    138       1.33  christos 			(void)close(pdes[1]);
    139       1.33  christos 		}
    140       1.33  christos 		if (type[1] == '+')
    141       1.33  christos 			(void)dup2(STDOUT_FILENO, STDIN_FILENO);
    142       1.33  christos 	} else {
    143       1.33  christos 		(void)close(pdes[1]);
    144       1.33  christos 		if (pdes[0] != STDIN_FILENO) {
    145       1.33  christos 			(void)dup2(pdes[0], STDIN_FILENO);
    146       1.21        tv 			(void)close(pdes[0]);
    147        1.1       cgd 		}
    148       1.33  christos 	}
    149       1.33  christos }
    150       1.12       jtc 
    151       1.33  christos static void
    152       1.33  christos pdes_parent(int *pdes, struct pid *cur, pid_t pid, const char *type)
    153       1.33  christos {
    154       1.33  christos 	FILE *iop;
    155        1.7       jtc 
    156        1.7       jtc 	/* Parent; assume fdopen can't fail. */
    157       1.33  christos 	if (*type == 'r') {
    158       1.33  christos 		iop = fdopen(pdes[0], type);
    159       1.28        cl #ifdef _REENTRANT
    160       1.28        cl 		cur->fd = pdes[0];
    161       1.28        cl #endif
    162        1.7       jtc 		(void)close(pdes[1]);
    163        1.1       cgd 	} else {
    164       1.33  christos 		iop = fdopen(pdes[1], type);
    165       1.28        cl #ifdef _REENTRANT
    166       1.28        cl 		cur->fd = pdes[1];
    167       1.28        cl #endif
    168        1.7       jtc 		(void)close(pdes[0]);
    169        1.1       cgd 	}
    170        1.7       jtc 
    171        1.7       jtc 	/* Link into list of file descriptors. */
    172        1.7       jtc 	cur->fp = iop;
    173        1.7       jtc 	cur->pid =  pid;
    174        1.7       jtc 	cur->next = pidlist;
    175        1.7       jtc 	pidlist = cur;
    176       1.33  christos }
    177       1.33  christos 
    178       1.33  christos static void
    179       1.33  christos pdes_error(int *pdes, struct pid *cur)
    180       1.33  christos {
    181       1.33  christos 	free(cur);
    182       1.33  christos 	(void)close(pdes[0]);
    183       1.33  christos 	(void)close(pdes[1]);
    184       1.33  christos }
    185       1.33  christos 
    186       1.33  christos FILE *
    187       1.33  christos popen(const char *cmd, const char *type)
    188       1.33  christos {
    189       1.33  christos 	struct pid *cur;
    190       1.33  christos 	int pdes[2], serrno;
    191       1.33  christos 	pid_t pid;
    192       1.33  christos 
    193       1.33  christos 	_DIAGASSERT(cmd != NULL);
    194       1.33  christos 	_DIAGASSERT(type != NULL);
    195       1.33  christos 
    196       1.33  christos 	if ((cur = pdes_get(pdes, &type)) == NULL)
    197       1.33  christos 		return NULL;
    198       1.33  christos 
    199  1.35.14.1  pgoyette 	MUTEX_LOCK();
    200       1.33  christos 	(void)__readlockenv();
    201       1.33  christos 	switch (pid = vfork()) {
    202       1.33  christos 	case -1:			/* Error. */
    203       1.33  christos 		serrno = errno;
    204       1.33  christos 		(void)__unlockenv();
    205  1.35.14.1  pgoyette 		MUTEX_UNLOCK();
    206       1.35  christos 		pdes_error(pdes, cur);
    207       1.33  christos 		errno = serrno;
    208       1.33  christos 		return NULL;
    209       1.33  christos 		/* NOTREACHED */
    210       1.33  christos 	case 0:				/* Child. */
    211       1.33  christos 		pdes_child(pdes, type);
    212       1.33  christos 		execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
    213       1.33  christos 		_exit(127);
    214       1.33  christos 		/* NOTREACHED */
    215       1.33  christos 	}
    216       1.33  christos 	(void)__unlockenv();
    217       1.33  christos 
    218       1.33  christos 	pdes_parent(pdes, cur, pid, type);
    219       1.33  christos 
    220  1.35.14.1  pgoyette 	MUTEX_UNLOCK();
    221       1.33  christos 
    222       1.33  christos 	return cur->fp;
    223       1.33  christos }
    224       1.33  christos 
    225       1.33  christos FILE *
    226       1.33  christos popenve(const char *cmd, char *const *argv, char *const *envp, const char *type)
    227       1.33  christos {
    228       1.33  christos 	struct pid *cur;
    229       1.33  christos 	int pdes[2], serrno;
    230       1.33  christos 	pid_t pid;
    231       1.33  christos 
    232       1.33  christos 	_DIAGASSERT(cmd != NULL);
    233       1.33  christos 	_DIAGASSERT(type != NULL);
    234       1.33  christos 
    235       1.33  christos 	if ((cur = pdes_get(pdes, &type)) == NULL)
    236       1.33  christos 		return NULL;
    237       1.33  christos 
    238  1.35.14.1  pgoyette 	MUTEX_LOCK();
    239       1.33  christos 	switch (pid = vfork()) {
    240       1.33  christos 	case -1:			/* Error. */
    241       1.33  christos 		serrno = errno;
    242  1.35.14.1  pgoyette 		MUTEX_UNLOCK();
    243       1.33  christos 		pdes_error(pdes, cur);
    244       1.33  christos 		errno = serrno;
    245       1.33  christos 		return NULL;
    246       1.33  christos 		/* NOTREACHED */
    247       1.33  christos 	case 0:				/* Child. */
    248       1.33  christos 		pdes_child(pdes, type);
    249       1.33  christos 		execve(cmd, argv, envp);
    250       1.33  christos 		_exit(127);
    251       1.33  christos 		/* NOTREACHED */
    252       1.33  christos 	}
    253       1.33  christos 
    254       1.33  christos 	pdes_parent(pdes, cur, pid, type);
    255       1.33  christos 
    256  1.35.14.1  pgoyette 	MUTEX_UNLOCK();
    257        1.7       jtc 
    258       1.33  christos 	return cur->fp;
    259        1.1       cgd }
    260        1.1       cgd 
    261        1.7       jtc /*
    262        1.7       jtc  * pclose --
    263        1.7       jtc  *	Pclose returns -1 if stream is not associated with a `popened' command,
    264        1.7       jtc  *	if already `pclosed', or waitpid returns an error.
    265        1.7       jtc  */
    266        1.1       cgd int
    267       1.32       abs pclose(FILE *iop)
    268        1.1       cgd {
    269       1.19     perry 	struct pid *cur, *last;
    270        1.9       jtc 	int pstat;
    271        1.1       cgd 	pid_t pid;
    272       1.23     lukem 
    273       1.23     lukem 	_DIAGASSERT(iop != NULL);
    274        1.1       cgd 
    275  1.35.14.1  pgoyette 	MUTEX_LOCK();
    276       1.28        cl 
    277        1.7       jtc 	/* Find the appropriate file pointer. */
    278        1.7       jtc 	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
    279        1.7       jtc 		if (cur->fp == iop)
    280        1.7       jtc 			break;
    281       1.28        cl 	if (cur == NULL) {
    282  1.35.14.1  pgoyette 		MUTEX_UNLOCK();
    283       1.33  christos 		errno = ESRCH;
    284       1.33  christos 		return -1;
    285       1.28        cl 	}
    286       1.16     perry 
    287       1.16     perry 	(void)fclose(iop);
    288        1.7       jtc 
    289        1.7       jtc 	/* Remove the entry from the linked list. */
    290        1.7       jtc 	if (last == NULL)
    291        1.7       jtc 		pidlist = cur->next;
    292        1.7       jtc 	else
    293        1.7       jtc 		last->next = cur->next;
    294       1.28        cl 
    295  1.35.14.1  pgoyette 	MUTEX_UNLOCK();
    296       1.28        cl 
    297       1.28        cl 	do {
    298       1.28        cl 		pid = waitpid(cur->pid, &pstat, 0);
    299       1.28        cl 	} while (pid == -1 && errno == EINTR);
    300       1.28        cl 
    301        1.7       jtc 	free(cur);
    302       1.28        cl 
    303       1.33  christos 	return pid == -1 ? -1 : pstat;
    304        1.1       cgd }
    305