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 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 * 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 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 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 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 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