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: 41.4% 53 / 34 / 162

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