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