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 2006 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 "synonyms.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 (void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL); 84 (void) thr_create(NULL, 0, reapchild, arg, THR_DAEMON, NULL); 85 } 86 87 FILE * 88 popen(const char *cmd, const char *mode) 89 { 90 int p[2]; 91 pid_t pid; 92 int myside; 93 int yourside; 94 int fd; 95 const char *shpath; 96 FILE *iop; 97 int stdio; 98 node_t *curr; 99 char *argvec[4]; 100 node_t *node; 101 posix_spawnattr_t attr; 102 posix_spawn_file_actions_t fact; 103 int error; 104 static const char *sun_path = "/bin/sh"; 105 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 106 static const char *shell = "sh"; 107 static const char *sh_flg = "-c"; 108 109 if ((node = lmalloc(sizeof (node_t))) == NULL) 110 return (NULL); 111 if ((error = posix_spawnattr_init(&attr)) != 0) { 112 lfree(node, sizeof (node_t)); 113 errno = error; 114 return (NULL); 115 } 116 if ((error = posix_spawn_file_actions_init(&fact)) != 0) { 117 lfree(node, sizeof (node_t)); 118 (void) posix_spawnattr_destroy(&attr); 119 errno = error; 120 return (NULL); 121 } 122 if (pipe(p) < 0) { 123 error = errno; 124 lfree(node, sizeof (node_t)); 125 (void) posix_spawnattr_destroy(&attr); 126 (void) posix_spawn_file_actions_destroy(&fact); 127 errno = error; 128 return (NULL); 129 } 130 131 shpath = __xpg4? xpg4_path : sun_path; 132 if (access(shpath, X_OK)) /* XPG4 Requirement: */ 133 shpath = ""; /* force child to fail immediately */ 134 135 myside = tst(p[WTR], p[RDR]); 136 yourside = tst(p[RDR], p[WTR]); 137 /* myside and yourside reverse roles in child */ 138 stdio = tst(0, 1); 139 140 /* This will fail more quickly if we run out of fds */ 141 if ((iop = fdopen(myside, mode)) == NULL) { 142 error = errno; 143 lfree(node, sizeof (node_t)); 144 (void) posix_spawnattr_destroy(&attr); 145 (void) posix_spawn_file_actions_destroy(&fact); 146 (void) close(yourside); 147 (void) close(myside); 148 errno = error; 149 return (NULL); 150 } 151 152 lmutex_lock(&popen_lock); 153 154 /* in the child, close all pipes from other popen's */ 155 for (curr = head; curr != NULL && error == 0; curr = curr->next) { 156 /* 157 * These conditions may apply if a previous iob returned 158 * by popen() was closed with fclose() rather than pclose(), 159 * or if close(fileno(iob)) was called. 160 * Accommodate these programming error. 161 */ 162 if ((fd = curr->fd) != myside && fd != yourside && 163 fcntl(fd, F_GETFD) >= 0) 164 error = posix_spawn_file_actions_addclose(&fact, fd); 165 } 166 if (error == 0) 167 error = posix_spawn_file_actions_addclose(&fact, myside); 168 if (yourside != stdio) { 169 if (error == 0) 170 error = posix_spawn_file_actions_adddup2(&fact, 171 yourside, stdio); 172 if (error == 0) 173 error = posix_spawn_file_actions_addclose(&fact, 174 yourside); 175 } 176 if (error == 0) 177 error = posix_spawnattr_setflags(&attr, 178 POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP); 179 if (error) { 180 lmutex_unlock(&popen_lock); 181 lfree(node, sizeof (node_t)); 182 (void) posix_spawnattr_destroy(&attr); 183 (void) posix_spawn_file_actions_destroy(&fact); 184 (void) fclose(iop); 185 (void) close(yourside); 186 errno = error; 187 return (NULL); 188 } 189 argvec[0] = (char *)shell; 190 argvec[1] = (char *)sh_flg; 191 argvec[2] = (char *)cmd; 192 argvec[3] = NULL; 193 error = posix_spawn(&pid, shpath, &fact, &attr, 194 (char *const *)argvec, (char *const *)environ); 195 (void) posix_spawnattr_destroy(&attr); 196 (void) posix_spawn_file_actions_destroy(&fact); 197 (void) close(yourside); 198 if (error) { 199 lmutex_unlock(&popen_lock); 200 lfree(node, sizeof (node_t)); 201 (void) fclose(iop); 202 errno = error; 203 return (NULL); 204 } 205 _insert_nolock(pid, myside, node); 206 207 lmutex_unlock(&popen_lock); 208 209 _SET_ORIENTATION_BYTE(iop); 210 211 return (iop); 212 } 213 214 int 215 pclose(FILE *ptr) 216 { 217 pid_t pid; 218 int status; 219 220 pid = _delete(fileno(ptr)); 221 222 /* mark this pipe closed */ 223 (void) fclose(ptr); 224 225 if (pid <= 0) { 226 errno = ECHILD; 227 return (-1); 228 } 229 230 /* 231 * pclose() is a cancellation point. 232 * Call waitpid_cancel() rather than _waitpid() to make 233 * sure that we actually perform the cancellation logic. 234 * 235 * If we have already been cancelled (pclose() was called from 236 * a cancellation cleanup handler), attempt to reap the process 237 * w/o waiting, and if that fails just call cleanup(pid). 238 */ 239 240 if (_thrp_cancelled()) { 241 if (waitpid(pid, &status, WNOHANG) == pid) 242 return (status); 243 cleanup((void *)(uintptr_t)pid); 244 errno = ECHILD; 245 return (-1); 246 } 247 248 pthread_cleanup_push(cleanup, (void *)(uintptr_t)pid); 249 while (waitpid_cancel(pid, &status, 0) < 0) { 250 if (errno != EINTR) { 251 status = -1; 252 break; 253 } 254 } 255 pthread_cleanup_pop(0); 256 257 return (status); 258 } 259 260 261 static void 262 _insert_nolock(pid_t pid, int fd, node_t *new) 263 { 264 node_t *prev; 265 node_t *curr; 266 267 for (prev = curr = head; curr != NULL; curr = curr->next) { 268 /* 269 * curr->fd can equal fd if a previous iob returned by 270 * popen() was closed with fclose() rather than pclose(), 271 * or if close(fileno(iob)) was called. 272 * Accommodate this programming error. 273 */ 274 if (curr->fd == fd) { 275 (void) waitpid(curr->pid, NULL, WNOHANG); 276 curr->pid = pid; 277 lfree(new, sizeof (node_t)); 278 return; 279 } 280 prev = curr; 281 } 282 283 new->pid = pid; 284 new->fd = fd; 285 new->next = NULL; 286 287 if (head == NULL) 288 head = new; 289 else 290 prev->next = new; 291 } 292 293 /* 294 * _insert() and _delete() are used by p2open() in libgen. 295 */ 296 int 297 _insert(pid_t pid, int fd) 298 { 299 node_t *node; 300 301 if ((node = lmalloc(sizeof (node_t))) == NULL) 302 return (-1); 303 304 lmutex_lock(&popen_lock); 305 _insert_nolock(pid, fd, node); 306 lmutex_unlock(&popen_lock); 307 308 return (0); 309 } 310 311 312 pid_t 313 _delete(int fd) 314 { 315 node_t *prev; 316 node_t *curr; 317 pid_t pid; 318 319 lmutex_lock(&popen_lock); 320 321 for (prev = curr = head; curr != NULL; curr = curr->next) { 322 if (curr->fd == fd) { 323 if (curr == head) 324 head = curr->next; 325 else 326 prev->next = curr->next; 327 lmutex_unlock(&popen_lock); 328 pid = curr->pid; 329 lfree(curr, sizeof (node_t)); 330 return (pid); 331 } 332 prev = curr; 333 } 334 335 lmutex_unlock(&popen_lock); 336 337 return (-1); 338 } 339