xref: /illumos-gate/usr/src/lib/libc/port/stdio/popen.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1988 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 #pragma weak pclose = _pclose
34 #pragma weak popen = _popen
35 
36 #include "synonyms.h"
37 #include "mtlib.h"
38 #include "file64.h"
39 #include <sys/types.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <wait.h>
43 #include <signal.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <thread.h>
48 #include <synch.h>
49 #include <spawn.h>
50 #include "stdiom.h"
51 #include "mse.h"
52 #include "libc.h"
53 
54 #define	tst(a, b) (*mode == 'r'? (b) : (a))
55 #define	RDR	0
56 #define	WTR	1
57 
58 #ifndef	_LP64
59 #define	MAX_FD (1 << (NBBY * (unsigned)sizeof (_lastbuf->_file))) /* now 256 */
60 #endif	/*	_LP64	*/
61 
62 static int _insert_nolock(pid_t, int);
63 
64 extern	int __xpg4;	/* defined in _xpg4.c; 0 if not xpg4-compiled program */
65 extern const char **environ;
66 
67 static mutex_t popen_lock = DEFAULTMUTEX;
68 
69 typedef struct node {
70 	pid_t	pid;
71 	int	fd;
72 	struct	node	*next;
73 } node_t;
74 
75 static	node_t  *head = NULL;
76 
77 
78 FILE *
79 popen(const char *cmd, const char *mode)
80 {
81 	int	p[2];
82 	pid_t	pid;
83 	int	myside, yourside;
84 	const char *shpath;
85 	FILE	*iop;
86 	int	stdio;
87 	node_t	*curr;
88 	char	*argvec[4];
89 	posix_spawn_file_actions_t fact;
90 	int	error;
91 	static const char *sun_path = "/bin/sh";
92 	static const char *xpg4_path = "/usr/xpg4/bin/sh";
93 	static const char *shell = "sh";
94 	static const char *sh_flg = "-c";
95 
96 	if (pipe(p) < 0)
97 		return (NULL);
98 
99 #ifndef	_LP64
100 	/* check that the fd's are in range for a struct FILE */
101 	if ((p[WTR] >= MAX_FD) || (p[RDR] >= MAX_FD)) {
102 		(void) close(p[WTR]);
103 		(void) close(p[RDR]);
104 		errno = EMFILE;
105 		return (NULL);
106 	}
107 #endif	/* _LP64 */
108 
109 	shpath = __xpg4? xpg4_path : sun_path;
110 	if (access(shpath, X_OK))	/* XPG4 Requirement: */
111 		shpath = "";		/* force child to fail immediately */
112 
113 	myside = tst(p[WTR], p[RDR]);
114 	yourside = tst(p[RDR], p[WTR]);
115 	/* myside and yourside reverse roles in child */
116 	stdio = tst(0, 1);
117 
118 	lmutex_lock(&popen_lock);
119 
120 	/* in the child, close all pipes from other popen's */
121 	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
122 		lmutex_unlock(&popen_lock);
123 		(void) close(myside);
124 		(void) close(yourside);
125 		errno = error;
126 		return (NULL);
127 	}
128 	for (curr = head; curr != NULL && error == 0; curr = curr->next)
129 		error = posix_spawn_file_actions_addclose(&fact, curr->fd);
130 	if (error == 0)
131 		error =  posix_spawn_file_actions_addclose(&fact, myside);
132 	if (yourside != stdio) {
133 		if (error == 0)
134 			error = posix_spawn_file_actions_adddup2(&fact,
135 				yourside, stdio);
136 		if (error == 0)
137 			error = posix_spawn_file_actions_addclose(&fact,
138 				yourside);
139 	}
140 	if (error) {
141 		lmutex_unlock(&popen_lock);
142 		(void) posix_spawn_file_actions_destroy(&fact);
143 		(void) close(myside);
144 		(void) close(yourside);
145 		errno = error;
146 		return (NULL);
147 	}
148 	argvec[0] = (char *)shell;
149 	argvec[1] = (char *)sh_flg;
150 	argvec[2] = (char *)cmd;
151 	argvec[3] = NULL;
152 	error = posix_spawn(&pid, shpath, &fact, NULL,
153 		(char *const *)argvec, (char *const *)environ);
154 	(void) posix_spawn_file_actions_destroy(&fact);
155 
156 	(void) close(yourside);
157 	if ((errno = error) != 0 || _insert_nolock(pid, myside) == -1) {
158 		lmutex_unlock(&popen_lock);
159 		(void) close(myside);
160 		return (NULL);
161 	}
162 
163 	lmutex_unlock(&popen_lock);
164 
165 	if ((iop = fdopen(myside, mode)) == NULL) {
166 		(void) _delete(myside);
167 		(void) close(myside);
168 		return (NULL);
169 	}
170 	_SET_ORIENTATION_BYTE(iop);
171 
172 	return (iop);
173 }
174 
175 int
176 pclose(FILE *ptr)
177 {
178 	pid_t	pid;
179 	int status;
180 
181 	pid = _delete(fileno(ptr));
182 
183 	/* mark this pipe closed */
184 	(void) fclose(ptr);
185 
186 	if (pid == -1)
187 		return (-1);
188 
189 	while (waitpid(pid, &status, 0) < 0) {
190 		/* If waitpid fails with EINTR, restart the waitpid call */
191 		if (errno != EINTR) {
192 			status = -1;
193 			break;
194 		}
195 	}
196 
197 	return (status);
198 }
199 
200 
201 static int
202 _insert_nolock(pid_t pid, int fd)
203 {
204 	node_t	*prev;
205 	node_t	*curr;
206 	node_t	*new;
207 
208 	for (prev = curr = head; curr != NULL; curr = curr->next)
209 		prev = curr;
210 
211 	if ((new = lmalloc(sizeof (node_t))) == NULL)
212 		return (-1);
213 
214 	new->pid = pid;
215 	new->fd = fd;
216 	new->next = NULL;
217 
218 	if (head == NULL)
219 		head = new;
220 	else
221 		prev->next = new;
222 
223 	return (0);
224 }
225 
226 /*
227  * _insert() and _delete() are used by p2open() in libgen.
228  */
229 int
230 _insert(pid_t pid, int fd)
231 {
232 	int rc;
233 
234 	lmutex_lock(&popen_lock);
235 	rc = _insert_nolock(pid, fd);
236 	lmutex_unlock(&popen_lock);
237 
238 	return (rc);
239 }
240 
241 
242 pid_t
243 _delete(int fd)
244 {
245 	node_t	*prev;
246 	node_t	*curr;
247 	pid_t	pid;
248 
249 	lmutex_lock(&popen_lock);
250 
251 	for (prev = curr = head; curr != NULL; curr = curr->next) {
252 		if (curr->fd == fd) {
253 			if (curr == head)
254 				head = curr->next;
255 			else
256 				prev->next = curr->next;
257 
258 			pid = curr->pid;
259 			lfree(curr, sizeof (node_t));
260 			lmutex_unlock(&popen_lock);
261 			return (pid);
262 		}
263 		prev = curr;
264 	}
265 
266 	lmutex_unlock(&popen_lock);
267 
268 	return (-1);
269 }
270