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 | 18 | 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 | 18 | static const int ignored_signals[] = { | |
20 | SIGINT, SIGQUIT, SIGTSTP, | ||
21 | SIGTTIN, SIGTTOU, SIGXFSZ, | ||
22 | SIGPIPE, SIGUSR1, SIGUSR2, | ||
23 | }; | ||
24 | |||
25 | 18 | struct sigaction action = {.sa_handler = SIG_DFL}; | |
26 |
1/2✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
|
18 | if (unlikely(sigemptyset(&action.sa_mask) != 0)) { |
27 | return false; | ||
28 | } | ||
29 | |||
30 |
2/2✓ Branch 0 taken 162 times.
✓ Branch 1 taken 18 times.
|
180 | for (size_t i = 0; i < ARRAYLEN(ignored_signals); i++) { |
31 | 162 | int r = sigaction(ignored_signals[i], &action, NULL); | |
32 |
1/2✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
|
162 | if (unlikely(r != 0)) { |
33 | return false; | ||
34 | } | ||
35 | } | ||
36 | |||
37 | return true; | ||
38 | } | ||
39 | |||
40 | 18 | 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 | 18 | int error; | |
48 | 18 | int nr_fds = 3; | |
49 | 18 | bool move = error_fd < nr_fds; | |
50 | 18 | int max = error_fd; | |
51 | |||
52 |
1/2✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
|
18 | if (drop_ctty) { |
53 | 18 | term_drop_controlling_tty(STDIN_FILENO); | |
54 | } | ||
55 | |||
56 | // Find if we must move fds out of the way | ||
57 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 18 times.
|
72 | for (int i = 0; i < nr_fds; i++) { |
58 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
|
54 | if (fd[i] > max) { |
59 | ✗ | max = fd[i]; | |
60 | } | ||
61 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
|
54 | if (fd[i] < i) { |
62 | ✗ | move = true; | |
63 | } | ||
64 | } | ||
65 | |||
66 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
18 | 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 taken 54 times.
✓ Branch 1 taken 18 times.
|
72 | for (int i = 0; i < nr_fds; i++) { |
86 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
|
54 | 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 not taken.
✓ Branch 1 taken 54 times.
|
54 | if (xdup3(fd[i], i, 0) < 0) { |
93 | ✗ | goto error; | |
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
|
18 | if (env) { |
99 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
|
6 | for (size_t i = 0; env[i]; i += 2) { |
100 | 4 | const char *name = env[i]; | |
101 | 4 | const char *value = env[i + 1]; | |
102 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | int r = value ? setenv(name, value, 1) : unsetenv(name); |
103 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (unlikely(r != 0)) { |
104 | ✗ | goto error; | |
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
18 | 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 | 18 | static pid_t xwaitpid(pid_t pid, int *status, int options) | |
122 | { | ||
123 | 18 | pid_t ret; | |
124 | 36 | do { | |
125 | 18 | ret = waitpid(pid, status, options); | |
126 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
18 | } while (unlikely(ret < 0 && errno == EINTR)); |
127 | 18 | return ret; | |
128 | } | ||
129 | |||
130 | 18 | pid_t fork_exec(const char **argv, const char **env, int fd[3], bool drop_ctty) | |
131 | { | ||
132 | 18 | int ep[2]; | |
133 |
1/2✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
|
18 | if (xpipe2(ep, O_CLOEXEC) != 0) { |
134 | return -1; | ||
135 | } | ||
136 | |||
137 | 18 | const pid_t pid = fork(); | |
138 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | 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 taken 18 times.
✓ Branch 1 taken 18 times.
|
36 | if (pid == 0) { |
147 | // Child | ||
148 | 18 | 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 | 18 | xclose(ep[1]); | |
155 | 18 | int error = 0; | |
156 | 18 | ssize_t rc = xread(ep[0], &error, sizeof(error)); | |
157 | 18 | int xread_errno = errno; | |
158 | 18 | xclose(ep[0]); | |
159 | 18 | BUG_ON(rc > sizeof(error)); | |
160 | |||
161 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
|
18 | if (rc == 0) { |
162 | // Child exec was successful | ||
163 | return pid; | ||
164 | } | ||
165 | |||
166 |
1/2✗ Branch 0 not taken.
✓ Branch 1 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 | 17 | int wait_child(pid_t pid) | |
177 | { | ||
178 | 17 | int status; | |
179 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (unlikely(xwaitpid(pid, &status, 0) < 0)) { |
180 | ✗ | return -errno; | |
181 | } | ||
182 | |||
183 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 2 times.
|
17 | if (likely(WIFEXITED(status))) { |
184 | 15 | return WEXITSTATUS(status) & 0xFF; | |
185 | } | ||
186 | |||
187 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 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 |