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