xref: /netbsd-src/lib/libc/gen/popen.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: popen.c,v 1.15 1997/09/16 00:35:47 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software written by Ken Arnold and
8  * published in UNIX Review, Vol. 6, No. 8.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #if defined(LIBC_SCCS) && !defined(lint)
41 #if 0
42 static char sccsid[] = "@(#)popen.c	8.1 (Berkeley) 6/4/93";
43 #else
44 __RCSID("$NetBSD: popen.c,v 1.15 1997/09/16 00:35:47 thorpej Exp $");
45 #endif
46 #endif /* LIBC_SCCS and not lint */
47 
48 #include "namespace.h"
49 #include <sys/param.h>
50 #include <sys/wait.h>
51 
52 #include <signal.h>
53 #include <errno.h>
54 #include <unistd.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <paths.h>
59 
60 #ifdef __weak_alias
61 __weak_alias(popen,_popen);
62 __weak_alias(pclose,_pclose);
63 #endif
64 
65 static struct pid {
66 	struct pid *next;
67 	FILE *fp;
68 	pid_t pid;
69 } *pidlist;
70 
71 FILE *
72 popen(program, type)
73 	const char *program;
74 	const char *type;
75 {
76 	struct pid *cur, *old;
77 	FILE *iop;
78 	int pdes[2], pid;
79 #ifdef __GNUC__
80 	(void) &cur;
81 #endif
82 
83 	if ((*type != 'r' && *type != 'w') || type[1]) {
84 		errno = EINVAL;
85 		return (NULL);
86 	}
87 
88 	if ((cur = malloc(sizeof(struct pid))) == NULL)
89 		return (NULL);
90 
91 	if (pipe(pdes) < 0) {
92 		free(cur);
93 		return (NULL);
94 	}
95 
96 	switch (pid = vfork()) {
97 	case -1:			/* Error. */
98 		(void)close(pdes[0]);
99 		(void)close(pdes[1]);
100 		free(cur);
101 		return (NULL);
102 		/* NOTREACHED */
103 	case 0:				/* Child. */
104 		if (*type == 'r') {
105 			if (pdes[1] != STDOUT_FILENO) {
106 				(void)dup2(pdes[1], STDOUT_FILENO);
107 				(void)close(pdes[1]);
108 			}
109 			(void) close(pdes[0]);
110 		} else {
111 			if (pdes[0] != STDIN_FILENO) {
112 				(void)dup2(pdes[0], STDIN_FILENO);
113 				(void)close(pdes[0]);
114 			}
115 			(void)close(pdes[1]);
116 		}
117 
118 		/* POSIX.2 B.3.2.2 "popen() shall ensure that any streams
119 		   from previous popen() calls that remain open in the
120 		   parent process are closed in the new child process. */
121 		for (old = pidlist; old; old = old->next)
122 			close(fileno(old->fp));
123 
124 		execl(_PATH_BSHELL, "sh", "-c", program, NULL);
125 		_exit(127);
126 		/* NOTREACHED */
127 	}
128 
129 	/* Parent; assume fdopen can't fail. */
130 	if (*type == 'r') {
131 		iop = fdopen(pdes[0], type);
132 		(void)close(pdes[1]);
133 	} else {
134 		iop = fdopen(pdes[1], type);
135 		(void)close(pdes[0]);
136 	}
137 
138 	/* Link into list of file descriptors. */
139 	cur->fp = iop;
140 	cur->pid =  pid;
141 	cur->next = pidlist;
142 	pidlist = cur;
143 
144 	return (iop);
145 }
146 
147 /*
148  * pclose --
149  *	Pclose returns -1 if stream is not associated with a `popened' command,
150  *	if already `pclosed', or waitpid returns an error.
151  */
152 int
153 pclose(iop)
154 	FILE *iop;
155 {
156 	register struct pid *cur, *last;
157 	int pstat;
158 	pid_t pid;
159 
160 	(void)fclose(iop);
161 
162 	/* Find the appropriate file pointer. */
163 	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
164 		if (cur->fp == iop)
165 			break;
166 	if (cur == NULL)
167 		return (-1);
168 
169 	do {
170 		pid = waitpid(cur->pid, &pstat, 0);
171 	} while (pid == -1 && errno == EINTR);
172 
173 	/* Remove the entry from the linked list. */
174 	if (last == NULL)
175 		pidlist = cur->next;
176 	else
177 		last->next = cur->next;
178 	free(cur);
179 
180 	return (pid == -1 ? -1 : pstat);
181 }
182