]>
Commit | Line | Data |
---|---|---|
1f2f436a A |
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 @@ | |
34e8f829 A |
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 */ | |
1f2f436a | 14 | @@ -40,7 +44,8 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/pop |
59e0d9fe | 15 | #include <sys/param.h> |
1f2f436a | 16 | #include <sys/queue.h> |
59e0d9fe | 17 | #include <sys/wait.h> |
224c7076 | 18 | - |
59e0d9fe | 19 | +#include <sys/socket.h> |
224c7076 | 20 | +#include <wchar.h> /* fwide() */ |
59e0d9fe A |
21 | #include <signal.h> |
22 | #include <errno.h> | |
224c7076 | 23 | #include <unistd.h> |
1f2f436a | 24 | @@ -49,18 +54,39 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/pop |
34e8f829 A |
25 | #include <string.h> |
26 | #include <paths.h> | |
27 | #include <pthread.h> | |
28 | +#include <spawn.h> | |
9385eb3d A |
29 | #include "un-namespace.h" |
30 | #include "libc_private.h" | |
31 | ||
32 | -extern char **environ; | |
33 | +#include <crt_externs.h> | |
34 | +#define environ (*_NSGetEnviron()) | |
1f2f436a A |
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 | |
9385eb3d | 45 | |
59e0d9fe | 46 | +/* 3516149 - store file descriptor and use that to close to prevent blocking */ |
1f2f436a A |
47 | struct pid { |
48 | SLIST_ENTRY(pid) next; | |
59e0d9fe A |
49 | FILE *fp; |
50 | + int fd; | |
51 | pid_t pid; | |
1f2f436a A |
52 | }; |
53 | -static SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist); | |
34e8f829 | 54 | -static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER; |
34e8f829 A |
55 | +#define pidlist __popen_pidlist |
56 | +#define pidlist_mutex __popen_pidlist_mutex | |
57 | +#ifndef BUILDING_VARIANT | |
1f2f436a | 58 | +__private_extern__ SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist); |
34e8f829 A |
59 | +__private_extern__ pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER; |
60 | +#else /* BUILDING_VARIANT */ | |
1f2f436a | 61 | +extern SLIST_HEAD(, pid) pidlist; |
34e8f829 A |
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) | |
1f2f436a | 67 | @@ -71,84 +97,108 @@ popen(command, type) |
34e8f829 A |
68 | { |
69 | struct pid *cur; | |
70 | FILE *iop; | |
71 | - int pdes[2], pid, twoway; | |
72 | + int pdes[2], pid, twoway, other; | |
59e0d9fe A |
73 | char *argv[4]; |
74 | struct pid *p; | |
34e8f829 A |
75 | + posix_spawn_file_actions_t file_actions; |
76 | + int err; | |
59e0d9fe A |
77 | |
78 | - /* | |
79 | - * Lite2 introduced two-way popen() pipes using _socketpair(). | |
80 | - * FreeBSD's pipe() is bidirectional, so we use that. | |
81 | - */ | |
224c7076 A |
82 | - if (strchr(type, '+')) { |
83 | + if (type == NULL) { | |
84 | + errno = EINVAL; | |
85 | + return (NULL); | |
86 | + } | |
87 | + if (strcmp(type, "r+") == 0) { | |
59e0d9fe A |
88 | twoway = 1; |
89 | type = "r+"; | |
90 | + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pdes) < 0) | |
91 | + return (NULL); | |
92 | } else { | |
93 | twoway = 0; | |
224c7076 A |
94 | - if ((*type != 'r' && *type != 'w') || type[1]) |
95 | + if ((*type != 'r' && *type != 'w') || type[1]) { | |
96 | + errno = EINVAL; | |
34e8f829 A |
97 | + return (NULL); |
98 | + } | |
99 | + if (pipe(pdes) < 0) | |
59e0d9fe | 100 | return (NULL); |
59e0d9fe | 101 | } |
34e8f829 A |
102 | - if (pipe(pdes) < 0) |
103 | - return (NULL); | |
59e0d9fe | 104 | |
34e8f829 A |
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) { | |
59e0d9fe | 115 | (void)_close(pdes[0]); |
34e8f829 A |
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 | + } | |
1f2f436a | 157 | + SLIST_FOREACH(p, &pidlist, next) |
34e8f829 | 158 | + (void)posix_spawn_file_actions_addclose(&file_actions, p->fd); |
34e8f829 A |
159 | + |
160 | argv[0] = "sh"; | |
161 | argv[1] = "-c"; | |
162 | argv[2] = (char *)command; | |
eb1cde05 A |
163 | argv[3] = NULL; |
164 | ||
34e8f829 | 165 | - THREAD_LOCK(); |
eb1cde05 | 166 | - switch (pid = vfork()) { |
34e8f829 A |
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 | - } | |
1f2f436a | 206 | - SLIST_FOREACH(p, &pidlist, next) |
59e0d9fe | 207 | - (void)_close(fileno(p->fp)); |
34e8f829 A |
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. */ | |
59e0d9fe | 217 | if (*type == 'r') { |
34e8f829 | 218 | - iop = fdopen(pdes[0], type); |
59e0d9fe A |
219 | + cur->fd = pdes[0]; |
220 | (void)_close(pdes[1]); | |
221 | } else { | |
34e8f829 | 222 | - iop = fdopen(pdes[1], type); |
59e0d9fe A |
223 | + cur->fd = pdes[1]; |
224 | (void)_close(pdes[0]); | |
225 | } | |
226 | ||
1f2f436a A |
227 | @@ -158,10 +208,11 @@ popen(command, type) |
228 | THREAD_LOCK(); | |
229 | SLIST_INSERT_HEAD(&pidlist, cur, next); | |
224c7076 A |
230 | THREAD_UNLOCK(); |
231 | - | |
232 | + fwide(iop, -1); /* byte stream */ | |
233 | return (iop); | |
234 | } | |
235 | ||
34e8f829 A |
236 | +#ifndef BUILDING_VARIANT |
237 | /* | |
238 | * pclose -- | |
239 | * Pclose returns -1 if stream is not associated with a `popened' command, | |
1f2f436a | 240 | @@ -196,6 +247,10 @@ pclose(iop) |
34e8f829 A |
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); | |
1f2f436a | 251 | @@ -204,3 +259,4 @@ pclose(iop) |
34e8f829 A |
252 | |
253 | return (pid == -1 ? -1 : pstat); | |
254 | } | |
255 | +#endif /* !BUILDING_VARIANT */ |