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 /*
28 * Copyright (c) 2011 by Delphix. All rights reserved.
29 */
30
31 /* Copyright (c) 1988 AT&T */
32 /* All Rights Reserved */
33
34 #pragma weak _pclose = pclose
35 #pragma weak _popen = popen
36
37 #include "lint.h"
38 #include "mtlib.h"
39 #include "file64.h"
40 #include <sys/types.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <wait.h>
44 #include <signal.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <errno.h>
48 #include <thread.h>
49 #include <pthread.h>
50 #include <synch.h>
51 #include <spawn.h>
52 #include <paths.h>
53 #include "stdiom.h"
54 #include "mse.h"
55 #include "libc.h"
56
57 static mutex_t popen_lock = DEFAULTMUTEX;
58
59 typedef struct node {
60 pid_t pid;
61 int fd;
62 struct node *next;
63 } node_t;
64
65 static node_t *head = NULL;
66 static void _insert_nolock(pid_t, int, node_t *);
67
68 /*
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.
72 */
73 static void
cleanup(void * arg)74 cleanup(void *arg)
75 {
76 extern const sigset_t maskset;
77 extern void *reapchild(void *); /* see port/stdio/system.c */
78
79 /*
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.
83 */
84 (void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL);
85 (void) thr_create(NULL, 0, reapchild, arg, THR_DAEMON, NULL);
86 }
87
88 FILE *
popen(const char * cmd,const char * mode)89 popen(const char *cmd, const char *mode)
90 {
91 pid_t pid;
92 int myfd, fd;
93 const char *shpath = _PATH_BSHELL;
94 FILE *iop;
95 node_t *curr;
96 node_t *node;
97 posix_spawn_file_actions_t fact;
98 posix_spawnattr_t attr;
99 int error;
100
101 if ((node = lmalloc(sizeof (node_t))) == NULL)
102 return (NULL);
103 if ((error = posix_spawnattr_init(&attr)) != 0) {
104 lfree(node, sizeof (node_t));
105 errno = error;
106 return (NULL);
107 }
108 if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
109 lfree(node, sizeof (node_t));
110 (void) posix_spawnattr_destroy(&attr);
111 errno = error;
112 return (NULL);
113 }
114
115 if (access(shpath, X_OK)) /* XPG4 Requirement: */
116 shpath = ""; /* force child to fail immediately */
117
118
119 /*
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.
124 */
125 myfd = open("/dev/null", O_RDWR);
126 if (myfd == -1) {
127 error = errno;
128 lfree(node, sizeof (node_t));
129 (void) posix_spawnattr_destroy(&attr);
130 (void) posix_spawn_file_actions_destroy(&fact);
131 errno = error;
132 return (NULL);
133 }
134 iop = fdopen(myfd, mode);
135 if (iop == NULL) {
136 error = errno;
137 lfree(node, sizeof (node_t));
138 (void) posix_spawnattr_destroy(&attr);
139 (void) posix_spawn_file_actions_destroy(&fact);
140 (void) close(myfd);
141 errno = error;
142 return (NULL);
143 }
144
145 lmutex_lock(&popen_lock);
146
147 /* in the child, close all pipes from other popen's */
148 for (curr = head; curr != NULL && error == 0; curr = curr->next) {
149 /*
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.
155 */
156 if (fcntl(curr->fd, F_GETFD) >= 0) {
157 error = posix_spawn_file_actions_addclose(&fact,
158 curr->fd);
159 }
160 }
161 /*
162 * See the comments in port/stdio/system.c for why these
163 * non-portable posix_spawn() attributes are being used.
164 */
165 if (error == 0) {
166 error = posix_spawnattr_setflags(&attr,
167 POSIX_SPAWN_NOSIGCHLD_NP |
168 POSIX_SPAWN_WAITPID_NP |
169 POSIX_SPAWN_NOEXECERR_NP);
170 }
171 if (error != 0) {
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);
176 (void) fclose(iop);
177 errno = error;
178 return (NULL);
179 }
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);
183 if (error != 0) {
184 lmutex_unlock(&popen_lock);
185 lfree(node, sizeof (node_t));
186 (void) fclose(iop);
187 errno = error;
188 return (NULL);
189 }
190 _insert_nolock(pid, myfd, node);
191
192 lmutex_unlock(&popen_lock);
193
194 /*
195 * myfd is the one that we fdopen()'ed; make it refer to the
196 * pipe to the child.
197 */
198 (void) dup2(fd, myfd);
199 (void) close(fd);
200
201 _SET_ORIENTATION_BYTE(iop);
202
203 return (iop);
204 }
205
206 /*
207 * pclose() is a cancellation point.
208 */
209 int
pclose(FILE * ptr)210 pclose(FILE *ptr)
211 {
212 pid_t pid;
213 int status;
214
215 pid = _delete(fileno(ptr));
216
217 /* mark this pipe closed */
218 (void) fclose(ptr);
219
220 if (pid <= 0) {
221 errno = ECHILD;
222 return (-1);
223 }
224
225 /*
226 * waitpid() is a cancellation point.
227 * This causes pclose() to be a cancellation point.
228 *
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).
232 */
233
234 if (_thrp_cancelled()) {
235 /* waitpid(..., WNOHANG) is not a cancellation point */
236 if (waitpid(pid, &status, WNOHANG) == pid)
237 return (status);
238 cleanup((void *)(uintptr_t)pid);
239 errno = ECHILD;
240 return (-1);
241 }
242
243 pthread_cleanup_push(cleanup, (void *)(uintptr_t)pid);
244 while (waitpid(pid, &status, 0) < 0) {
245 if (errno != EINTR) {
246 status = -1;
247 break;
248 }
249 }
250 pthread_cleanup_pop(0);
251
252 return (status);
253 }
254
255
256 static void
_insert_nolock(pid_t pid,int fd,node_t * new)257 _insert_nolock(pid_t pid, int fd, node_t *new)
258 {
259 node_t *prev;
260 node_t *curr;
261
262 for (prev = curr = head; curr != NULL; curr = curr->next) {
263 /*
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.
268 */
269 if (curr->fd == fd) {
270 /* make a lame attempt to reap the forgotten child */
271 (void) waitpid(curr->pid, NULL, WNOHANG);
272 curr->pid = pid;
273 lfree(new, sizeof (node_t));
274 return;
275 }
276 prev = curr;
277 }
278
279 new->pid = pid;
280 new->fd = fd;
281 new->next = NULL;
282
283 if (head == NULL)
284 head = new;
285 else
286 prev->next = new;
287 }
288
289 /*
290 * _insert() and _delete() are used by p2open() in libgen.
291 */
292 int
_insert(pid_t pid,int fd)293 _insert(pid_t pid, int fd)
294 {
295 node_t *node;
296
297 if ((node = lmalloc(sizeof (node_t))) == NULL)
298 return (-1);
299
300 lmutex_lock(&popen_lock);
301 _insert_nolock(pid, fd, node);
302 lmutex_unlock(&popen_lock);
303
304 return (0);
305 }
306
307
308 pid_t
_delete(int fd)309 _delete(int fd)
310 {
311 node_t *prev;
312 node_t *curr;
313 pid_t pid;
314
315 lmutex_lock(&popen_lock);
316
317 for (prev = curr = head; curr != NULL; curr = curr->next) {
318 if (curr->fd == fd) {
319 if (curr == head)
320 head = curr->next;
321 else
322 prev->next = curr->next;
323 lmutex_unlock(&popen_lock);
324 pid = curr->pid;
325 lfree(curr, sizeof (node_t));
326 return (pid);
327 }
328 prev = curr;
329 }
330
331 lmutex_unlock(&popen_lock);
332
333 return (-1);
334 }
335