]> git.saurik.com Git - apple/libc.git/blame - gen/popen-fbsd.c
Libc-594.1.4.tar.gz
[apple/libc.git] / gen / popen-fbsd.c
CommitLineData
224c7076
A
1/*
2 * Copyright (c) 1988, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software written by Ken Arnold and
6 * published in UNIX Review, Vol. 6, No. 8.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
34e8f829
A
37#ifdef VARIANT_DARWINEXTSN
38#define _DARWIN_UNLIMITED_STREAMS
39#endif /* VARIANT_DARWINEXTSN */
40
224c7076
A
41#if defined(LIBC_SCCS) && !defined(lint)
42static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 5/3/95";
43#endif /* LIBC_SCCS and not lint */
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD: src/lib/libc/gen/popen.c,v 1.18 2003/01/04 00:15:15 tjr Exp $");
46
47#include "namespace.h"
48#include <sys/param.h>
49#include <sys/wait.h>
50#include <sys/socket.h>
51#include <wchar.h> /* fwide() */
52#include <signal.h>
53#include <errno.h>
54#include <unistd.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <paths.h>
59#include <pthread.h>
34e8f829 60#include <spawn.h>
224c7076
A
61#include "un-namespace.h"
62#include "libc_private.h"
63
64#include <crt_externs.h>
65#define environ (*_NSGetEnviron())
66
67/* 3516149 - store file descriptor and use that to close to prevent blocking */
34e8f829 68struct pid {
224c7076
A
69 struct pid *next;
70 FILE *fp;
71 int fd;
72 pid_t pid;
34e8f829
A
73};
74#define pidlist __popen_pidlist
75#define pidlist_mutex __popen_pidlist_mutex
76#ifndef BUILDING_VARIANT
77__private_extern__ struct pid *pidlist = NULL;
78__private_extern__ pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
79#else /* BUILDING_VARIANT */
80extern struct pid *pidlist;
81extern pthread_mutex_t pidlist_mutex;
82#endif /* !BUILDING_VARIANT */
224c7076
A
83
84#define THREAD_LOCK() if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex)
85#define THREAD_UNLOCK() if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex)
86
87FILE *
88popen(command, type)
89 const char *command, *type;
90{
91 struct pid *cur;
92 FILE *iop;
34e8f829 93 int pdes[2], pid, twoway, other;
224c7076
A
94 char *argv[4];
95 struct pid *p;
34e8f829
A
96 posix_spawn_file_actions_t file_actions;
97 int err;
224c7076
A
98
99 if (type == NULL) {
100 errno = EINVAL;
101 return (NULL);
102 }
103 if (strcmp(type, "r+") == 0) {
104 twoway = 1;
105 type = "r+";
106 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pdes) < 0)
107 return (NULL);
108 } else {
109 twoway = 0;
110 if ((*type != 'r' && *type != 'w') || type[1]) {
111 errno = EINVAL;
112 return (NULL);
34e8f829
A
113 }
114 if (pipe(pdes) < 0)
115 return (NULL);
116 }
117
118 /* fdopen can now fail */
119 if (*type == 'r') {
120 iop = fdopen(pdes[0], type);
121 other = pdes[1];
122 } else {
123 iop = fdopen(pdes[1], type);
124 other = pdes[0];
224c7076 125 }
34e8f829
A
126 if (iop == NULL) {
127 (void)_close(pdes[0]);
128 (void)_close(pdes[1]);
224c7076
A
129 return (NULL);
130 }
131
132 if ((cur = malloc(sizeof(struct pid))) == NULL) {
34e8f829
A
133 (void)fclose(iop);
134 (void)_close(other);
135 return (NULL);
136 }
137
138 if ((err = posix_spawn_file_actions_init(&file_actions)) != 0) {
139 (void)fclose(iop);
140 (void)_close(other);
141 free(cur);
142 errno = err;
224c7076
A
143 return (NULL);
144 }
34e8f829
A
145 if (*type == 'r') {
146 /*
147 * The dup2() to STDIN_FILENO is repeated to avoid
148 * writing to pdes[1], which might corrupt the
149 * parent's copy. This isn't good enough in
150 * general, since the _exit() is no return, so
151 * the compiler is free to corrupt all the local
152 * variables.
153 */
154 (void)posix_spawn_file_actions_addclose(&file_actions, pdes[0]);
155 if (pdes[1] != STDOUT_FILENO) {
156 (void)posix_spawn_file_actions_adddup2(&file_actions, pdes[1], STDOUT_FILENO);
157 (void)posix_spawn_file_actions_addclose(&file_actions, pdes[1]);
158 if (twoway)
159 (void)posix_spawn_file_actions_adddup2(&file_actions, STDOUT_FILENO, STDIN_FILENO);
160 } else if (twoway && (pdes[1] != STDIN_FILENO))
161 (void)posix_spawn_file_actions_adddup2(&file_actions, pdes[1], STDIN_FILENO);
162 } else {
163 if (pdes[0] != STDIN_FILENO) {
164 (void)posix_spawn_file_actions_adddup2(&file_actions, pdes[0], STDIN_FILENO);
165 (void)posix_spawn_file_actions_addclose(&file_actions, pdes[0]);
166 }
167 (void)posix_spawn_file_actions_addclose(&file_actions, pdes[1]);
168 }
169 for (p = pidlist; p; p = p->next) {
170 (void)posix_spawn_file_actions_addclose(&file_actions, p->fd);
171 }
224c7076
A
172
173 argv[0] = "sh";
174 argv[1] = "-c";
175 argv[2] = (char *)command;
176 argv[3] = NULL;
177
34e8f829
A
178 err = posix_spawn(&pid, _PATH_BSHELL, &file_actions, NULL, argv, environ);
179 posix_spawn_file_actions_destroy(&file_actions);
180
181 if (err == ENOMEM || err == EAGAIN) { /* as if fork failed */
182 (void)fclose(iop);
183 (void)_close(other);
224c7076 184 free(cur);
34e8f829 185 errno = err;
224c7076 186 return (NULL);
34e8f829
A
187 } else if (err != 0) { /* couldn't exec the shell */
188 pid = -1;
224c7076 189 }
224c7076 190
224c7076 191 if (*type == 'r') {
224c7076
A
192 cur->fd = pdes[0];
193 (void)_close(pdes[1]);
194 } else {
224c7076
A
195 cur->fd = pdes[1];
196 (void)_close(pdes[0]);
197 }
198
199 /* Link into list of file descriptors. */
200 cur->fp = iop;
201 cur->pid = pid;
202 THREAD_LOCK();
203 cur->next = pidlist;
204 pidlist = cur;
205 THREAD_UNLOCK();
206 fwide(iop, -1); /* byte stream */
207 return (iop);
208}
209
34e8f829 210#ifndef BUILDING_VARIANT
224c7076
A
211/*
212 * pclose --
213 * Pclose returns -1 if stream is not associated with a `popened' command,
214 * if already `pclosed', or waitpid returns an error.
215 */
216int
217pclose(iop)
218 FILE *iop;
219{
220 struct pid *cur, *last;
221 int pstat;
222 pid_t pid;
223
224 /*
225 * Find the appropriate file pointer and remove it from the list.
226 */
227 THREAD_LOCK();
228 for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
229 if (cur->fp == iop)
230 break;
231 if (cur == NULL) {
232 THREAD_UNLOCK();
233 return (-1);
234 }
235 if (last == NULL)
236 pidlist = cur->next;
237 else
238 last->next = cur->next;
239 THREAD_UNLOCK();
240
241 (void)fclose(iop);
242
34e8f829
A
243 if (cur->pid < 0) {
244 free(cur);
245 return W_EXITCODE(127, 0);
246 }
224c7076
A
247 do {
248 pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0);
249 } while (pid == -1 && errno == EINTR);
250
251 free(cur);
252
253 return (pid == -1 ? -1 : pstat);
254}
34e8f829 255#endif /* !BUILDING_VARIANT */