rewrite popen to use posix_spawn instead of fragile vfork hacks
[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         FLOCK(f);
35
36         /* If the child's end of the pipe happens to already be on the final
37          * fd number to which it will be assigned (either 0 or 1), it must
38          * be moved to a different fd. Otherwise, there is no safe way to
39          * remove the close-on-exec flag in the child without also creating
40          * a file descriptor leak race condition in the parent. */
41         if (p[1-op] == 1-op) {
42                 int tmp = fcntl(F_DUPFD_CLOEXEC, 1-op, 0);
43                 if (tmp < 0) {
44                         e = errno;
45                         goto fail;
46                 }
47                 __syscall(SYS_close, p[1-op]);
48                 p[1-op] = tmp;
49         }
50
51         e = ENOMEM;
52         if (!posix_spawn_file_actions_init(&fa)) {
53                 if (!posix_spawn_file_actions_adddup2(&fa, p[1-op], 1-op)) {
54                         if (!(e = posix_spawn(&pid, "/bin/sh", &fa, 0,
55                             (char *[]){ "sh", "-c", (char *)cmd, 0 }, __environ))) {
56                                 posix_spawn_file_actions_destroy(&fa);
57                                 f->pipe_pid = pid;
58                                 if (!strchr(mode, 'e'))
59                                         fcntl(p[op], F_SETFD, 0);
60                                 __syscall(SYS_close, p[1-op]);
61                                 FUNLOCK(f);
62                                 return f;
63                         }
64                 }
65                 posix_spawn_file_actions_destroy(&fa);
66         }
67 fail:
68         fclose(f);
69         __syscall(SYS_close, p[1-op]);
70
71         errno = e;
72         return 0;
73 }