OLD | NEW |
1 #include <fcntl.h> | 1 #include <fcntl.h> |
2 #include <unistd.h> | 2 #include <unistd.h> |
3 #include <errno.h> | 3 #include <errno.h> |
4 #include <string.h> | 4 #include <string.h> |
5 #include <spawn.h> | 5 #include <spawn.h> |
6 #include "stdio_impl.h" | 6 #include "stdio_impl.h" |
7 #include "syscall.h" | 7 #include "syscall.h" |
8 | 8 |
9 extern char **__environ; | 9 extern char** __environ; |
10 | 10 |
11 FILE *popen(const char *cmd, const char *mode) | 11 FILE* popen(const char* cmd, const char* mode) { |
12 { | 12 int p[2], op, e; |
13 » int p[2], op, e; | 13 pid_t pid; |
14 » pid_t pid; | 14 FILE* f; |
15 » FILE *f; | 15 posix_spawn_file_actions_t fa; |
16 » posix_spawn_file_actions_t fa; | |
17 | 16 |
18 » if (*mode == 'r') { | 17 if (*mode == 'r') { |
19 » » op = 0; | 18 op = 0; |
20 » } else if (*mode == 'w') { | 19 } else if (*mode == 'w') { |
21 » » op = 1; | 20 op = 1; |
22 » } else { | 21 } else { |
23 » » errno = EINVAL; | 22 errno = EINVAL; |
24 » » return 0; | 23 return 0; |
25 » } | 24 } |
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 | 25 |
36 » /* If the child's end of the pipe happens to already be on the final | 26 if (pipe2(p, O_CLOEXEC)) |
37 » * fd number to which it will be assigned (either 0 or 1), it must | 27 return NULL; |
38 » * be moved to a different fd. Otherwise, there is no safe way to | 28 f = fdopen(p[op], mode); |
39 » * remove the close-on-exec flag in the child without also creating | 29 if (!f) { |
40 » * a file descriptor leak race condition in the parent. */ | 30 __syscall(SYS_close, p[0]); |
41 » if (p[1-op] == 1-op) { | 31 __syscall(SYS_close, p[1]); |
42 » » int tmp = fcntl(1-op, F_DUPFD_CLOEXEC, 0); | 32 return NULL; |
43 » » if (tmp < 0) { | 33 } |
44 » » » e = errno; | 34 FLOCK(f); |
45 » » » goto fail; | |
46 » » } | |
47 » » __syscall(SYS_close, p[1-op]); | |
48 » » p[1-op] = tmp; | |
49 » } | |
50 | 35 |
51 » e = ENOMEM; | 36 /* If the child's end of the pipe happens to already be on the final |
52 » if (!posix_spawn_file_actions_init(&fa)) { | 37 * fd number to which it will be assigned (either 0 or 1), it must |
53 » » if (!posix_spawn_file_actions_adddup2(&fa, p[1-op], 1-op)) { | 38 * be moved to a different fd. Otherwise, there is no safe way to |
54 » » » if (!(e = posix_spawn(&pid, "/bin/sh", &fa, 0, | 39 * remove the close-on-exec flag in the child without also creating |
55 » » » (char *[]){ "sh", "-c", (char *)cmd, 0 }, __environ)
)) { | 40 * a file descriptor leak race condition in the parent. */ |
56 » » » » posix_spawn_file_actions_destroy(&fa); | 41 if (p[1 - op] == 1 - op) { |
57 » » » » f->pipe_pid = pid; | 42 int tmp = fcntl(1 - op, F_DUPFD_CLOEXEC, 0); |
58 » » » » if (!strchr(mode, 'e')) | 43 if (tmp < 0) { |
59 » » » » » fcntl(p[op], F_SETFD, 0); | 44 e = errno; |
60 » » » » __syscall(SYS_close, p[1-op]); | 45 goto fail; |
61 » » » » FUNLOCK(f); | 46 } |
62 » » » » return f; | 47 __syscall(SYS_close, p[1 - op]); |
63 » » » } | 48 p[1 - op] = tmp; |
64 » » } | 49 } |
65 » » posix_spawn_file_actions_destroy(&fa); | 50 |
66 » } | 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}, |
| 56 __environ))) { |
| 57 posix_spawn_file_actions_destroy(&fa); |
| 58 f->pipe_pid = pid; |
| 59 if (!strchr(mode, 'e')) |
| 60 fcntl(p[op], F_SETFD, 0); |
| 61 __syscall(SYS_close, p[1 - op]); |
| 62 FUNLOCK(f); |
| 63 return f; |
| 64 } |
| 65 } |
| 66 posix_spawn_file_actions_destroy(&fa); |
| 67 } |
67 fail: | 68 fail: |
68 » fclose(f); | 69 fclose(f); |
69 » __syscall(SYS_close, p[1-op]); | 70 __syscall(SYS_close, p[1 - op]); |
70 | 71 |
71 » errno = e; | 72 errno = e; |
72 » return 0; | 73 return 0; |
73 } | 74 } |
OLD | NEW |