]>
Commit | Line | Data |
---|---|---|
04277e02 | 1 | /* Copyright (C) 1993-2019 Free Software Foundation, Inc. |
41bdb6e2 | 2 | This file is part of the GNU C Library. |
40a55d20 UD |
3 | Written by Per Bothner <bothner@cygnus.com>. |
4 | ||
41bdb6e2 AJ |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
40a55d20 | 9 | |
41bdb6e2 AJ |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
40a55d20 | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
41bdb6e2 | 13 | Lesser General Public License for more details. |
40a55d20 | 14 | |
41bdb6e2 | 15 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 PE |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. | |
40a55d20 | 18 | |
41bdb6e2 AJ |
19 | As a special exception, if you link the code in this file with |
20 | files compiled with a GNU compiler to produce an executable, | |
21 | that does not cause the resulting executable to be covered by | |
22 | the GNU Lesser General Public License. This exception does not | |
23 | however invalidate any other reasons why the executable file | |
24 | might be covered by the GNU Lesser General Public License. | |
25 | This exception applies to code released by its copyright holders | |
26 | in files containing the exception. */ | |
196980f5 | 27 | |
196980f5 | 28 | #include "libioP.h" |
8ae4bb5a | 29 | #include <fcntl.h> |
196980f5 RM |
30 | #include <signal.h> |
31 | #include <unistd.h> | |
196980f5 | 32 | #include <stdlib.h> |
5f0704b6 FW |
33 | #include <shlib-compat.h> |
34 | #include <not-cancel.h> | |
196980f5 RM |
35 | #include <sys/types.h> |
36 | #include <sys/wait.h> | |
14d0e87d AZ |
37 | #include <spawn.h> |
38 | #include <paths.h> | |
196980f5 | 39 | |
196980f5 RM |
40 | struct _IO_proc_file |
41 | { | |
bd355af0 | 42 | struct _IO_FILE_plus file; |
196980f5 | 43 | /* Following fields must match those in class procbuf (procbuf.h) */ |
9964a145 | 44 | pid_t pid; |
196980f5 RM |
45 | struct _IO_proc_file *next; |
46 | }; | |
47 | typedef struct _IO_proc_file _IO_proc_file; | |
48 | ||
b2637a22 | 49 | static const struct _IO_jump_t _IO_proc_jumps; |
d64b6ad0 | 50 | |
390500b1 | 51 | static struct _IO_proc_file *proc_file_chain; |
196980f5 | 52 | |
34183f57 UD |
53 | #ifdef _IO_MTSAFE_IO |
54 | static _IO_lock_t proc_file_chain_lock = _IO_lock_initializer; | |
55 | ||
56 | static void | |
57 | unlock (void *not_used) | |
58 | { | |
59 | _IO_lock_unlock (proc_file_chain_lock); | |
60 | } | |
61 | #endif | |
62 | ||
14d0e87d AZ |
63 | /* POSIX states popen shall ensure that any streams from previous popen() |
64 | calls that remain open in the parent process should be closed in the new | |
65 | child process. | |
66 | To avoid a race-condition between checking which file descriptors need to | |
67 | be close (by transversing the proc_file_chain list) and the insertion of a | |
68 | new one after a successful posix_spawn this function should be called | |
69 | with proc_file_chain_lock acquired. */ | |
70 | static bool | |
71 | spawn_process (posix_spawn_file_actions_t *fa, FILE *fp, const char *command, | |
72 | int do_cloexec, int pipe_fds[2], int parent_end, int child_end, | |
73 | int child_pipe_fd) | |
74 | { | |
75 | ||
76 | for (struct _IO_proc_file *p = proc_file_chain; p; p = p->next) | |
77 | { | |
78 | int fd = _IO_fileno ((FILE *) p); | |
79 | ||
80 | /* If any stream from previous popen() calls has fileno | |
81 | child_pipe_fd, it has been already closed by the adddup2 action | |
82 | above. */ | |
83 | if (fd != child_pipe_fd | |
84 | && __posix_spawn_file_actions_addclose (fa, fd) != 0) | |
85 | return false; | |
86 | } | |
87 | ||
88 | if (__posix_spawn (&((_IO_proc_file *) fp)->pid, _PATH_BSHELL, fa, 0, | |
89 | (char *const[]){ (char*) "sh", (char*) "-c", | |
90 | (char *) command, NULL }, __environ) != 0) | |
91 | return false; | |
92 | ||
93 | __close_nocancel (pipe_fds[child_end]); | |
94 | ||
95 | if (!do_cloexec) | |
96 | /* Undo the effects of the pipe2 call which set the | |
97 | close-on-exec flag. */ | |
98 | __fcntl (pipe_fds[parent_end], F_SETFD, 0); | |
99 | ||
100 | _IO_fileno (fp) = pipe_fds[parent_end]; | |
101 | ||
102 | ((_IO_proc_file *) fp)->next = proc_file_chain; | |
103 | proc_file_chain = (_IO_proc_file *) fp; | |
104 | ||
105 | return true; | |
106 | } | |
107 | ||
9964a145 ZW |
108 | FILE * |
109 | _IO_new_proc_open (FILE *fp, const char *command, const char *mode) | |
196980f5 | 110 | { |
372aece0 | 111 | int read_or_write; |
14d0e87d | 112 | /* These are indexes for pipe_fds. */ |
372aece0 | 113 | int parent_end, child_end; |
196980f5 | 114 | int pipe_fds[2]; |
14d0e87d AZ |
115 | int child_pipe_fd; |
116 | bool spawn_ok; | |
d6e0c2a6 UD |
117 | |
118 | int do_read = 0; | |
119 | int do_write = 0; | |
120 | int do_cloexec = 0; | |
121 | while (*mode != '\0') | |
122 | switch (*mode++) | |
123 | { | |
124 | case 'r': | |
125 | do_read = 1; | |
126 | break; | |
127 | case 'w': | |
128 | do_write = 1; | |
129 | break; | |
130 | case 'e': | |
131 | do_cloexec = 1; | |
132 | break; | |
133 | default: | |
134 | errout: | |
135 | __set_errno (EINVAL); | |
136 | return NULL; | |
137 | } | |
138 | ||
139 | if ((do_read ^ do_write) == 0) | |
140 | goto errout; | |
141 | ||
40a55d20 | 142 | if (_IO_file_is_open (fp)) |
196980f5 | 143 | return NULL; |
d6e0c2a6 | 144 | |
46d8874d FW |
145 | /* Atomically set the O_CLOEXEC flag for the pipe end used by the |
146 | child process (to avoid leaking the file descriptor in case of a | |
147 | concurrent fork). This is later reverted in the child process. | |
148 | When popen returns, the parent pipe end can be O_CLOEXEC or not, | |
149 | depending on the 'e' open mode, but there is only one flag which | |
150 | controls both descriptors. The parent end is adjusted below, | |
151 | after creating the child process. (In the child process, the | |
152 | parent end should be closed on execve, so O_CLOEXEC remains set | |
153 | there.) */ | |
154 | if (__pipe2 (pipe_fds, O_CLOEXEC) < 0) | |
155 | return NULL; | |
d6e0c2a6 UD |
156 | |
157 | if (do_read) | |
196980f5 | 158 | { |
14d0e87d AZ |
159 | parent_end = 0; |
160 | child_end = 1; | |
196980f5 | 161 | read_or_write = _IO_NO_WRITES; |
14d0e87d | 162 | child_pipe_fd = 1; |
196980f5 | 163 | } |
d6e0c2a6 | 164 | else |
196980f5 | 165 | { |
14d0e87d AZ |
166 | parent_end = 1; |
167 | child_end = 0; | |
196980f5 | 168 | read_or_write = _IO_NO_READS; |
14d0e87d | 169 | child_pipe_fd = 0; |
196980f5 | 170 | } |
d6e0c2a6 | 171 | |
14d0e87d AZ |
172 | posix_spawn_file_actions_t fa; |
173 | /* posix_spawn_file_actions_init does not fail. */ | |
174 | __posix_spawn_file_actions_init (&fa); | |
175 | ||
176 | /* The descriptor is already the one the child will use. In this case | |
177 | it must be moved to another one otherwise, there is no safe way to | |
178 | remove the close-on-exec flag in the child without creating a FD leak | |
179 | race in the parent. */ | |
180 | if (pipe_fds[child_end] == child_pipe_fd) | |
196980f5 | 181 | { |
14d0e87d AZ |
182 | int tmp = __fcntl (child_pipe_fd, F_DUPFD_CLOEXEC, 0); |
183 | if (tmp < 0) | |
184 | goto spawn_failure; | |
185 | __close_nocancel (pipe_fds[child_end]); | |
186 | pipe_fds[child_end] = tmp; | |
196980f5 | 187 | } |
d6e0c2a6 | 188 | |
14d0e87d AZ |
189 | if (__posix_spawn_file_actions_adddup2 (&fa, pipe_fds[child_end], |
190 | child_pipe_fd) != 0) | |
191 | goto spawn_failure; | |
196980f5 | 192 | |
9ec9864b | 193 | #ifdef _IO_MTSAFE_IO |
34183f57 UD |
194 | _IO_cleanup_region_start_noarg (unlock); |
195 | _IO_lock_lock (proc_file_chain_lock); | |
196 | #endif | |
14d0e87d AZ |
197 | spawn_ok = spawn_process (&fa, fp, command, do_cloexec, pipe_fds, |
198 | parent_end, child_end, child_pipe_fd); | |
9ec9864b | 199 | #ifdef _IO_MTSAFE_IO |
34183f57 UD |
200 | _IO_lock_unlock (proc_file_chain_lock); |
201 | _IO_cleanup_region_end (0); | |
202 | #endif | |
196980f5 | 203 | |
14d0e87d AZ |
204 | __posix_spawn_file_actions_destroy (&fa); |
205 | ||
206 | if (!spawn_ok) | |
207 | { | |
208 | spawn_failure: | |
209 | __close_nocancel (pipe_fds[child_end]); | |
210 | __close_nocancel (pipe_fds[parent_end]); | |
211 | __set_errno (ENOMEM); | |
212 | return NULL; | |
213 | } | |
214 | ||
196980f5 RM |
215 | _IO_mask_flags (fp, read_or_write, _IO_NO_READS|_IO_NO_WRITES); |
216 | return fp; | |
196980f5 RM |
217 | } |
218 | ||
9964a145 | 219 | FILE * |
9d46370c | 220 | _IO_new_popen (const char *command, const char *mode) |
196980f5 | 221 | { |
edf5b2d7 UD |
222 | struct locked_FILE |
223 | { | |
224 | struct _IO_proc_file fpx; | |
499e7464 | 225 | #ifdef _IO_MTSAFE_IO |
edf5b2d7 | 226 | _IO_lock_t lock; |
499e7464 | 227 | #endif |
edf5b2d7 | 228 | } *new_f; |
9964a145 | 229 | FILE *fp; |
edf5b2d7 UD |
230 | |
231 | new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE)); | |
232 | if (new_f == NULL) | |
196980f5 | 233 | return NULL; |
499e7464 | 234 | #ifdef _IO_MTSAFE_IO |
bd355af0 | 235 | new_f->fpx.file.file._lock = &new_f->lock; |
499e7464 | 236 | #endif |
7ce241a0 | 237 | fp = &new_f->fpx.file.file; |
db3476af | 238 | _IO_init_internal (fp, 0); |
2ca8b1ee | 239 | _IO_JUMPS (&new_f->fpx.file) = &_IO_proc_jumps; |
db3476af | 240 | _IO_new_file_init_internal (&new_f->fpx.file); |
7d6a8338 | 241 | if (_IO_new_proc_open (fp, command, mode) != NULL) |
9964a145 | 242 | return (FILE *) &new_f->fpx.file; |
d18ea0c5 | 243 | _IO_un_link (&new_f->fpx.file); |
edf5b2d7 | 244 | free (new_f); |
196980f5 RM |
245 | return NULL; |
246 | } | |
247 | ||
196980f5 | 248 | int |
9964a145 | 249 | _IO_new_proc_close (FILE *fp) |
196980f5 RM |
250 | { |
251 | /* This is not name-space clean. FIXME! */ | |
196980f5 RM |
252 | int wstatus; |
253 | _IO_proc_file **ptr = &proc_file_chain; | |
9964a145 | 254 | pid_t wait_pid; |
196980f5 RM |
255 | int status = -1; |
256 | ||
257 | /* Unlink from proc_file_chain. */ | |
9ec9864b | 258 | #ifdef _IO_MTSAFE_IO |
34183f57 UD |
259 | _IO_cleanup_region_start_noarg (unlock); |
260 | _IO_lock_lock (proc_file_chain_lock); | |
261 | #endif | |
196980f5 RM |
262 | for ( ; *ptr != NULL; ptr = &(*ptr)->next) |
263 | { | |
40a55d20 | 264 | if (*ptr == (_IO_proc_file *) fp) |
196980f5 RM |
265 | { |
266 | *ptr = (*ptr)->next; | |
267 | status = 0; | |
268 | break; | |
269 | } | |
270 | } | |
9ec9864b | 271 | #ifdef _IO_MTSAFE_IO |
34183f57 UD |
272 | _IO_lock_unlock (proc_file_chain_lock); |
273 | _IO_cleanup_region_end (0); | |
274 | #endif | |
196980f5 | 275 | |
5f0704b6 | 276 | if (status < 0 || __close_nocancel (_IO_fileno(fp)) < 0) |
196980f5 RM |
277 | return -1; |
278 | /* POSIX.2 Rationale: "Some historical implementations either block | |
279 | or ignore the signals SIGINT, SIGQUIT, and SIGHUP while waiting | |
280 | for the child process to terminate. Since this behavior is not | |
281 | described in POSIX.2, such implementations are not conforming." */ | |
282 | do | |
283 | { | |
5f0704b6 | 284 | wait_pid = __waitpid_nocancel (((_IO_proc_file *) fp)->pid, &wstatus, 0); |
40a55d20 UD |
285 | } |
286 | while (wait_pid == -1 && errno == EINTR); | |
196980f5 RM |
287 | if (wait_pid == -1) |
288 | return -1; | |
289 | return wstatus; | |
196980f5 RM |
290 | } |
291 | ||
db3476af | 292 | static const struct _IO_jump_t _IO_proc_jumps libio_vtable = { |
196980f5 | 293 | JUMP_INIT_DUMMY, |
7d6a8338 UD |
294 | JUMP_INIT(finish, _IO_new_file_finish), |
295 | JUMP_INIT(overflow, _IO_new_file_overflow), | |
296 | JUMP_INIT(underflow, _IO_new_file_underflow), | |
d18ea0c5 AS |
297 | JUMP_INIT(uflow, _IO_default_uflow), |
298 | JUMP_INIT(pbackfail, _IO_default_pbackfail), | |
d64b6ad0 | 299 | JUMP_INIT(xsputn, _IO_new_file_xsputn), |
d18ea0c5 | 300 | JUMP_INIT(xsgetn, _IO_default_xsgetn), |
d64b6ad0 | 301 | JUMP_INIT(seekoff, _IO_new_file_seekoff), |
196980f5 | 302 | JUMP_INIT(seekpos, _IO_default_seekpos), |
7d6a8338 UD |
303 | JUMP_INIT(setbuf, _IO_new_file_setbuf), |
304 | JUMP_INIT(sync, _IO_new_file_sync), | |
d18ea0c5 AS |
305 | JUMP_INIT(doallocate, _IO_file_doallocate), |
306 | JUMP_INIT(read, _IO_file_read), | |
7d6a8338 | 307 | JUMP_INIT(write, _IO_new_file_write), |
d18ea0c5 | 308 | JUMP_INIT(seek, _IO_file_seek), |
7d6a8338 | 309 | JUMP_INIT(close, _IO_new_proc_close), |
d18ea0c5 | 310 | JUMP_INIT(stat, _IO_file_stat), |
dfd2257a UD |
311 | JUMP_INIT(showmanyc, _IO_default_showmanyc), |
312 | JUMP_INIT(imbue, _IO_default_imbue) | |
196980f5 | 313 | }; |
7d6a8338 | 314 | |
7d6a8338 | 315 | strong_alias (_IO_new_popen, __new_popen) |
fd091d3f UD |
316 | versioned_symbol (libc, _IO_new_popen, _IO_popen, GLIBC_2_1); |
317 | versioned_symbol (libc, __new_popen, popen, GLIBC_2_1); | |
318 | versioned_symbol (libc, _IO_new_proc_open, _IO_proc_open, GLIBC_2_1); | |
319 | versioned_symbol (libc, _IO_new_proc_close, _IO_proc_close, GLIBC_2_1); |