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 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 static int _insert_nolock(pid_t, int); 59 60 extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ 61 extern const char **environ; 62 63 static mutex_t popen_lock = DEFAULTMUTEX; 64 65 typedef struct node { 66 pid_t pid; 67 int fd; 68 struct node *next; 69 } node_t; 70 71 static node_t *head = NULL; 72 73 74 FILE * 75 popen(const char *cmd, const char *mode) 76 { 77 int p[2]; 78 pid_t pid; 79 int myside, yourside; 80 const char *shpath; 81 FILE *iop; 82 int stdio; 83 node_t *curr; 84 char *argvec[4]; 85 posix_spawn_file_actions_t fact; 86 int error; 87 static const char *sun_path = "/bin/sh"; 88 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 89 static const char *shell = "sh"; 90 static const char *sh_flg = "-c"; 91 92 if (pipe(p) < 0) 93 return (NULL); 94 95 shpath = __xpg4? xpg4_path : sun_path; 96 if (access(shpath, X_OK)) /* XPG4 Requirement: */ 97 shpath = ""; /* force child to fail immediately */ 98 99 myside = tst(p[WTR], p[RDR]); 100 yourside = tst(p[RDR], p[WTR]); 101 /* myside and yourside reverse roles in child */ 102 stdio = tst(0, 1); 103 104 /* This will fail more quickly if we run out of fds */ 105 if ((iop = fdopen(myside, mode)) == NULL) { 106 (void) close(yourside); 107 (void) close(myside); 108 return (NULL); 109 } 110 111 lmutex_lock(&popen_lock); 112 113 /* in the child, close all pipes from other popen's */ 114 if ((error = posix_spawn_file_actions_init(&fact)) != 0) { 115 lmutex_unlock(&popen_lock); 116 (void) fclose(iop); 117 (void) close(yourside); 118 errno = error; 119 return (NULL); 120 } 121 for (curr = head; curr != NULL && error == 0; curr = curr->next) 122 error = posix_spawn_file_actions_addclose(&fact, curr->fd); 123 if (error == 0) 124 error = posix_spawn_file_actions_addclose(&fact, myside); 125 if (yourside != stdio) { 126 if (error == 0) 127 error = posix_spawn_file_actions_adddup2(&fact, 128 yourside, stdio); 129 if (error == 0) 130 error = posix_spawn_file_actions_addclose(&fact, 131 yourside); 132 } 133 if (error) { 134 lmutex_unlock(&popen_lock); 135 (void) posix_spawn_file_actions_destroy(&fact); 136 (void) fclose(iop); 137 (void) close(yourside); 138 errno = error; 139 return (NULL); 140 } 141 argvec[0] = (char *)shell; 142 argvec[1] = (char *)sh_flg; 143 argvec[2] = (char *)cmd; 144 argvec[3] = NULL; 145 error = posix_spawn(&pid, shpath, &fact, NULL, 146 (char *const *)argvec, (char *const *)environ); 147 (void) posix_spawn_file_actions_destroy(&fact); 148 149 (void) close(yourside); 150 if ((errno = error) != 0 || _insert_nolock(pid, myside) == -1) { 151 lmutex_unlock(&popen_lock); 152 (void) fclose(iop); 153 return (NULL); 154 } 155 156 lmutex_unlock(&popen_lock); 157 158 _SET_ORIENTATION_BYTE(iop); 159 160 return (iop); 161 } 162 163 int 164 pclose(FILE *ptr) 165 { 166 pid_t pid; 167 int status; 168 169 pid = _delete(fileno(ptr)); 170 171 /* mark this pipe closed */ 172 (void) fclose(ptr); 173 174 if (pid == -1) 175 return (-1); 176 177 while (waitpid(pid, &status, 0) < 0) { 178 /* If waitpid fails with EINTR, restart the waitpid call */ 179 if (errno != EINTR) { 180 status = -1; 181 break; 182 } 183 } 184 185 return (status); 186 } 187 188 189 static int 190 _insert_nolock(pid_t pid, int fd) 191 { 192 node_t *prev; 193 node_t *curr; 194 node_t *new; 195 196 for (prev = curr = head; curr != NULL; curr = curr->next) 197 prev = curr; 198 199 if ((new = lmalloc(sizeof (node_t))) == NULL) 200 return (-1); 201 202 new->pid = pid; 203 new->fd = fd; 204 new->next = NULL; 205 206 if (head == NULL) 207 head = new; 208 else 209 prev->next = new; 210 211 return (0); 212 } 213 214 /* 215 * _insert() and _delete() are used by p2open() in libgen. 216 */ 217 int 218 _insert(pid_t pid, int fd) 219 { 220 int rc; 221 222 lmutex_lock(&popen_lock); 223 rc = _insert_nolock(pid, fd); 224 lmutex_unlock(&popen_lock); 225 226 return (rc); 227 } 228 229 230 pid_t 231 _delete(int fd) 232 { 233 node_t *prev; 234 node_t *curr; 235 pid_t pid; 236 237 lmutex_lock(&popen_lock); 238 239 for (prev = curr = head; curr != NULL; curr = curr->next) { 240 if (curr->fd == fd) { 241 if (curr == head) 242 head = curr->next; 243 else 244 prev->next = curr->next; 245 246 pid = curr->pid; 247 lfree(curr, sizeof (node_t)); 248 lmutex_unlock(&popen_lock); 249 return (pid); 250 } 251 prev = curr; 252 } 253 254 lmutex_unlock(&popen_lock); 255 256 return (-1); 257 } 258