1301169Slidl/* $NetBSD: popenve.c,v 1.2 2015/01/22 03:10:50 christos Exp $ */ 2301169Slidl 3301169Slidl/* 4301169Slidl * Copyright (c) 1988, 1993 5301169Slidl * The Regents of the University of California. All rights reserved. 6301169Slidl * 7301169Slidl * This code is derived from software written by Ken Arnold and 8301169Slidl * published in UNIX Review, Vol. 6, No. 8. 9301169Slidl * 10301169Slidl * Redistribution and use in source and binary forms, with or without 11301169Slidl * modification, are permitted provided that the following conditions 12301169Slidl * are met: 13301169Slidl * 1. Redistributions of source code must retain the above copyright 14301169Slidl * notice, this list of conditions and the following disclaimer. 15301169Slidl * 2. Redistributions in binary form must reproduce the above copyright 16301169Slidl * notice, this list of conditions and the following disclaimer in the 17301169Slidl * documentation and/or other materials provided with the distribution. 18301169Slidl * 3. Neither the name of the University nor the names of its contributors 19301169Slidl * may be used to endorse or promote products derived from this software 20301169Slidl * without specific prior written permission. 21301169Slidl * 22301169Slidl * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23301169Slidl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24301169Slidl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25301169Slidl * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26301169Slidl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27301169Slidl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28301169Slidl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29301169Slidl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30301169Slidl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31301169Slidl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32301169Slidl * SUCH DAMAGE. 33301169Slidl */ 34301169Slidl 35301169Slidl#ifdef HAVE_CONFIG_H 36301169Slidl#include "config.h" 37301169Slidl#endif 38301169Slidl 39301169Slidl#include <sys/cdefs.h> 40301169Slidl#if defined(LIBC_SCCS) && !defined(lint) 41301169Slidl#if 0 42301169Slidlstatic char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 5/3/95"; 43301169Slidl#else 44301169Slidl__RCSID("$NetBSD: popenve.c,v 1.2 2015/01/22 03:10:50 christos Exp $"); 45301169Slidl#endif 46301169Slidl#endif /* LIBC_SCCS and not lint */ 47301169Slidl 48301169Slidl#include <sys/param.h> 49301169Slidl#include <sys/wait.h> 50301169Slidl#include <sys/socket.h> 51301169Slidl 52301169Slidl#include <assert.h> 53301169Slidl#include <errno.h> 54301169Slidl#include <paths.h> 55301169Slidl#include <signal.h> 56301169Slidl#include <stdio.h> 57301169Slidl#include <stdlib.h> 58301169Slidl#include <string.h> 59301169Slidl#include <unistd.h> 60301169Slidl#include <fcntl.h> 61301169Slidl 62301169Slidl#ifdef __weak_alias 63301169Slidl__weak_alias(popen,_popen) 64301169Slidl__weak_alias(pclose,_pclose) 65301169Slidl#endif 66301169Slidl 67301169Slidlstatic struct pid { 68301169Slidl struct pid *next; 69301169Slidl FILE *fp; 70301169Slidl#ifdef _REENTRANT 71301169Slidl int fd; 72301169Slidl#endif 73301169Slidl pid_t pid; 74301169Slidl} *pidlist; 75301169Slidl 76301169Slidl#ifdef _REENTRANT 77301169Slidlstatic rwlock_t pidlist_lock = RWLOCK_INITIALIZER; 78301169Slidl#endif 79301169Slidl 80301169Slidlstatic struct pid * 81301169Slidlpdes_get(int *pdes, const char **type) 82301169Slidl{ 83301169Slidl struct pid *cur; 84301169Slidl int flags = strchr(*type, 'e') ? O_CLOEXEC : 0; 85301169Slidl int serrno; 86301169Slidl 87301169Slidl if (strchr(*type, '+')) { 88301169Slidl#ifndef SOCK_CLOEXEC 89301169Slidl#define SOCK_CLOEXEC 0 90301169Slidl#endif 91301169Slidl int stype = flags ? (SOCK_STREAM | SOCK_CLOEXEC) : SOCK_STREAM; 92301169Slidl *type = "r+"; 93301169Slidl if (socketpair(AF_LOCAL, stype, 0, pdes) < 0) 94301169Slidl return NULL; 95301169Slidl#if SOCK_CLOEXEC == 0 96301169Slidl fcntl(pdes[0], F_SETFD, FD_CLOEXEC); 97301169Slidl fcntl(pdes[1], F_SETFD, FD_CLOEXEC); 98301169Slidl#endif 99301169Slidl } else { 100301169Slidl *type = strrchr(*type, 'r') ? "r" : "w"; 101301169Slidl#if SOCK_CLOEXEC != 0 102301169Slidl if (pipe2(pdes, flags) == -1) 103301169Slidl return NULL; 104301169Slidl#else 105301169Slidl if (pipe(pdes) == -1) 106301169Slidl return NULL; 107301169Slidl fcntl(pdes[0], F_SETFL, fcntl(pdes[0], F_GETFL) | flags); 108301169Slidl fcntl(pdes[1], F_SETFL, fcntl(pdes[1], F_GETFL) | flags); 109301169Slidl#endif 110301169Slidl } 111301169Slidl 112301169Slidl if ((cur = malloc(sizeof(*cur))) != NULL) 113301169Slidl return cur; 114301169Slidl serrno = errno; 115301169Slidl (void)close(pdes[0]); 116301169Slidl (void)close(pdes[1]); 117301169Slidl errno = serrno; 118301169Slidl return NULL; 119301169Slidl} 120301169Slidl 121301169Slidlstatic void 122301169Slidlpdes_child(int *pdes, const char *type) 123301169Slidl{ 124301169Slidl struct pid *old; 125301169Slidl 126301169Slidl /* POSIX.2 B.3.2.2 "popen() shall ensure that any streams 127301169Slidl from previous popen() calls that remain open in the 128301169Slidl parent process are closed in the new child process. */ 129301169Slidl for (old = pidlist; old; old = old->next) 130301169Slidl#ifdef _REENTRANT 131301169Slidl (void)close(old->fd); /* don't allow a flush */ 132301169Slidl#else 133301169Slidl (void)close(fileno(old->fp)); /* don't allow a flush */ 134301169Slidl#endif 135301169Slidl 136301169Slidl if (type[0] == 'r') { 137301169Slidl (void)close(pdes[0]); 138301169Slidl if (pdes[1] != STDOUT_FILENO) { 139301169Slidl (void)dup2(pdes[1], STDOUT_FILENO); 140301169Slidl (void)close(pdes[1]); 141301169Slidl } 142301169Slidl if (type[1] == '+') 143301169Slidl (void)dup2(STDOUT_FILENO, STDIN_FILENO); 144301169Slidl } else { 145301169Slidl (void)close(pdes[1]); 146301169Slidl if (pdes[0] != STDIN_FILENO) { 147301169Slidl (void)dup2(pdes[0], STDIN_FILENO); 148301169Slidl (void)close(pdes[0]); 149301169Slidl } 150301169Slidl } 151301169Slidl} 152301169Slidl 153301169Slidlstatic void 154301169Slidlpdes_parent(int *pdes, struct pid *cur, pid_t pid, const char *type) 155301169Slidl{ 156301169Slidl FILE *iop; 157301169Slidl 158301169Slidl /* Parent; assume fdopen can't fail. */ 159301169Slidl if (*type == 'r') { 160301169Slidl iop = fdopen(pdes[0], type); 161301169Slidl#ifdef _REENTRANT 162301169Slidl cur->fd = pdes[0]; 163301169Slidl#endif 164301169Slidl (void)close(pdes[1]); 165301169Slidl } else { 166301169Slidl iop = fdopen(pdes[1], type); 167301169Slidl#ifdef _REENTRANT 168301169Slidl cur->fd = pdes[1]; 169301169Slidl#endif 170301169Slidl (void)close(pdes[0]); 171301169Slidl } 172301169Slidl 173301169Slidl /* Link into list of file descriptors. */ 174301169Slidl cur->fp = iop; 175301169Slidl cur->pid = pid; 176301169Slidl cur->next = pidlist; 177301169Slidl pidlist = cur; 178301169Slidl} 179301169Slidl 180301169Slidlstatic void 181301169Slidlpdes_error(int *pdes, struct pid *cur) 182301169Slidl{ 183301169Slidl free(cur); 184301169Slidl (void)close(pdes[0]); 185301169Slidl (void)close(pdes[1]); 186301169Slidl} 187301169Slidl 188301169SlidlFILE * 189301169Slidlpopenve(const char *cmd, char *const *argv, char *const *envp, const char *type) 190301169Slidl{ 191301169Slidl struct pid *cur; 192301169Slidl int pdes[2], serrno; 193301169Slidl pid_t pid; 194301169Slidl 195301169Slidl if ((cur = pdes_get(pdes, &type)) == NULL) 196301169Slidl return NULL; 197301169Slidl 198301169Slidl#ifdef _REENTRANT 199301169Slidl (void)rwlock_rdlock(&pidlist_lock); 200301169Slidl#endif 201301169Slidl switch (pid = vfork()) { 202301169Slidl case -1: /* Error. */ 203301169Slidl serrno = errno; 204301169Slidl#ifdef _REENTRANT 205301169Slidl (void)rwlock_unlock(&pidlist_lock); 206301169Slidl#endif 207301169Slidl pdes_error(pdes, cur); 208301169Slidl errno = serrno; 209301169Slidl return NULL; 210301169Slidl /* NOTREACHED */ 211301169Slidl case 0: /* Child. */ 212301169Slidl pdes_child(pdes, type); 213301169Slidl execve(cmd, argv, envp); 214301169Slidl _exit(127); 215301169Slidl /* NOTREACHED */ 216301169Slidl } 217301169Slidl 218301169Slidl pdes_parent(pdes, cur, pid, type); 219301169Slidl 220301169Slidl#ifdef _REENTRANT 221301169Slidl (void)rwlock_unlock(&pidlist_lock); 222301169Slidl#endif 223301169Slidl 224301169Slidl return cur->fp; 225301169Slidl} 226301169Slidl 227301169Slidl/* 228301169Slidl * pclose -- 229301169Slidl * Pclose returns -1 if stream is not associated with a `popened' command, 230301169Slidl * if already `pclosed', or waitpid returns an error. 231301169Slidl */ 232301169Slidlint 233301169Slidlpcloseve(FILE *iop) 234301169Slidl{ 235301169Slidl struct pid *cur, *last; 236301169Slidl int pstat; 237301169Slidl pid_t pid; 238301169Slidl 239301169Slidl#ifdef _REENTRANT 240301169Slidl rwlock_wrlock(&pidlist_lock); 241301169Slidl#endif 242301169Slidl 243301169Slidl /* Find the appropriate file pointer. */ 244301169Slidl for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next) 245301169Slidl if (cur->fp == iop) 246301169Slidl break; 247301169Slidl if (cur == NULL) { 248301169Slidl#ifdef _REENTRANT 249301169Slidl (void)rwlock_unlock(&pidlist_lock); 250301169Slidl#endif 251301169Slidl errno = ESRCH; 252301169Slidl return -1; 253301169Slidl } 254301169Slidl 255301169Slidl (void)fclose(iop); 256301169Slidl 257301169Slidl /* Remove the entry from the linked list. */ 258301169Slidl if (last == NULL) 259301169Slidl pidlist = cur->next; 260301169Slidl else 261301169Slidl last->next = cur->next; 262301169Slidl 263301169Slidl#ifdef _REENTRANT 264301169Slidl (void)rwlock_unlock(&pidlist_lock); 265301169Slidl#endif 266301169Slidl 267301169Slidl do { 268301169Slidl pid = waitpid(cur->pid, &pstat, 0); 269301169Slidl } while (pid == -1 && errno == EINTR); 270301169Slidl 271301169Slidl free(cur); 272301169Slidl 273301169Slidl return pid == -1 ? -1 : pstat; 274301169Slidl} 275