]> git.saurik.com Git - apple/libc.git/blob - gen/FreeBSD/popen.c.patch
Libc-763.11.tar.gz
[apple/libc.git] / gen / FreeBSD / popen.c.patch
1 --- popen.c.orig 2009-12-02 15:21:37.000000000 -0800
2 +++ popen.c 2009-12-02 15:36:51.000000000 -0800
3 @@ -30,6 +30,10 @@
4 * SUCH DAMAGE.
5 */
6
7 +#ifdef VARIANT_DARWINEXTSN
8 +#define _DARWIN_UNLIMITED_STREAMS
9 +#endif /* VARIANT_DARWINEXTSN */
10 +
11 #if defined(LIBC_SCCS) && !defined(lint)
12 static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 5/3/95";
13 #endif /* LIBC_SCCS and not lint */
14 @@ -40,7 +44,8 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/pop
15 #include <sys/param.h>
16 #include <sys/queue.h>
17 #include <sys/wait.h>
18 -
19 +#include <sys/socket.h>
20 +#include <wchar.h> /* fwide() */
21 #include <signal.h>
22 #include <errno.h>
23 #include <unistd.h>
24 @@ -49,18 +54,39 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/pop
25 #include <string.h>
26 #include <paths.h>
27 #include <pthread.h>
28 +#include <spawn.h>
29 #include "un-namespace.h"
30 #include "libc_private.h"
31
32 -extern char **environ;
33 +#include <crt_externs.h>
34 +#define environ (*_NSGetEnviron())
35 +
36 +/* Our queue.h doesn't have SLIST_REMOVE_AFTER in it yet
37 + * <rdar://problem/7431558> API: Add SLIST_REMOVE_AFTER to sys/queue.h (from FreeBSD)
38 + */
39 +#ifndef SLIST_REMOVE_AFTER
40 +#define SLIST_REMOVE_AFTER(elm, field) do { \
41 + SLIST_NEXT(elm, field) = \
42 + SLIST_NEXT(SLIST_NEXT(elm, field), field); \
43 +} while (0)
44 +#endif
45
46 +/* 3516149 - store file descriptor and use that to close to prevent blocking */
47 struct pid {
48 SLIST_ENTRY(pid) next;
49 FILE *fp;
50 + int fd;
51 pid_t pid;
52 };
53 -static SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
54 -static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
55 +#define pidlist __popen_pidlist
56 +#define pidlist_mutex __popen_pidlist_mutex
57 +#ifndef BUILDING_VARIANT
58 +__private_extern__ SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
59 +__private_extern__ pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
60 +#else /* BUILDING_VARIANT */
61 +extern SLIST_HEAD(, pid) pidlist;
62 +extern pthread_mutex_t pidlist_mutex;
63 +#endif /* !BUILDING_VARIANT */
64
65 #define THREAD_LOCK() if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex)
66 #define THREAD_UNLOCK() if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex)
67 @@ -71,84 +97,108 @@ popen(command, type)
68 {
69 struct pid *cur;
70 FILE *iop;
71 - int pdes[2], pid, twoway;
72 + int pdes[2], pid, twoway, other;
73 char *argv[4];
74 struct pid *p;
75 + posix_spawn_file_actions_t file_actions;
76 + int err;
77
78 - /*
79 - * Lite2 introduced two-way popen() pipes using _socketpair().
80 - * FreeBSD's pipe() is bidirectional, so we use that.
81 - */
82 - if (strchr(type, '+')) {
83 + if (type == NULL) {
84 + errno = EINVAL;
85 + return (NULL);
86 + }
87 + if (strcmp(type, "r+") == 0) {
88 twoway = 1;
89 type = "r+";
90 + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pdes) < 0)
91 + return (NULL);
92 } else {
93 twoway = 0;
94 - if ((*type != 'r' && *type != 'w') || type[1])
95 + if ((*type != 'r' && *type != 'w') || type[1]) {
96 + errno = EINVAL;
97 + return (NULL);
98 + }
99 + if (pipe(pdes) < 0)
100 return (NULL);
101 }
102 - if (pipe(pdes) < 0)
103 - return (NULL);
104
105 - if ((cur = malloc(sizeof(struct pid))) == NULL) {
106 + /* fdopen can now fail */
107 + if (*type == 'r') {
108 + iop = fdopen(pdes[0], type);
109 + other = pdes[1];
110 + } else {
111 + iop = fdopen(pdes[1], type);
112 + other = pdes[0];
113 + }
114 + if (iop == NULL) {
115 (void)_close(pdes[0]);
116 (void)_close(pdes[1]);
117 return (NULL);
118 }
119
120 + if ((cur = malloc(sizeof(struct pid))) == NULL) {
121 + (void)fclose(iop);
122 + (void)_close(other);
123 + return (NULL);
124 + }
125 +
126 + if ((err = posix_spawn_file_actions_init(&file_actions)) != 0) {
127 + (void)fclose(iop);
128 + (void)_close(other);
129 + free(cur);
130 + errno = err;
131 + return (NULL);
132 + }
133 + if (*type == 'r') {
134 + /*
135 + * The dup2() to STDIN_FILENO is repeated to avoid
136 + * writing to pdes[1], which might corrupt the
137 + * parent's copy. This isn't good enough in
138 + * general, since the _exit() is no return, so
139 + * the compiler is free to corrupt all the local
140 + * variables.
141 + */
142 + (void)posix_spawn_file_actions_addclose(&file_actions, pdes[0]);
143 + if (pdes[1] != STDOUT_FILENO) {
144 + (void)posix_spawn_file_actions_adddup2(&file_actions, pdes[1], STDOUT_FILENO);
145 + (void)posix_spawn_file_actions_addclose(&file_actions, pdes[1]);
146 + if (twoway)
147 + (void)posix_spawn_file_actions_adddup2(&file_actions, STDOUT_FILENO, STDIN_FILENO);
148 + } else if (twoway && (pdes[1] != STDIN_FILENO))
149 + (void)posix_spawn_file_actions_adddup2(&file_actions, pdes[1], STDIN_FILENO);
150 + } else {
151 + if (pdes[0] != STDIN_FILENO) {
152 + (void)posix_spawn_file_actions_adddup2(&file_actions, pdes[0], STDIN_FILENO);
153 + (void)posix_spawn_file_actions_addclose(&file_actions, pdes[0]);
154 + }
155 + (void)posix_spawn_file_actions_addclose(&file_actions, pdes[1]);
156 + }
157 + SLIST_FOREACH(p, &pidlist, next)
158 + (void)posix_spawn_file_actions_addclose(&file_actions, p->fd);
159 +
160 argv[0] = "sh";
161 argv[1] = "-c";
162 argv[2] = (char *)command;
163 argv[3] = NULL;
164
165 - THREAD_LOCK();
166 - switch (pid = vfork()) {
167 - case -1: /* Error. */
168 - THREAD_UNLOCK();
169 - (void)_close(pdes[0]);
170 - (void)_close(pdes[1]);
171 + err = posix_spawn(&pid, _PATH_BSHELL, &file_actions, NULL, argv, environ);
172 + posix_spawn_file_actions_destroy(&file_actions);
173 +
174 + if (err == ENOMEM || err == EAGAIN) { /* as if fork failed */
175 + (void)fclose(iop);
176 + (void)_close(other);
177 free(cur);
178 + errno = err;
179 return (NULL);
180 - /* NOTREACHED */
181 - case 0: /* Child. */
182 - if (*type == 'r') {
183 - /*
184 - * The _dup2() to STDIN_FILENO is repeated to avoid
185 - * writing to pdes[1], which might corrupt the
186 - * parent's copy. This isn't good enough in
187 - * general, since the _exit() is no return, so
188 - * the compiler is free to corrupt all the local
189 - * variables.
190 - */
191 - (void)_close(pdes[0]);
192 - if (pdes[1] != STDOUT_FILENO) {
193 - (void)_dup2(pdes[1], STDOUT_FILENO);
194 - (void)_close(pdes[1]);
195 - if (twoway)
196 - (void)_dup2(STDOUT_FILENO, STDIN_FILENO);
197 - } else if (twoway && (pdes[1] != STDIN_FILENO))
198 - (void)_dup2(pdes[1], STDIN_FILENO);
199 - } else {
200 - if (pdes[0] != STDIN_FILENO) {
201 - (void)_dup2(pdes[0], STDIN_FILENO);
202 - (void)_close(pdes[0]);
203 - }
204 - (void)_close(pdes[1]);
205 - }
206 - SLIST_FOREACH(p, &pidlist, next)
207 - (void)_close(fileno(p->fp));
208 - _execve(_PATH_BSHELL, argv, environ);
209 - _exit(127);
210 - /* NOTREACHED */
211 + } else if (err != 0) { /* couldn't exec the shell */
212 + pid = -1;
213 }
214 - THREAD_UNLOCK();
215
216 - /* Parent; assume fdopen can't fail. */
217 if (*type == 'r') {
218 - iop = fdopen(pdes[0], type);
219 + cur->fd = pdes[0];
220 (void)_close(pdes[1]);
221 } else {
222 - iop = fdopen(pdes[1], type);
223 + cur->fd = pdes[1];
224 (void)_close(pdes[0]);
225 }
226
227 @@ -158,10 +208,11 @@ popen(command, type)
228 THREAD_LOCK();
229 SLIST_INSERT_HEAD(&pidlist, cur, next);
230 THREAD_UNLOCK();
231 -
232 + fwide(iop, -1); /* byte stream */
233 return (iop);
234 }
235
236 +#ifndef BUILDING_VARIANT
237 /*
238 * pclose --
239 * Pclose returns -1 if stream is not associated with a `popened' command,
240 @@ -196,6 +247,10 @@ pclose(iop)
241
242 (void)fclose(iop);
243
244 + if (cur->pid < 0) {
245 + free(cur);
246 + return W_EXITCODE(127, 0);
247 + }
248 do {
249 pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0);
250 } while (pid == -1 && errno == EINTR);
251 @@ -204,3 +259,4 @@ pclose(iop)
252
253 return (pid == -1 ? -1 : pstat);
254 }
255 +#endif /* !BUILDING_VARIANT */