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