fix popen not to leak pipes from one child to another
[musl] / src / stdio / popen.c
1 #include <fcntl.h>
2 #include <unistd.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <spawn.h>
6 #include "stdio_impl.h"
7 #include "syscall.h"
8
9 extern char **__environ;
10
11 FILE *popen(const char *cmd, const char *mode)
12 {
13         int p[2], op, e;
14         pid_t pid;
15         FILE *f;
16         posix_spawn_file_actions_t fa;
17
18         if (*mode == 'r') {
19                 op = 0;
20         } else if (*mode == 'w') {
21                 op = 1;
22         } else {
23                 errno = EINVAL;
24                 return 0;
25         }
26         
27         if (pipe2(p, O_CLOEXEC)) return NULL;
28         f = fdopen(p[op], mode);
29         if (!f) {
30                 __syscall(SYS_close, p[0]);
31                 __syscall(SYS_close, p[1]);
32                 return NULL;
33         }
34
35         e = ENOMEM;
36         if (!posix_spawn_file_actions_init(&fa)) {
37                 for (FILE *l = *__ofl_lock(); l; l=l->next)
38                         if (l->pipe_pid && posix_spawn_file_actions_addclose(&fa, l->fd))
39                                 goto fail;
40                 if (!posix_spawn_file_actions_adddup2(&fa, p[1-op], 1-op)) {
41                         if (!(e = posix_spawn(&pid, "/bin/sh", &fa, 0,
42                             (char *[]){ "sh", "-c", (char *)cmd, 0 }, __environ))) {
43                                 posix_spawn_file_actions_destroy(&fa);
44                                 f->pipe_pid = pid;
45                                 if (!strchr(mode, 'e'))
46                                         fcntl(p[op], F_SETFD, 0);
47                                 __syscall(SYS_close, p[1-op]);
48                                 __ofl_unlock();
49                                 return f;
50                         }
51                 }
52 fail:
53                 __ofl_unlock();
54                 posix_spawn_file_actions_destroy(&fa);
55         }
56         fclose(f);
57         __syscall(SYS_close, p[1-op]);
58
59         errno = e;
60         return 0;
61 }