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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* Copyright (c) 1988 AT&T */ 30 /* All Rights Reserved */ 31 32 33 #pragma weak pclose = _pclose 34 #pragma weak popen = _popen 35 36 #include "synonyms.h" 37 #include "mtlib.h" 38 #include "file64.h" 39 #include <sys/types.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <wait.h> 43 #include <signal.h> 44 #include <fcntl.h> 45 #include <unistd.h> 46 #include <errno.h> 47 #include <thread.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 #ifndef _LP64 59 #define MAX_FD (1 << (NBBY * (unsigned)sizeof (_lastbuf->_file))) /* now 256 */ 60 #endif /* _LP64 */ 61 62 static int _insert_nolock(pid_t, int); 63 64 extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ 65 extern const char **environ; 66 67 static mutex_t popen_lock = DEFAULTMUTEX; 68 69 typedef struct node { 70 pid_t pid; 71 int fd; 72 struct node *next; 73 } node_t; 74 75 static node_t *head = NULL; 76 77 78 FILE * 79 popen(const char *cmd, const char *mode) 80 { 81 int p[2]; 82 pid_t pid; 83 int myside, yourside; 84 const char *shpath; 85 FILE *iop; 86 int stdio; 87 node_t *curr; 88 char *argvec[4]; 89 posix_spawn_file_actions_t fact; 90 int error; 91 static const char *sun_path = "/bin/sh"; 92 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 93 static const char *shell = "sh"; 94 static const char *sh_flg = "-c"; 95 96 if (pipe(p) < 0) 97 return (NULL); 98 99 #ifndef _LP64 100 /* check that the fd's are in range for a struct FILE */ 101 if ((p[WTR] >= MAX_FD) || (p[RDR] >= MAX_FD)) { 102 (void) close(p[WTR]); 103 (void) close(p[RDR]); 104 errno = EMFILE; 105 return (NULL); 106 } 107 #endif /* _LP64 */ 108 109 shpath = __xpg4? xpg4_path : sun_path; 110 if (access(shpath, X_OK)) /* XPG4 Requirement: */ 111 shpath = ""; /* force child to fail immediately */ 112 113 myside = tst(p[WTR], p[RDR]); 114 yourside = tst(p[RDR], p[WTR]); 115 /* myside and yourside reverse roles in child */ 116 stdio = tst(0, 1); 117 118 lmutex_lock(&popen_lock); 119 120 /* in the child, close all pipes from other popen's */ 121 if ((error = posix_spawn_file_actions_init(&fact)) != 0) { 122 lmutex_unlock(&popen_lock); 123 (void) close(myside); 124 (void) close(yourside); 125 errno = error; 126 return (NULL); 127 } 128 for (curr = head; curr != NULL && error == 0; curr = curr->next) 129 error = posix_spawn_file_actions_addclose(&fact, curr->fd); 130 if (error == 0) 131 error = posix_spawn_file_actions_addclose(&fact, myside); 132 if (yourside != stdio) { 133 if (error == 0) 134 error = posix_spawn_file_actions_adddup2(&fact, 135 yourside, stdio); 136 if (error == 0) 137 error = posix_spawn_file_actions_addclose(&fact, 138 yourside); 139 } 140 if (error) { 141 lmutex_unlock(&popen_lock); 142 (void) posix_spawn_file_actions_destroy(&fact); 143 (void) close(myside); 144 (void) close(yourside); 145 errno = error; 146 return (NULL); 147 } 148 argvec[0] = (char *)shell; 149 argvec[1] = (char *)sh_flg; 150 argvec[2] = (char *)cmd; 151 argvec[3] = NULL; 152 error = posix_spawn(&pid, shpath, &fact, NULL, 153 (char *const *)argvec, (char *const *)environ); 154 (void) posix_spawn_file_actions_destroy(&fact); 155 156 (void) close(yourside); 157 if ((errno = error) != 0 || _insert_nolock(pid, myside) == -1) { 158 lmutex_unlock(&popen_lock); 159 (void) close(myside); 160 return (NULL); 161 } 162 163 lmutex_unlock(&popen_lock); 164 165 if ((iop = fdopen(myside, mode)) == NULL) { 166 (void) _delete(myside); 167 (void) close(myside); 168 return (NULL); 169 } 170 _SET_ORIENTATION_BYTE(iop); 171 172 return (iop); 173 } 174 175 int 176 pclose(FILE *ptr) 177 { 178 pid_t pid; 179 int status; 180 181 pid = _delete(fileno(ptr)); 182 183 /* mark this pipe closed */ 184 (void) fclose(ptr); 185 186 if (pid == -1) 187 return (-1); 188 189 while (waitpid(pid, &status, 0) < 0) { 190 /* If waitpid fails with EINTR, restart the waitpid call */ 191 if (errno != EINTR) { 192 status = -1; 193 break; 194 } 195 } 196 197 return (status); 198 } 199 200 201 static int 202 _insert_nolock(pid_t pid, int fd) 203 { 204 node_t *prev; 205 node_t *curr; 206 node_t *new; 207 208 for (prev = curr = head; curr != NULL; curr = curr->next) 209 prev = curr; 210 211 if ((new = lmalloc(sizeof (node_t))) == NULL) 212 return (-1); 213 214 new->pid = pid; 215 new->fd = fd; 216 new->next = NULL; 217 218 if (head == NULL) 219 head = new; 220 else 221 prev->next = new; 222 223 return (0); 224 } 225 226 /* 227 * _insert() and _delete() are used by p2open() in libgen. 228 */ 229 int 230 _insert(pid_t pid, int fd) 231 { 232 int rc; 233 234 lmutex_lock(&popen_lock); 235 rc = _insert_nolock(pid, fd); 236 lmutex_unlock(&popen_lock); 237 238 return (rc); 239 } 240 241 242 pid_t 243 _delete(int fd) 244 { 245 node_t *prev; 246 node_t *curr; 247 pid_t pid; 248 249 lmutex_lock(&popen_lock); 250 251 for (prev = curr = head; curr != NULL; curr = curr->next) { 252 if (curr->fd == fd) { 253 if (curr == head) 254 head = curr->next; 255 else 256 prev->next = curr->next; 257 258 pid = curr->pid; 259 lfree(curr, sizeof (node_t)); 260 lmutex_unlock(&popen_lock); 261 return (pid); 262 } 263 prev = curr; 264 } 265 266 lmutex_unlock(&popen_lock); 267 268 return (-1); 269 } 270