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