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