1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1988, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software written by Ken Arnold and
8 * published in UNIX Review, Vol. 6, No. 8.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 __SCCSID("@(#)popen.c 8.3 (Berkeley) 5/3/95");
37 #include "namespace.h"
38 #include <sys/param.h>
39 #include <sys/queue.h>
40 #include <sys/wait.h>
41
42 #include <signal.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <paths.h>
50 #include <pthread.h>
51 #include "un-namespace.h"
52 #include "libc_private.h"
53
54 struct pid {
55 SLIST_ENTRY(pid) next;
56 FILE *fp;
57 pid_t pid;
58 };
59 static SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
60 static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
61
62 #define THREAD_LOCK() if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex)
63 #define THREAD_UNLOCK() if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex)
64
65 FILE *
popen(const char * command,const char * type)66 popen(const char *command, const char *type)
67 {
68 struct pid *cur;
69 FILE *iop;
70 int pdes[2], pid, twoway, cloexec;
71 int pdes_unused_in_parent;
72 char *argv[4];
73 struct pid *p;
74
75 cloexec = strchr(type, 'e') != NULL;
76 /*
77 * Lite2 introduced two-way popen() pipes using _socketpair().
78 * FreeBSD's pipe() is bidirectional, so we use that.
79 */
80 if (strchr(type, '+')) {
81 twoway = 1;
82 type = "r+";
83 } else {
84 twoway = 0;
85 if ((*type != 'r' && *type != 'w') ||
86 (type[1] && (type[1] != 'e' || type[2])))
87 return (NULL);
88 }
89 if (pipe2(pdes, O_CLOEXEC) < 0)
90 return (NULL);
91
92 if ((cur = malloc(sizeof(struct pid))) == NULL) {
93 (void)_close(pdes[0]);
94 (void)_close(pdes[1]);
95 return (NULL);
96 }
97
98 if (*type == 'r') {
99 iop = fdopen(pdes[0], type);
100 pdes_unused_in_parent = pdes[1];
101 } else {
102 iop = fdopen(pdes[1], type);
103 pdes_unused_in_parent = pdes[0];
104 }
105 if (iop == NULL) {
106 (void)_close(pdes[0]);
107 (void)_close(pdes[1]);
108 free(cur);
109 return (NULL);
110 }
111
112 argv[0] = "sh";
113 argv[1] = "-c";
114 argv[2] = (char *)command;
115 argv[3] = NULL;
116
117 THREAD_LOCK();
118 switch (pid = vfork()) {
119 case -1: /* Error. */
120 THREAD_UNLOCK();
121 /*
122 * The _close() closes the unused end of pdes[], while
123 * the fclose() closes the used end of pdes[], *and* cleans
124 * up iop.
125 */
126 (void)_close(pdes_unused_in_parent);
127 free(cur);
128 (void)fclose(iop);
129 return (NULL);
130 /* NOTREACHED */
131 case 0: /* Child. */
132 if (*type == 'r') {
133 /*
134 * The _dup2() to STDIN_FILENO is repeated to avoid
135 * writing to pdes[1], which might corrupt the
136 * parent's copy. This isn't good enough in
137 * general, since the _exit() is no return, so
138 * the compiler is free to corrupt all the local
139 * variables.
140 */
141 if (pdes[1] != STDOUT_FILENO) {
142 (void)_dup2(pdes[1], STDOUT_FILENO);
143 if (twoway)
144 (void)_dup2(STDOUT_FILENO, STDIN_FILENO);
145 } else if (twoway && (pdes[1] != STDIN_FILENO)) {
146 (void)_dup2(pdes[1], STDIN_FILENO);
147 (void)_fcntl(pdes[1], F_SETFD, 0);
148 } else
149 (void)_fcntl(pdes[1], F_SETFD, 0);
150 } else {
151 if (pdes[0] != STDIN_FILENO) {
152 (void)_dup2(pdes[0], STDIN_FILENO);
153 } else
154 (void)_fcntl(pdes[0], F_SETFD, 0);
155 }
156 SLIST_FOREACH(p, &pidlist, next)
157 (void)_close(fileno(p->fp));
158 _execve(_PATH_BSHELL, argv, environ);
159 _exit(127);
160 /* NOTREACHED */
161 }
162 THREAD_UNLOCK();
163
164 /* Parent. */
165 (void)_close(pdes_unused_in_parent);
166
167 /* Link into list of file descriptors. */
168 cur->fp = iop;
169 cur->pid = pid;
170 THREAD_LOCK();
171 SLIST_INSERT_HEAD(&pidlist, cur, next);
172 THREAD_UNLOCK();
173
174 /*
175 * To guard against undesired fd passing with concurrent calls,
176 * only clear the close-on-exec flag after linking the file into
177 * the list which will cause an explicit close.
178 */
179 if (!cloexec)
180 (void)_fcntl(fileno(iop), F_SETFD, 0);
181
182 return (iop);
183 }
184
185 /*
186 * pclose --
187 * Pclose returns -1 if stream is not associated with a `popened' command,
188 * if already `pclosed', or waitpid returns an error.
189 */
190 int
pclose(FILE * iop)191 pclose(FILE *iop)
192 {
193 struct pid *cur, *last = NULL;
194 int pstat;
195 pid_t pid;
196
197 /*
198 * Find the appropriate file pointer and remove it from the list.
199 */
200 THREAD_LOCK();
201 SLIST_FOREACH(cur, &pidlist, next) {
202 if (cur->fp == iop)
203 break;
204 last = cur;
205 }
206 if (cur == NULL) {
207 THREAD_UNLOCK();
208 return (-1);
209 }
210 if (last == NULL)
211 SLIST_REMOVE_HEAD(&pidlist, next);
212 else
213 SLIST_REMOVE_AFTER(last, next);
214 THREAD_UNLOCK();
215
216 (void)fclose(iop);
217
218 do {
219 pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0);
220 } while (pid == -1 && errno == EINTR);
221
222 free(cur);
223
224 return (pid == -1 ? -1 : pstat);
225 }
226