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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Copyright (c) 2011 by Delphix. All rights reserved.
31 /* Copyright (c) 1988 AT&T */
32 /* All Rights Reserved */
34 #pragma weak _pclose = pclose
35 #pragma weak _popen = popen
40 #include <sys/types.h>
57 static mutex_t popen_lock
= DEFAULTMUTEX
;
65 static node_t
*head
= NULL
;
66 static void _insert_nolock(pid_t
, int, node_t
*);
69 * Cancellation cleanup handler.
70 * If we were cancelled in waitpid(), create a daemon thread to
71 * reap our abandoned child. No other thread can do this for us.
76 extern const sigset_t maskset
;
77 extern void *reapchild(void *); /* see port/stdio/system.c */
80 * We have been cancelled. There is no need to restore
81 * the original sigmask after blocking all signals because
82 * pthread_exit() will block all signals while we exit.
84 (void) thr_sigsetmask(SIG_SETMASK
, &maskset
, NULL
);
85 (void) thr_create(NULL
, 0, reapchild
, arg
, THR_DAEMON
, NULL
);
89 popen(const char *cmd
, const char *mode
)
93 const char *shpath
= _PATH_BSHELL
;
97 posix_spawn_file_actions_t fact
;
98 posix_spawnattr_t attr
;
101 if ((node
= lmalloc(sizeof (node_t
))) == NULL
)
103 if ((error
= posix_spawnattr_init(&attr
)) != 0) {
104 lfree(node
, sizeof (node_t
));
108 if ((error
= posix_spawn_file_actions_init(&fact
)) != 0) {
109 lfree(node
, sizeof (node_t
));
110 (void) posix_spawnattr_destroy(&attr
);
115 if (access(shpath
, X_OK
)) /* XPG4 Requirement: */
116 shpath
= ""; /* force child to fail immediately */
120 * fdopen() can fail (if the fd is too high or we are out of memory),
121 * but we don't want to have any way to fail after creating the child
122 * process. So we fdopen() a dummy fd (myfd), and once we get the real
123 * fd from posix_spawn_pipe_np(), we dup2() the real fd onto the dummy.
125 myfd
= open("/dev/null", O_RDWR
);
128 lfree(node
, sizeof (node_t
));
129 (void) posix_spawnattr_destroy(&attr
);
130 (void) posix_spawn_file_actions_destroy(&fact
);
134 iop
= fdopen(myfd
, mode
);
137 lfree(node
, sizeof (node_t
));
138 (void) posix_spawnattr_destroy(&attr
);
139 (void) posix_spawn_file_actions_destroy(&fact
);
145 lmutex_lock(&popen_lock
);
147 /* in the child, close all pipes from other popen's */
148 for (curr
= head
; curr
!= NULL
&& error
== 0; curr
= curr
->next
) {
150 * The fd may no longer be open if an iob previously returned
151 * by popen() was closed with fclose() rather than pclose(),
152 * or if close(fileno(iob)) was called. Use fcntl() to check
153 * if the fd is still open, so that these programming errors
154 * won't cause us to malfunction here.
156 if (fcntl(curr
->fd
, F_GETFD
) >= 0) {
157 error
= posix_spawn_file_actions_addclose(&fact
,
162 * See the comments in port/stdio/system.c for why these
163 * non-portable posix_spawn() attributes are being used.
166 error
= posix_spawnattr_setflags(&attr
,
167 POSIX_SPAWN_NOSIGCHLD_NP
|
168 POSIX_SPAWN_WAITPID_NP
|
169 POSIX_SPAWN_NOEXECERR_NP
);
172 lmutex_unlock(&popen_lock
);
173 lfree(node
, sizeof (node_t
));
174 (void) posix_spawnattr_destroy(&attr
);
175 (void) posix_spawn_file_actions_destroy(&fact
);
180 error
= posix_spawn_pipe_np(&pid
, &fd
, cmd
, *mode
!= 'r', &fact
, &attr
);
181 (void) posix_spawnattr_destroy(&attr
);
182 (void) posix_spawn_file_actions_destroy(&fact
);
184 lmutex_unlock(&popen_lock
);
185 lfree(node
, sizeof (node_t
));
190 _insert_nolock(pid
, myfd
, node
);
192 lmutex_unlock(&popen_lock
);
195 * myfd is the one that we fdopen()'ed; make it refer to the
198 (void) dup2(fd
, myfd
);
201 _SET_ORIENTATION_BYTE(iop
);
207 * pclose() is a cancellation point.
215 pid
= _delete(fileno(ptr
));
217 /* mark this pipe closed */
226 * waitpid() is a cancellation point.
227 * This causes pclose() to be a cancellation point.
229 * If we have already been cancelled (pclose() was called from
230 * a cancellation cleanup handler), attempt to reap the process
231 * w/o waiting, and if that fails just call cleanup(pid).
234 if (_thrp_cancelled()) {
235 /* waitpid(..., WNOHANG) is not a cancellation point */
236 if (waitpid(pid
, &status
, WNOHANG
) == pid
)
238 cleanup((void *)(uintptr_t)pid
);
243 pthread_cleanup_push(cleanup
, (void *)(uintptr_t)pid
);
244 while (waitpid(pid
, &status
, 0) < 0) {
245 if (errno
!= EINTR
) {
250 pthread_cleanup_pop(0);
257 _insert_nolock(pid_t pid
, int fd
, node_t
*new)
262 for (prev
= curr
= head
; curr
!= NULL
; curr
= curr
->next
) {
264 * curr->fd can equal fd if a previous iob returned by
265 * popen() was closed with fclose() rather than pclose(),
266 * or if close(fileno(iob)) was called. Don't let these
267 * programming errors cause us to malfunction here.
269 if (curr
->fd
== fd
) {
270 /* make a lame attempt to reap the forgotten child */
271 (void) waitpid(curr
->pid
, NULL
, WNOHANG
);
273 lfree(new, sizeof (node_t
));
290 * _insert() and _delete() are used by p2open() in libgen.
293 _insert(pid_t pid
, int fd
)
297 if ((node
= lmalloc(sizeof (node_t
))) == NULL
)
300 lmutex_lock(&popen_lock
);
301 _insert_nolock(pid
, fd
, node
);
302 lmutex_unlock(&popen_lock
);
315 lmutex_lock(&popen_lock
);
317 for (prev
= curr
= head
; curr
!= NULL
; curr
= curr
->next
) {
318 if (curr
->fd
== fd
) {
322 prev
->next
= curr
->next
;
323 lmutex_unlock(&popen_lock
);
325 lfree(curr
, sizeof (node_t
));
331 lmutex_unlock(&popen_lock
);