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 2008 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 weak _pclose = pclose
31 #pragma weak _popen = popen
32
33 #include "lint.h"
34 #include "mtlib.h"
35 #include "file64.h"
36 #include <sys/types.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <wait.h>
40 #include <signal.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <errno.h>
44 #include <thread.h>
45 #include <pthread.h>
46 #include <synch.h>
47 #include <spawn.h>
48 #include "stdiom.h"
49 #include "mse.h"
50 #include "libc.h"
51
52 #define tst(a, b) (*mode == 'r'? (b) : (a))
53 #define RDR 0
54 #define WTR 1
55
56 extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */
57 extern const char **_environ;
58
59 static mutex_t popen_lock = DEFAULTMUTEX;
60
61 typedef struct node {
62 pid_t pid;
63 int fd;
64 struct node *next;
65 } node_t;
66
67 static node_t *head = NULL;
68 static void _insert_nolock(pid_t, int, node_t *);
69
70 /*
71 * Cancellation cleanup handler.
72 * If we were cancelled in waitpid(), create a daemon thread to
73 * reap our abandoned child. No other thread can do this for us.
74 */
75 static void
cleanup(void * arg)76 cleanup(void *arg)
77 {
78 extern const sigset_t maskset;
79 extern void *reapchild(void *); /* see port/stdio/system.c */
80
81 /*
82 * We have been cancelled. There is no need to restore
83 * the original sigmask after blocking all signals because
84 * pthread_exit() will block all signals while we exit.
85 */
86 (void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL);
87 (void) thr_create(NULL, 0, reapchild, arg, THR_DAEMON, NULL);
88 }
89
90 FILE *
popen(const char * cmd,const char * mode)91 popen(const char *cmd, const char *mode)
92 {
93 int p[2];
94 pid_t pid;
95 int myside;
96 int yourside;
97 int fd;
98 const char *shpath;
99 FILE *iop;
100 int stdio;
101 node_t *curr;
102 char *argvec[4];
103 node_t *node;
104 posix_spawnattr_t attr;
105 posix_spawn_file_actions_t fact;
106 int error;
107 static const char *sun_path = "/bin/sh";
108 static const char *xpg4_path = "/usr/xpg4/bin/sh";
109 static const char *shell = "sh";
110 static const char *sh_flg = "-c";
111
112 if ((node = lmalloc(sizeof (node_t))) == NULL)
113 return (NULL);
114 if ((error = posix_spawnattr_init(&attr)) != 0) {
115 lfree(node, sizeof (node_t));
116 errno = error;
117 return (NULL);
118 }
119 if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
120 lfree(node, sizeof (node_t));
121 (void) posix_spawnattr_destroy(&attr);
122 errno = error;
123 return (NULL);
124 }
125 if (pipe(p) < 0) {
126 error = errno;
127 lfree(node, sizeof (node_t));
128 (void) posix_spawnattr_destroy(&attr);
129 (void) posix_spawn_file_actions_destroy(&fact);
130 errno = error;
131 return (NULL);
132 }
133
134 shpath = __xpg4? xpg4_path : sun_path;
135 if (access(shpath, X_OK)) /* XPG4 Requirement: */
136 shpath = ""; /* force child to fail immediately */
137
138 myside = tst(p[WTR], p[RDR]);
139 yourside = tst(p[RDR], p[WTR]);
140 /* myside and yourside reverse roles in child */
141 stdio = tst(0, 1);
142
143 /* This will fail more quickly if we run out of fds */
144 if ((iop = fdopen(myside, mode)) == NULL) {
145 error = errno;
146 lfree(node, sizeof (node_t));
147 (void) posix_spawnattr_destroy(&attr);
148 (void) posix_spawn_file_actions_destroy(&fact);
149 (void) close(yourside);
150 (void) close(myside);
151 errno = error;
152 return (NULL);
153 }
154
155 lmutex_lock(&popen_lock);
156
157 /* in the child, close all pipes from other popen's */
158 for (curr = head; curr != NULL && error == 0; curr = curr->next) {
159 /*
160 * These conditions may apply if a previous iob returned
161 * by popen() was closed with fclose() rather than pclose(),
162 * or if close(fileno(iob)) was called. Don't let these
163 * programming errors cause us to malfunction here.
164 */
165 if ((fd = curr->fd) != myside && fd != yourside &&
166 fcntl(fd, F_GETFD) >= 0)
167 error = posix_spawn_file_actions_addclose(&fact, fd);
168 }
169 if (error == 0)
170 error = posix_spawn_file_actions_addclose(&fact, myside);
171 if (yourside != stdio) {
172 if (error == 0)
173 error = posix_spawn_file_actions_adddup2(&fact,
174 yourside, stdio);
175 if (error == 0)
176 error = posix_spawn_file_actions_addclose(&fact,
177 yourside);
178 }
179 /*
180 * See the comments in port/stdio/system.c for why these
181 * non-portable posix_spawn() attributes are being used.
182 */
183 if (error == 0)
184 error = posix_spawnattr_setflags(&attr,
185 POSIX_SPAWN_NOSIGCHLD_NP |
186 POSIX_SPAWN_WAITPID_NP |
187 POSIX_SPAWN_NOEXECERR_NP);
188 if (error) {
189 lmutex_unlock(&popen_lock);
190 lfree(node, sizeof (node_t));
191 (void) posix_spawnattr_destroy(&attr);
192 (void) posix_spawn_file_actions_destroy(&fact);
193 (void) fclose(iop);
194 (void) close(yourside);
195 errno = error;
196 return (NULL);
197 }
198 argvec[0] = (char *)shell;
199 argvec[1] = (char *)sh_flg;
200 argvec[2] = (char *)cmd;
201 argvec[3] = NULL;
202 error = posix_spawn(&pid, shpath, &fact, &attr,
203 (char *const *)argvec, (char *const *)_environ);
204 (void) posix_spawnattr_destroy(&attr);
205 (void) posix_spawn_file_actions_destroy(&fact);
206 (void) close(yourside);
207 if (error) {
208 lmutex_unlock(&popen_lock);
209 lfree(node, sizeof (node_t));
210 (void) fclose(iop);
211 errno = error;
212 return (NULL);
213 }
214 _insert_nolock(pid, myside, node);
215
216 lmutex_unlock(&popen_lock);
217
218 _SET_ORIENTATION_BYTE(iop);
219
220 return (iop);
221 }
222
223 /*
224 * pclose() is a cancellation point.
225 */
226 int
pclose(FILE * ptr)227 pclose(FILE *ptr)
228 {
229 pid_t pid;
230 int status;
231
232 pid = _delete(fileno(ptr));
233
234 /* mark this pipe closed */
235 (void) fclose(ptr);
236
237 if (pid <= 0) {
238 errno = ECHILD;
239 return (-1);
240 }
241
242 /*
243 * waitpid() is a cancellation point.
244 * This causes pclose() to be a cancellation point.
245 *
246 * If we have already been cancelled (pclose() was called from
247 * a cancellation cleanup handler), attempt to reap the process
248 * w/o waiting, and if that fails just call cleanup(pid).
249 */
250
251 if (_thrp_cancelled()) {
252 /* waitpid(..., WNOHANG) is not a cancellation point */
253 if (waitpid(pid, &status, WNOHANG) == pid)
254 return (status);
255 cleanup((void *)(uintptr_t)pid);
256 errno = ECHILD;
257 return (-1);
258 }
259
260 pthread_cleanup_push(cleanup, (void *)(uintptr_t)pid);
261 while (waitpid(pid, &status, 0) < 0) {
262 if (errno != EINTR) {
263 status = -1;
264 break;
265 }
266 }
267 pthread_cleanup_pop(0);
268
269 return (status);
270 }
271
272
273 static void
_insert_nolock(pid_t pid,int fd,node_t * new)274 _insert_nolock(pid_t pid, int fd, node_t *new)
275 {
276 node_t *prev;
277 node_t *curr;
278
279 for (prev = curr = head; curr != NULL; curr = curr->next) {
280 /*
281 * curr->fd can equal fd if a previous iob returned by
282 * popen() was closed with fclose() rather than pclose(),
283 * or if close(fileno(iob)) was called. Don't let these
284 * programming errors cause us to malfunction here.
285 */
286 if (curr->fd == fd) {
287 /* make a lame attempt to reap the forgotten child */
288 (void) waitpid(curr->pid, NULL, WNOHANG);
289 curr->pid = pid;
290 lfree(new, sizeof (node_t));
291 return;
292 }
293 prev = curr;
294 }
295
296 new->pid = pid;
297 new->fd = fd;
298 new->next = NULL;
299
300 if (head == NULL)
301 head = new;
302 else
303 prev->next = new;
304 }
305
306 /*
307 * _insert() and _delete() are used by p2open() in libgen.
308 */
309 int
_insert(pid_t pid,int fd)310 _insert(pid_t pid, int fd)
311 {
312 node_t *node;
313
314 if ((node = lmalloc(sizeof (node_t))) == NULL)
315 return (-1);
316
317 lmutex_lock(&popen_lock);
318 _insert_nolock(pid, fd, node);
319 lmutex_unlock(&popen_lock);
320
321 return (0);
322 }
323
324
325 pid_t
_delete(int fd)326 _delete(int fd)
327 {
328 node_t *prev;
329 node_t *curr;
330 pid_t pid;
331
332 lmutex_lock(&popen_lock);
333
334 for (prev = curr = head; curr != NULL; curr = curr->next) {
335 if (curr->fd == fd) {
336 if (curr == head)
337 head = curr->next;
338 else
339 prev->next = curr->next;
340 lmutex_unlock(&popen_lock);
341 pid = curr->pid;
342 lfree(curr, sizeof (node_t));
343 return (pid);
344 }
345 prev = curr;
346 }
347
348 lmutex_unlock(&popen_lock);
349
350 return (-1);
351 }
352