dte test coverage


Directory: ./
File: src/spawn.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 103 219 47.0%
Functions: 7 11 63.6%
Branches: 52 124 41.9%

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