Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <errno.h> | ||
2 | #include <signal.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <sys/wait.h> | ||
5 | #include <unistd.h> | ||
6 | #include "fork-exec.h" | ||
7 | #include "debug.h" | ||
8 | #include "fd.h" | ||
9 | #include "log.h" | ||
10 | #include "terminal/ioctl.h" | ||
11 | #include "xreadwrite.h" | ||
12 | |||
13 | // Reset ignored signal dispositions (i.e. as originally set up by | ||
14 | // set_basic_signal_dispositions()) to SIG_DFL | ||
15 | 21 | static bool reset_ignored_signals(void) | |
16 | { | ||
17 | // Note that handled signals don't need to be restored here, since | ||
18 | // they're necessarily and automatically reset after exec(3p) | ||
19 | 21 | static const int ignored_signals[] = { | |
20 | SIGINT, SIGQUIT, SIGTSTP, | ||
21 | SIGTTIN, SIGTTOU, SIGXFSZ, | ||
22 | SIGPIPE, SIGUSR1, SIGUSR2, | ||
23 | }; | ||
24 | |||
25 | 21 | struct sigaction action = {.sa_handler = SIG_DFL}; | |
26 |
1/2✓ Branch 0 (3→7) taken 21 times.
✗ Branch 1 (3→8) not taken.
|
21 | if (unlikely(sigemptyset(&action.sa_mask) != 0)) { |
27 | return false; | ||
28 | } | ||
29 | |||
30 |
2/2✓ Branch 0 (7→4) taken 189 times.
✓ Branch 1 (7→8) taken 21 times.
|
210 | for (size_t i = 0; i < ARRAYLEN(ignored_signals); i++) { |
31 | 189 | int r = sigaction(ignored_signals[i], &action, NULL); | |
32 |
1/2✓ Branch 0 (5→6) taken 189 times.
✗ Branch 1 (5→8) not taken.
|
189 | if (unlikely(r != 0)) { |
33 | return false; | ||
34 | } | ||
35 | } | ||
36 | |||
37 | return true; | ||
38 | } | ||
39 | |||
40 | 21 | static noreturn void child_process_exec ( | |
41 | const char **argv, | ||
42 | const char **env, | ||
43 | int fd[3], | ||
44 | int error_fd, | ||
45 | bool drop_ctty | ||
46 | ) { | ||
47 | 21 | int error; | |
48 | 21 | int nr_fds = 3; | |
49 | 21 | bool move = error_fd < nr_fds; | |
50 | 21 | int max = error_fd; | |
51 | |||
52 |
1/2✓ Branch 0 (2→3) taken 21 times.
✗ Branch 1 (2→4) not taken.
|
21 | if (drop_ctty) { |
53 | 21 | term_drop_controlling_tty(STDIN_FILENO); | |
54 | } | ||
55 | |||
56 | // Find if we must move fds out of the way | ||
57 |
2/2✓ Branch 0 (10→5) taken 63 times.
✓ Branch 1 (10→11) taken 21 times.
|
84 | for (int i = 0; i < nr_fds; i++) { |
58 |
1/2✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 63 times.
|
63 | if (fd[i] > max) { |
59 | ✗ | max = fd[i]; | |
60 | } | ||
61 |
1/2✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→9) taken 63 times.
|
63 | if (fd[i] < i) { |
62 | ✗ | move = true; | |
63 | } | ||
64 | } | ||
65 | |||
66 |
1/2✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→23) taken 21 times.
|
21 | if (move) { |
67 | ✗ | int next_free = max + 1; | |
68 | ✗ | if (error_fd < nr_fds) { | |
69 | ✗ | error_fd = xdup3(error_fd, next_free++, O_CLOEXEC); | |
70 | ✗ | if (error_fd < 0) { | |
71 | ✗ | goto error; | |
72 | } | ||
73 | } | ||
74 | ✗ | for (int i = 0; i < nr_fds; i++) { | |
75 | ✗ | if (fd[i] < i) { | |
76 | ✗ | fd[i] = xdup3(fd[i], next_free++, O_CLOEXEC); | |
77 | ✗ | if (fd[i] < 0) { | |
78 | ✗ | goto error; | |
79 | } | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | |||
84 | // Now it is safe to duplicate fds in this order | ||
85 |
2/2✓ Branch 0 (32→24) taken 63 times.
✓ Branch 1 (32→33) taken 21 times.
|
84 | for (int i = 0; i < nr_fds; i++) { |
86 |
1/2✗ Branch 0 (24→25) not taken.
✓ Branch 1 (24→28) taken 63 times.
|
63 | if (i == fd[i]) { |
87 | // Clear FD_CLOEXEC flag | ||
88 | ✗ | if (!fd_set_cloexec(fd[i], false)) { | |
89 | ✗ | goto error; | |
90 | } | ||
91 | } else { | ||
92 |
1/2✗ Branch 0 (29→30) not taken.
✓ Branch 1 (29→31) taken 63 times.
|
63 | if (xdup3(fd[i], i, 0) < 0) { |
93 | ✗ | goto error; | |
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 |
2/2✓ Branch 0 (33→40) taken 5 times.
✓ Branch 1 (33→41) taken 16 times.
|
21 | if (env) { |
99 |
2/2✓ Branch 0 (40→34) taken 10 times.
✓ Branch 1 (40→41) taken 5 times.
|
15 | for (size_t i = 0; env[i]; i += 2) { |
100 | 10 | const char *name = env[i]; | |
101 | 10 | const char *value = env[i + 1]; | |
102 |
1/2✓ Branch 0 (34→35) taken 10 times.
✗ Branch 1 (34→36) not taken.
|
10 | int r = value ? setenv(name, value, 1) : unsetenv(name); |
103 |
1/2✗ Branch 0 (37→38) not taken.
✓ Branch 1 (37→39) taken 10 times.
|
10 | if (unlikely(r != 0)) { |
104 | ✗ | goto error; | |
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 |
1/2✗ Branch 0 (42→43) not taken.
✓ Branch 1 (42→44) taken 1 times.
|
21 | if (!reset_ignored_signals()) { |
110 | ✗ | goto error; | |
111 | } | ||
112 | |||
113 | 1 | execvp(argv[0], (char**)argv); | |
114 | |||
115 | 1 | error: | |
116 | 1 | error = errno; | |
117 | 1 | error = xwrite(error_fd, &error, sizeof(error)); | |
118 | 1 | exit(42); | |
119 | } | ||
120 | |||
121 | 21 | static pid_t xwaitpid(pid_t pid, int *status, int options) | |
122 | { | ||
123 | 21 | pid_t ret; | |
124 | 42 | do { | |
125 | 21 | ret = waitpid(pid, status, options); | |
126 |
1/4✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 21 times.
✗ Branch 2 (5→3) not taken.
✗ Branch 3 (5→6) not taken.
|
21 | } while (unlikely(ret < 0 && errno == EINTR)); |
127 | 21 | return ret; | |
128 | } | ||
129 | |||
130 | 21 | pid_t fork_exec(const char **argv, const char **env, int fd[3], bool drop_ctty) | |
131 | { | ||
132 | 21 | int ep[2]; | |
133 |
1/2✓ Branch 0 (3→4) taken 21 times.
✗ Branch 1 (3→23) not taken.
|
21 | if (xpipe2(ep, O_CLOEXEC) != 0) { |
134 | return -1; | ||
135 | } | ||
136 | |||
137 | 21 | const pid_t pid = fork(); | |
138 |
1/2✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→9) taken 42 times.
|
42 | if (unlikely(pid == -1)) { |
139 | ✗ | int saved_errno = errno; | |
140 | ✗ | xclose(ep[0]); | |
141 | ✗ | xclose(ep[1]); | |
142 | ✗ | errno = saved_errno; | |
143 | ✗ | return -1; | |
144 | } | ||
145 | |||
146 |
2/2✓ Branch 0 (9→10) taken 21 times.
✓ Branch 1 (9→11) taken 21 times.
|
42 | if (pid == 0) { |
147 | // Child | ||
148 | 21 | child_process_exec(argv, env, fd, ep[1], drop_ctty); | |
149 | BUG("child_process_exec() should never return"); | ||
150 | return -1; | ||
151 | } | ||
152 | |||
153 | // Parent | ||
154 | 21 | xclose(ep[1]); | |
155 | 21 | int error = 0; | |
156 | 21 | ssize_t rc = xread(ep[0], &error, sizeof(error)); | |
157 | 21 | int xread_errno = errno; | |
158 | 21 | xclose(ep[0]); | |
159 | 21 | BUG_ON(rc > sizeof(error)); | |
160 | |||
161 |
2/2✓ Branch 0 (16→17) taken 1 times.
✓ Branch 1 (16→23) taken 20 times.
|
21 | if (rc == 0) { |
162 | // Child exec was successful | ||
163 | return pid; | ||
164 | } | ||
165 | |||
166 |
1/2✗ Branch 0 (17→18) not taken.
✓ Branch 1 (17→21) taken 1 times.
|
1 | if (unlikely(rc != sizeof(error))) { |
167 | ✗ | error = (rc < 0) ? xread_errno : EPIPE; | |
168 | } | ||
169 | |||
170 | 1 | int status; | |
171 | 1 | xwaitpid(pid, &status, 0); | |
172 | 1 | errno = error; | |
173 | 1 | return -1; | |
174 | } | ||
175 | |||
176 | 20 | int wait_child(pid_t pid) | |
177 | { | ||
178 | 20 | int status; | |
179 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 20 times.
|
20 | if (unlikely(xwaitpid(pid, &status, 0) < 0)) { |
180 | ✗ | return -errno; | |
181 | } | ||
182 | |||
183 |
2/2✓ Branch 0 (5→6) taken 18 times.
✓ Branch 1 (5→7) taken 2 times.
|
20 | if (likely(WIFEXITED(status))) { |
184 | 18 | return WEXITSTATUS(status) & 0xFF; | |
185 | } | ||
186 | |||
187 |
1/2✓ Branch 0 (7→8) taken 2 times.
✗ Branch 1 (7→9) not taken.
|
2 | if (likely(WIFSIGNALED(status))) { |
188 | 2 | return WTERMSIG(status) << 8; | |
189 | } | ||
190 | |||
191 | ✗ | LOG_ERROR("unhandled waitpid() status: %d", status); | |
192 | ✗ | return -EINVAL; | |
193 | } | ||
194 |