1 1.38 rillig /* $NetBSD: popen.c,v 1.38 2022/04/19 20:32:15 rillig Exp $ */ 2 1.10 cgd 3 1.1 cgd /* 4 1.7 jtc * Copyright (c) 1988, 1993 5 1.7 jtc * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software written by Ken Arnold and 8 1.1 cgd * published in UNIX Review, Vol. 6, No. 8. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.27 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.13 christos #include <sys/cdefs.h> 36 1.1 cgd #if defined(LIBC_SCCS) && !defined(lint) 37 1.10 cgd #if 0 38 1.16 perry static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 5/3/95"; 39 1.10 cgd #else 40 1.38 rillig __RCSID("$NetBSD: popen.c,v 1.38 2022/04/19 20:32:15 rillig Exp $"); 41 1.10 cgd #endif 42 1.1 cgd #endif /* LIBC_SCCS and not lint */ 43 1.1 cgd 44 1.14 jtc #include "namespace.h" 45 1.1 cgd #include <sys/param.h> 46 1.1 cgd #include <sys/wait.h> 47 1.16 perry #include <sys/socket.h> 48 1.7 jtc 49 1.23 lukem #include <assert.h> 50 1.23 lukem #include <errno.h> 51 1.23 lukem #include <paths.h> 52 1.7 jtc #include <signal.h> 53 1.1 cgd #include <stdio.h> 54 1.1 cgd #include <stdlib.h> 55 1.1 cgd #include <string.h> 56 1.23 lukem #include <unistd.h> 57 1.31 christos #include <fcntl.h> 58 1.30 tron 59 1.30 tron #include "env.h" 60 1.14 jtc 61 1.14 jtc #ifdef __weak_alias 62 1.25 mycroft __weak_alias(popen,_popen) 63 1.25 mycroft __weak_alias(pclose,_pclose) 64 1.14 jtc #endif 65 1.1 cgd 66 1.7 jtc static struct pid { 67 1.7 jtc struct pid *next; 68 1.7 jtc FILE *fp; 69 1.28 cl #ifdef _REENTRANT 70 1.28 cl int fd; 71 1.28 cl #endif 72 1.7 jtc pid_t pid; 73 1.7 jtc } *pidlist; 74 1.7 jtc 75 1.28 cl #ifdef _REENTRANT 76 1.36 christos static mutex_t pidlist_mutex = MUTEX_INITIALIZER; 77 1.36 christos # define MUTEX_LOCK() \ 78 1.36 christos do { \ 79 1.36 christos if (__isthreaded) \ 80 1.36 christos mutex_lock(&pidlist_mutex); \ 81 1.38 rillig } while (0) 82 1.36 christos # define MUTEX_UNLOCK() \ 83 1.36 christos do { \ 84 1.36 christos if (__isthreaded) \ 85 1.36 christos mutex_unlock(&pidlist_mutex); \ 86 1.38 rillig } while (0) 87 1.36 christos #else 88 1.36 christos # define MUTEX_LOCK() __nothing 89 1.36 christos # define MUTEX_UNLOCK() __nothing 90 1.28 cl #endif 91 1.28 cl 92 1.33 christos static struct pid * 93 1.33 christos pdes_get(int *pdes, const char **type) 94 1.1 cgd { 95 1.33 christos struct pid *cur; 96 1.33 christos int flags = strchr(*type, 'e') ? O_CLOEXEC : 0; 97 1.33 christos int serrno; 98 1.23 lukem 99 1.33 christos if (strchr(*type, '+')) { 100 1.31 christos int stype = flags ? (SOCK_STREAM | SOCK_CLOEXEC) : SOCK_STREAM; 101 1.33 christos *type = "r+"; 102 1.31 christos if (socketpair(AF_LOCAL, stype, 0, pdes) < 0) 103 1.31 christos return NULL; 104 1.16 perry } else { 105 1.33 christos *type = strrchr(*type, 'r') ? "r" : "w"; 106 1.31 christos if (pipe2(pdes, flags) == -1) 107 1.31 christos return NULL; 108 1.9 jtc } 109 1.1 cgd 110 1.33 christos if ((cur = malloc(sizeof(*cur))) != NULL) 111 1.33 christos return cur; 112 1.33 christos serrno = errno; 113 1.33 christos (void)close(pdes[0]); 114 1.33 christos (void)close(pdes[1]); 115 1.33 christos errno = serrno; 116 1.33 christos return NULL; 117 1.33 christos } 118 1.33 christos 119 1.33 christos static void 120 1.33 christos pdes_child(int *pdes, const char *type) 121 1.33 christos { 122 1.33 christos struct pid *old; 123 1.7 jtc 124 1.33 christos /* POSIX.2 B.3.2.2 "popen() shall ensure that any streams 125 1.33 christos from previous popen() calls that remain open in the 126 1.33 christos parent process are closed in the new child process. */ 127 1.33 christos for (old = pidlist; old; old = old->next) 128 1.28 cl #ifdef _REENTRANT 129 1.33 christos (void)close(old->fd); /* don't allow a flush */ 130 1.28 cl #else 131 1.33 christos (void)close(fileno(old->fp)); /* don't allow a flush */ 132 1.28 cl #endif 133 1.21 tv 134 1.33 christos if (type[0] == 'r') { 135 1.33 christos (void)close(pdes[0]); 136 1.33 christos if (pdes[1] != STDOUT_FILENO) { 137 1.33 christos (void)dup2(pdes[1], STDOUT_FILENO); 138 1.33 christos (void)close(pdes[1]); 139 1.33 christos } 140 1.33 christos if (type[1] == '+') 141 1.33 christos (void)dup2(STDOUT_FILENO, STDIN_FILENO); 142 1.33 christos } else { 143 1.33 christos (void)close(pdes[1]); 144 1.33 christos if (pdes[0] != STDIN_FILENO) { 145 1.33 christos (void)dup2(pdes[0], STDIN_FILENO); 146 1.21 tv (void)close(pdes[0]); 147 1.1 cgd } 148 1.33 christos } 149 1.33 christos } 150 1.12 jtc 151 1.33 christos static void 152 1.33 christos pdes_parent(int *pdes, struct pid *cur, pid_t pid, const char *type) 153 1.33 christos { 154 1.33 christos FILE *iop; 155 1.7 jtc 156 1.7 jtc /* Parent; assume fdopen can't fail. */ 157 1.33 christos if (*type == 'r') { 158 1.33 christos iop = fdopen(pdes[0], type); 159 1.28 cl #ifdef _REENTRANT 160 1.28 cl cur->fd = pdes[0]; 161 1.28 cl #endif 162 1.7 jtc (void)close(pdes[1]); 163 1.1 cgd } else { 164 1.33 christos iop = fdopen(pdes[1], type); 165 1.28 cl #ifdef _REENTRANT 166 1.28 cl cur->fd = pdes[1]; 167 1.28 cl #endif 168 1.7 jtc (void)close(pdes[0]); 169 1.1 cgd } 170 1.7 jtc 171 1.7 jtc /* Link into list of file descriptors. */ 172 1.7 jtc cur->fp = iop; 173 1.7 jtc cur->pid = pid; 174 1.7 jtc cur->next = pidlist; 175 1.7 jtc pidlist = cur; 176 1.33 christos } 177 1.33 christos 178 1.33 christos static void 179 1.33 christos pdes_error(int *pdes, struct pid *cur) 180 1.33 christos { 181 1.33 christos free(cur); 182 1.33 christos (void)close(pdes[0]); 183 1.33 christos (void)close(pdes[1]); 184 1.33 christos } 185 1.33 christos 186 1.33 christos FILE * 187 1.33 christos popen(const char *cmd, const char *type) 188 1.33 christos { 189 1.33 christos struct pid *cur; 190 1.33 christos int pdes[2], serrno; 191 1.33 christos pid_t pid; 192 1.33 christos 193 1.33 christos _DIAGASSERT(cmd != NULL); 194 1.33 christos _DIAGASSERT(type != NULL); 195 1.33 christos 196 1.33 christos if ((cur = pdes_get(pdes, &type)) == NULL) 197 1.33 christos return NULL; 198 1.33 christos 199 1.36 christos MUTEX_LOCK(); 200 1.33 christos (void)__readlockenv(); 201 1.33 christos switch (pid = vfork()) { 202 1.33 christos case -1: /* Error. */ 203 1.33 christos serrno = errno; 204 1.33 christos (void)__unlockenv(); 205 1.36 christos MUTEX_UNLOCK(); 206 1.35 christos pdes_error(pdes, cur); 207 1.33 christos errno = serrno; 208 1.33 christos return NULL; 209 1.33 christos /* NOTREACHED */ 210 1.33 christos case 0: /* Child. */ 211 1.33 christos pdes_child(pdes, type); 212 1.37 kre execl(_PATH_BSHELL, "sh", "-c", "--", cmd, NULL); 213 1.33 christos _exit(127); 214 1.33 christos /* NOTREACHED */ 215 1.33 christos } 216 1.33 christos (void)__unlockenv(); 217 1.33 christos 218 1.33 christos pdes_parent(pdes, cur, pid, type); 219 1.33 christos 220 1.36 christos MUTEX_UNLOCK(); 221 1.33 christos 222 1.33 christos return cur->fp; 223 1.33 christos } 224 1.33 christos 225 1.33 christos FILE * 226 1.33 christos popenve(const char *cmd, char *const *argv, char *const *envp, const char *type) 227 1.33 christos { 228 1.33 christos struct pid *cur; 229 1.33 christos int pdes[2], serrno; 230 1.33 christos pid_t pid; 231 1.33 christos 232 1.33 christos _DIAGASSERT(cmd != NULL); 233 1.33 christos _DIAGASSERT(type != NULL); 234 1.33 christos 235 1.33 christos if ((cur = pdes_get(pdes, &type)) == NULL) 236 1.33 christos return NULL; 237 1.33 christos 238 1.36 christos MUTEX_LOCK(); 239 1.33 christos switch (pid = vfork()) { 240 1.33 christos case -1: /* Error. */ 241 1.33 christos serrno = errno; 242 1.36 christos MUTEX_UNLOCK(); 243 1.33 christos pdes_error(pdes, cur); 244 1.33 christos errno = serrno; 245 1.33 christos return NULL; 246 1.33 christos /* NOTREACHED */ 247 1.33 christos case 0: /* Child. */ 248 1.33 christos pdes_child(pdes, type); 249 1.33 christos execve(cmd, argv, envp); 250 1.33 christos _exit(127); 251 1.33 christos /* NOTREACHED */ 252 1.33 christos } 253 1.33 christos 254 1.33 christos pdes_parent(pdes, cur, pid, type); 255 1.33 christos 256 1.36 christos MUTEX_UNLOCK(); 257 1.7 jtc 258 1.33 christos return cur->fp; 259 1.1 cgd } 260 1.1 cgd 261 1.7 jtc /* 262 1.7 jtc * pclose -- 263 1.7 jtc * Pclose returns -1 if stream is not associated with a `popened' command, 264 1.7 jtc * if already `pclosed', or waitpid returns an error. 265 1.7 jtc */ 266 1.1 cgd int 267 1.32 abs pclose(FILE *iop) 268 1.1 cgd { 269 1.19 perry struct pid *cur, *last; 270 1.9 jtc int pstat; 271 1.1 cgd pid_t pid; 272 1.23 lukem 273 1.23 lukem _DIAGASSERT(iop != NULL); 274 1.1 cgd 275 1.36 christos MUTEX_LOCK(); 276 1.28 cl 277 1.7 jtc /* Find the appropriate file pointer. */ 278 1.7 jtc for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next) 279 1.7 jtc if (cur->fp == iop) 280 1.7 jtc break; 281 1.28 cl if (cur == NULL) { 282 1.36 christos MUTEX_UNLOCK(); 283 1.33 christos errno = ESRCH; 284 1.33 christos return -1; 285 1.28 cl } 286 1.16 perry 287 1.16 perry (void)fclose(iop); 288 1.7 jtc 289 1.7 jtc /* Remove the entry from the linked list. */ 290 1.7 jtc if (last == NULL) 291 1.7 jtc pidlist = cur->next; 292 1.7 jtc else 293 1.7 jtc last->next = cur->next; 294 1.28 cl 295 1.36 christos MUTEX_UNLOCK(); 296 1.28 cl 297 1.28 cl do { 298 1.28 cl pid = waitpid(cur->pid, &pstat, 0); 299 1.28 cl } while (pid == -1 && errno == EINTR); 300 1.28 cl 301 1.7 jtc free(cur); 302 1.28 cl 303 1.33 christos return pid == -1 ? -1 : pstat; 304 1.1 cgd } 305