dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 47.1% 104 / 1 / 222
Functions: 63.6% 7 / 0 / 11
Branches: 40.8% 53 / 34 / 164

src/spawn.c
Line Branch Exec Source
1 #include <errno.h>
2 #include <poll.h>
3 #include <stddef.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include "spawn.h"
8 #include "command/error.h"
9 #include "regexp.h"
10 #include "util/debug.h"
11 #include "util/fd.h"
12 #include "util/fork-exec.h"
13 #include "util/log.h"
14 #include "util/str-util.h"
15 #include "util/strtonum.h"
16 #include "util/xmalloc.h"
17 #include "util/xreadwrite.h"
18 #include "util/xstdio.h"
19
20 enum {
21 IN = STDIN_FILENO,
22 OUT = STDOUT_FILENO,
23 ERR = STDERR_FILENO,
24 };
25
26 static void handle_error_msg (
27 const Compiler *c,
28 MessageList *msgs,
29 char *str,
30 size_t str_len
31 ) {
32 if (str_len == 0 || str[0] == '\n') {
33 return;
34 }
35
36 strn_replace_byte(str, str_len, '\t', ' ');
37 if (str[str_len - 1] == '\n') {
38 str[--str_len] = '\0';
39 }
40
41 for (size_t i = 0, n = c->error_formats.count; i < n; i++) {
42 const ErrorFormat *p = c->error_formats.ptrs[i];
43 regmatch_t m[ERRORFMT_CAPTURE_MAX];
44 if (!regexp_exec(&p->re, str, str_len, ARRAYLEN(m), m, 0)) {
45 continue;
46 }
47 if (p->ignore) {
48 return;
49 }
50
51 int mi = (int)p->capture_index[ERRFMT_MESSAGE];
52 if (m[mi].rm_so < 0) {
53 mi = 0;
54 }
55
56 Message *msg = new_message(str + m[mi].rm_so, m[mi].rm_eo - m[mi].rm_so);
57 msg->loc = new_file_location(NULL, 0, 0, 0);
58
59 int fi = (int)p->capture_index[ERRFMT_FILE];
60 if (fi >= 0 && m[fi].rm_so >= 0) {
61 msg->loc->filename = xstrslice(str, m[fi].rm_so, m[fi].rm_eo);
62
63 unsigned long *const ptrs[] = {
64 [ERRFMT_LINE] = &msg->loc->line,
65 [ERRFMT_COLUMN] = &msg->loc->column,
66 };
67
68 static_assert(ARRAYLEN(ptrs) == 3);
69 static_assert(ERRFMT_LINE == 1);
70 static_assert(ERRFMT_COLUMN == 2);
71
72 for (size_t j = ERRFMT_LINE; j < ARRAYLEN(ptrs); j++) {
73 int ci = (int)p->capture_index[j];
74 if (ci >= 0 && m[ci].rm_so >= 0) {
75 size_t len = m[ci].rm_eo - m[ci].rm_so;
76 unsigned long val;
77 if (len == buf_parse_ulong(str + m[ci].rm_so, len, &val)) {
78 *ptrs[j] = val;
79 }
80 }
81 }
82 }
83
84 add_message(msgs, msg);
85 return;
86 }
87
88 add_message(msgs, new_message(str, str_len));
89 }
90
91 static void read_errors(const Compiler *c, MessageList *msgs, int fd, bool quiet)
92 {
93 FILE *f = fdopen(fd, "r");
94 if (unlikely(!f)) {
95 return;
96 }
97
98 // TODO: Use POSIX 2008 getline(3) instead of xfgets()
99 char line[4096];
100 while (xfgets(line, sizeof(line), f)) {
101 size_t line_len = strlen(line);
102 if (!quiet) {
103 size_t bytes_written = xfwrite_all(line, line_len, stderr);
104 if (unlikely(bytes_written != line_len)) {
105 // This can happen even when just interrupting the child,
106 // e.g. with `compile gcc make` followed by Ctrl+C
107 LOG_ERRNO("fwrite() failed; setting quiet=true");
108 quiet = true; // Don't try to write any more lines
109 }
110 }
111 handle_error_msg(c, msgs, line, line_len);
112 }
113
114 fclose(f);
115 }
116
117 11 static void handle_piped_data(int f[3], SpawnContext *ctx)
118 {
119 11 BUG_ON(f[IN] < 0 && f[OUT] < 0 && f[ERR] < 0);
120 11 BUG_ON(IS_STD_FD(f[IN]));
121 11 BUG_ON(IS_STD_FD(f[OUT]));
122 11 BUG_ON(IS_STD_FD(f[ERR]));
123
124
2/2
✓ Branch 12 → 13 taken 5 times.
✓ Branch 12 → 16 taken 6 times.
11 if (ctx->input.length == 0) {
125 5 xclose(f[IN]);
126 5 f[IN] = -1;
127
4/4
✓ Branch 14 → 15 taken 2 times.
✓ Branch 14 → 16 taken 3 times.
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 63 taken 1 time.
5 if (f[OUT] < 0 && f[ERR] < 0) {
128 return;
129 }
130 }
131
132 10 struct pollfd fds[] = {
133 10 {.fd = f[IN], .events = POLLOUT},
134 {.fd = f[OUT], .events = POLLIN},
135 {.fd = f[ERR], .events = POLLIN},
136 };
137
138 10 size_t wlen = 0;
139 25 while (1) {
140
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 38 taken 25 times.
25 if (unlikely(poll(fds, ARRAYLEN(fds), -1) < 0)) {
141 if (errno == EINTR) {
142 continue;
143 }
144 error_msg_errno(ctx->ebuf, "poll");
145 return;
146 }
147
148
2/2
✓ Branch 38 → 24 taken 50 times.
✓ Branch 38 → 39 taken 25 times.
75 for (size_t i = 0; i < ARRAYLEN(ctx->outputs); i++) {
149 50 struct pollfd *pfd = fds + i + 1;
150
2/2
✓ Branch 24 → 25 taken 11 times.
✓ Branch 24 → 37 taken 39 times.
50 if (pfd->revents & POLLIN) {
151 11 String *output = &ctx->outputs[i];
152 11 char *buf = string_reserve_space(output, 4096);
153 11 ssize_t rc = xread(pfd->fd, buf, output->alloc - output->len);
154
1/2
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 30 taken 11 times.
11 if (unlikely(rc < 0)) {
155 error_msg_errno(ctx->ebuf, "read");
156 return;
157 }
158
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 36 taken 11 times.
11 if (rc == 0) { // EOF
159 if (xclose(pfd->fd)) {
160 error_msg_errno(ctx->ebuf, "close");
161 return;
162 }
163 pfd->fd = -1;
164 continue;
165 }
166 11 output->len += rc;
167 }
168 }
169
170
2/2
✓ Branch 39 → 40 taken 4 times.
✓ Branch 39 → 50 taken 21 times.
25 if (fds[IN].revents & POLLOUT) {
171 4 ssize_t rc = xwrite(fds[IN].fd, ctx->input.data + wlen, ctx->input.length - wlen);
172
1/2
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 44 taken 4 times.
4 if (unlikely(rc < 0)) {
173 error_msg_errno(ctx->ebuf, "write");
174 return;
175 }
176 4 wlen += (size_t) rc;
177
1/2
✓ Branch 44 → 45 taken 4 times.
✗ Branch 44 → 50 not taken.
4 if (wlen == ctx->input.length) {
178
1/2
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 49 taken 4 times.
4 if (xclose(fds[IN].fd)) {
179 error_msg_errno(ctx->ebuf, "close");
180 return;
181 }
182 4 fds[IN].fd = -1;
183 }
184 }
185
186 25 size_t active_fds = ARRAYLEN(fds);
187
2/2
✓ Branch 61 → 51 taken 75 times.
✓ Branch 61 → 62 taken 25 times.
100 for (size_t i = 0; i < ARRAYLEN(fds); i++) {
188 75 int rev = fds[i].revents;
189
3/4
✓ Branch 51 → 52 taken 38 times.
✓ Branch 51 → 53 taken 37 times.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 54 taken 38 times.
75 if (fds[i].fd < 0 || rev & POLLNVAL) {
190 37 fds[i].fd = -1;
191 37 active_fds--;
192 37 continue;
193 }
194
3/4
✓ Branch 54 → 55 taken 38 times.
✗ Branch 54 → 56 not taken.
✓ Branch 55 → 56 taken 16 times.
✓ Branch 55 → 60 taken 22 times.
38 if (rev & POLLERR || (rev & (POLLHUP | POLLIN)) == POLLHUP) {
195
1/2
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 59 taken 16 times.
16 if (xclose(fds[i].fd)) {
196 error_msg_errno(ctx->ebuf, "close");
197 }
198 16 fds[i].fd = -1;
199 16 active_fds--;
200 }
201 }
202
2/2
✓ Branch 62 → 17 taken 15 times.
✓ Branch 62 → 63 taken 10 times.
25 if (active_fds == 0) {
203 return;
204 }
205 }
206 }
207
208 36 static int open_dev_null(ErrorBuffer *ebuf, int flags)
209 {
210 36 int fd = xopen("/dev/null", flags | O_CLOEXEC, 0);
211
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 36 times.
36 if (unlikely(fd < 0)) {
212 error_msg_errno(ebuf, "Error opening /dev/null");
213 }
214
215 // Prevented by init_std_fds() and relied upon by fork_exec()
216 36 BUG_ON(fd <= STDERR_FILENO);
217
218 36 return fd;
219 }
220
221 21 static bool open_pipe(ErrorBuffer *ebuf, int fds[2])
222 {
223
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 21 times.
21 if (unlikely(xpipe2(fds, O_CLOEXEC) != 0)) {
224 return error_msg_errno(ebuf, "xpipe2");
225 }
226
227 // Prevented by init_std_fds() and relied upon by fork_exec()
228 21 BUG_ON(fds[0] <= STDERR_FILENO);
229 21 BUG_ON(fds[1] <= STDERR_FILENO);
230
231 return true;
232 }
233
234 static int handle_child_error(ErrorBuffer *ebuf, pid_t pid)
235 {
236 int ret = wait_child(pid);
237 if (ret < 0) {
238 error_msg_errno(ebuf, "waitpid");
239 } else if (ret >= 256) {
240 int sig = ret >> 8;
241 const char *str = strsignal(sig);
242 error_msg(ebuf, "Child received signal %d (%s)", sig, str ? str : "??");
243 } else if (ret) {
244 error_msg(ebuf, "Child returned %d", ret);
245 }
246 return ret;
247 }
248
249 1 static void exec_error(SpawnContext *ctx)
250 {
251 1 error_msg(ctx->ebuf, "Unable to exec '%s': %s", ctx->argv[0], strerror(errno));
252 1 }
253
254 bool spawn_compiler(SpawnContext *ctx, const Compiler *c, MessageList *msgs, bool read_stdout)
255 {
256 BUG_ON(!ctx->argv);
257 BUG_ON(!ctx->argv[0]);
258
259 int fd[3];
260 fd[IN] = open_dev_null(ctx->ebuf, O_RDONLY);
261 if (fd[IN] < 0) {
262 return false;
263 }
264
265 int dev_null = open_dev_null(ctx->ebuf, O_WRONLY);
266 if (dev_null < 0) {
267 xclose(fd[IN]);
268 return false;
269 }
270
271 int p[2];
272 if (!open_pipe(ctx->ebuf, p)) {
273 xclose(dev_null);
274 xclose(fd[IN]);
275 return false;
276 }
277
278 bool quiet = ctx->quiet;
279 if (read_stdout) {
280 fd[OUT] = p[1];
281 fd[ERR] = quiet ? dev_null : ERR;
282 } else {
283 fd[OUT] = quiet ? dev_null : OUT;
284 fd[ERR] = p[1];
285 }
286
287 pid_t pid = fork_exec(ctx->argv, fd, ctx->lines, ctx->columns, quiet);
288
289 // Note that the write end of the pipe must be closed before
290 // read_errors() is called, otherwise the read end never gets EOF
291 xclose(p[1]);
292
293 if (pid == -1) {
294 exec_error(ctx);
295 } else {
296 read_errors(c, msgs, p[0], quiet);
297 handle_child_error(ctx->ebuf, pid);
298 }
299
300 xclose(p[0]);
301 xclose(dev_null);
302 xclose(fd[IN]);
303 return (pid != -1);
304 }
305
306 // Close each fd only if valid (positive) and not stdin/stdout/stderr
307 86 static void safe_xclose_all(int fds[], size_t nr_fds)
308 {
309
2/2
✓ Branch 6 → 3 taken 234 times.
✓ Branch 6 → 7 taken 86 times.
320 for (size_t i = 0; i < nr_fds; i++) {
310
2/2
✓ Branch 3 → 4 taken 77 times.
✓ Branch 3 → 5 taken 157 times.
234 if (fds[i] > STDERR_FILENO) {
311 77 xclose(fds[i]);
312 }
313 // Also prevent use-after-close errors
314 234 fds[i] = -1;
315 }
316 86 }
317
318 24 UNITTEST {
319 24 int fds[] = {-2, -3, -4};
320 24 safe_xclose_all(fds, 2);
321 24 BUG_ON(fds[0] != -1);
322 24 BUG_ON(fds[1] != -1);
323 24 BUG_ON(fds[2] != -4);
324 24 safe_xclose_all(fds, 3);
325 24 BUG_ON(fds[2] != -1);
326 24 }
327
328 19 int spawn(SpawnContext *ctx)
329 {
330 19 BUG_ON(!ctx->argv);
331 19 BUG_ON(!ctx->argv[0]);
332
333 19 int child_fds[3] = {-1, -1, -1};
334 19 int parent_fds[3] = {-1, -1, -1};
335 19 bool quiet = ctx->quiet;
336 19 size_t nr_pipes = 0;
337
338
2/2
✓ Branch 30 → 7 taken 57 times.
✓ Branch 30 → 31 taken 19 times.
76 for (size_t i = 0; i < ARRAYLEN(child_fds); i++) {
339
3/4
✓ Branch 7 → 8 taken 30 times.
✓ Branch 7 → 10 taken 6 times.
✓ Branch 7 → 13 taken 21 times.
✗ Branch 7 → 28 not taken.
57 switch (ctx->actions[i]) {
340 30 case SPAWN_TTY:
341
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 30 times.
30 if (!quiet) {
342 child_fds[i] = i;
343 break;
344 }
345 // Fallthrough
346 case SPAWN_NULL:
347 36 child_fds[i] = open_dev_null(ctx->ebuf, O_RDWR);
348
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 29 taken 36 times.
36 if (child_fds[i] < 0) {
349 goto error;
350 }
351 break;
352 21 case SPAWN_PIPE: {
353 21 int p[2];
354
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 21 times.
21 if (!open_pipe(ctx->ebuf, p)) {
355 goto error;
356 }
357
2/2
✓ Branch 16 → 17 taken 16 times.
✓ Branch 16 → 18 taken 5 times.
21 child_fds[i] = i ? p[1] : p[0];
358
2/2
✓ Branch 19 → 20 taken 16 times.
✓ Branch 19 → 21 taken 5 times.
21 parent_fds[i] = i ? p[0] : p[1];
359
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 26 taken 21 times.
21 if (!fd_set_nonblock(parent_fds[i], true)) {
360 error_msg_errno(ctx->ebuf, "fcntl");
361 goto error;
362 }
363 21 nr_pipes++;
364 21 break;
365 }
366 default:
367 BUG("unhandled action type");
368 goto error;
369 }
370 }
371
372 19 pid_t pid = fork_exec(ctx->argv, child_fds, ctx->lines, ctx->columns, quiet);
373
2/2
✓ Branch 32 → 33 taken 1 time.
✓ Branch 32 → 35 taken 18 times.
19 if (pid == -1) {
374 1 exec_error(ctx);
375 1 goto error;
376 }
377
378 18 safe_xclose_all(child_fds, ARRAYLEN(child_fds));
379
2/2
✓ Branch 36 → 37 taken 11 times.
✓ Branch 36 → 38 taken 7 times.
18 if (nr_pipes > 0) {
380 11 handle_piped_data(parent_fds, ctx);
381 }
382
383 18 safe_xclose_all(parent_fds, ARRAYLEN(parent_fds));
384 18 int err = wait_child(pid);
385
1/2
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 45 taken 18 times.
18 if (err < 0) {
386 error_msg_errno(ctx->ebuf, "waitpid");
387 }
388
389 return err;
390
391 1 error:
392 1 safe_xclose_all(child_fds, ARRAYLEN(child_fds));
393 1 safe_xclose_all(parent_fds, ARRAYLEN(parent_fds));
394 1 return -1;
395 }
396