]>
Commit | Line | Data |
---|---|---|
e701e381 LDM |
1 | /* |
2 | * Copyright (C) 2012 ProFUSION embedded systems | |
3 | * | |
4 | * This program is free software: you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation, either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
80f9e023 LDM |
18 | #include <errno.h> |
19 | #include <fcntl.h> | |
20 | #include <getopt.h> | |
21 | #include <limits.h> | |
22 | #include <stdio.h> | |
23 | #include <stdarg.h> | |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
b8e344a6 | 26 | #include <time.h> |
80f9e023 | 27 | #include <unistd.h> |
3dbb8dea | 28 | #include <sys/epoll.h> |
80f9e023 | 29 | #include <sys/prctl.h> |
0de690c9 | 30 | #include <sys/stat.h> |
80f9e023 LDM |
31 | #include <sys/wait.h> |
32 | ||
0de690c9 | 33 | #include "libkmod-util.h" |
80f9e023 LDM |
34 | #include "testsuite.h" |
35 | ||
51b1d1ab LDM |
36 | static const char *ANSI_HIGHLIGHT_GREEN_ON = "\x1B[1;32m"; |
37 | static const char *ANSI_HIGHLIGHT_RED_ON = "\x1B[1;31m"; | |
38 | static const char *ANSI_HIGHLIGHT_OFF = "\x1B[0m"; | |
39 | ||
80f9e023 | 40 | static const char *progname; |
ed2df4e9 | 41 | static int oneshot = 0; |
80f9e023 LDM |
42 | static const char options_short[] = "lhn"; |
43 | static const struct option options[] = { | |
44 | { "list", no_argument, 0, 'l' }, | |
45 | { "help", no_argument, 0, 'h' }, | |
46 | { NULL, 0, 0, 0 } | |
47 | }; | |
48 | ||
d2c2b8b5 LDM |
49 | #define OVERRIDE_LIBDIR ABS_TOP_BUILDDIR "/testsuite/.libs/" |
50 | ||
395478cb LDM |
51 | struct _env_config { |
52 | const char *key; | |
53 | const char *ldpreload; | |
54 | } env_config[_TC_LAST] = { | |
d2c2b8b5 LDM |
55 | [TC_UNAME_R] = { S_TC_UNAME_R, OVERRIDE_LIBDIR "uname.so" }, |
56 | [TC_ROOTFS] = { S_TC_ROOTFS, OVERRIDE_LIBDIR "path.so" }, | |
57 | [TC_INIT_MODULE_RETCODES] = { S_TC_INIT_MODULE_RETCODES, OVERRIDE_LIBDIR "init_module.so" }, | |
f6ef5d6b | 58 | [TC_DELETE_MODULE_RETCODES] = { S_TC_DELETE_MODULE_RETCODES, OVERRIDE_LIBDIR "delete_module.so" }, |
395478cb LDM |
59 | }; |
60 | ||
b8e344a6 LDM |
61 | #define USEC_PER_SEC 1000000ULL |
62 | #define USEC_PER_MSEC 1000ULL | |
63 | #define TEST_TIMEOUT_USEC 2 * USEC_PER_SEC | |
64 | static unsigned long long now_usec(void) | |
65 | { | |
66 | struct timespec ts; | |
67 | ||
68 | if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) | |
69 | return 0; | |
70 | ||
71 | return ts_usec(&ts); | |
72 | } | |
73 | ||
80f9e023 LDM |
74 | static void help(void) |
75 | { | |
76 | const struct option *itr; | |
77 | const char *itr_short; | |
78 | ||
79 | printf("Usage:\n" | |
80 | "\t%s [options] <test>\n" | |
81 | "Options:\n", basename(progname)); | |
82 | ||
83 | for (itr = options, itr_short = options_short; | |
84 | itr->name != NULL; itr++, itr_short++) | |
85 | printf("\t-%c, --%s\n", *itr_short, itr->name); | |
86 | } | |
87 | ||
88 | static void test_list(const struct test *tests[]) | |
89 | { | |
90 | size_t i; | |
91 | ||
92 | printf("Available tests:\n"); | |
93 | for (i = 0; tests[i] != NULL; i++) | |
94 | printf("\t%s, %s\n", tests[i]->name, tests[i]->description); | |
95 | } | |
96 | ||
97 | int test_init(int argc, char *const argv[], const struct test *tests[]) | |
98 | { | |
99 | progname = argv[0]; | |
100 | ||
101 | for (;;) { | |
102 | int c, idx = 0; | |
103 | c = getopt_long(argc, argv, options_short, options, &idx); | |
104 | if (c == -1) | |
105 | break; | |
106 | switch (c) { | |
107 | case 'l': | |
108 | test_list(tests); | |
109 | return 0; | |
110 | case 'h': | |
111 | help(); | |
112 | return 0; | |
113 | case 'n': | |
114 | oneshot = 1; | |
115 | break; | |
116 | case '?': | |
117 | return -1; | |
118 | default: | |
119 | ERR("unexpected getopt_long() value %c\n", c); | |
120 | return -1; | |
121 | } | |
122 | } | |
123 | ||
51b1d1ab LDM |
124 | if (isatty(STDOUT_FILENO) == 0) { |
125 | ANSI_HIGHLIGHT_OFF = ""; | |
126 | ANSI_HIGHLIGHT_RED_ON = ""; | |
127 | ANSI_HIGHLIGHT_GREEN_ON = ""; | |
128 | } | |
129 | ||
80f9e023 LDM |
130 | return optind; |
131 | } | |
132 | ||
133 | const struct test *test_find(const struct test *tests[], const char *name) | |
134 | { | |
135 | size_t i; | |
136 | ||
137 | for (i = 0; tests[i] != NULL; i++) { | |
138 | if (strcmp(tests[i]->name, name) == 0) | |
139 | return tests[i]; | |
140 | } | |
141 | ||
142 | return NULL; | |
143 | } | |
144 | ||
ed2df4e9 | 145 | static int test_spawn_test(const struct test *t) |
80f9e023 LDM |
146 | { |
147 | const char *const args[] = { progname, "-n", t->name, NULL }; | |
148 | ||
149 | execv(progname, (char *const *) args); | |
150 | ||
151 | ERR("failed to spawn %s for %s: %m\n", progname, t->name); | |
152 | return EXIT_FAILURE; | |
153 | } | |
154 | ||
ed2df4e9 LDM |
155 | static int test_run_spawned(const struct test *t) |
156 | { | |
157 | int err = t->func(t); | |
158 | exit(err); | |
159 | ||
160 | return EXIT_FAILURE; | |
161 | } | |
162 | ||
9e3b9d2e | 163 | int test_spawn_prog(const char *prog, const char *const args[]) |
80f9e023 LDM |
164 | { |
165 | execv(prog, (char *const *) args); | |
166 | ||
9e3b9d2e LDM |
167 | ERR("failed to spawn %s\n", prog); |
168 | ERR("did you forget to build tools?\n"); | |
80f9e023 LDM |
169 | return EXIT_FAILURE; |
170 | } | |
171 | ||
395478cb | 172 | static void test_export_environ(const struct test *t) |
80f9e023 | 173 | { |
395478cb LDM |
174 | char *preload = NULL; |
175 | size_t preloadlen = 0; | |
176 | size_t i; | |
177 | ||
178 | unsetenv("LD_PRELOAD"); | |
179 | ||
180 | for (i = 0; i < _TC_LAST; i++) { | |
181 | const char *ldpreload; | |
182 | size_t ldpreloadlen; | |
183 | char *tmp; | |
184 | ||
185 | if (t->config[i] == NULL) | |
186 | continue; | |
187 | ||
188 | setenv(env_config[i].key, t->config[i], 1); | |
189 | ||
190 | ldpreload = env_config[i].ldpreload; | |
191 | ldpreloadlen = strlen(ldpreload); | |
192 | tmp = realloc(preload, preloadlen + 2 + ldpreloadlen); | |
193 | if (tmp == NULL) { | |
194 | ERR("oom: test_export_environ()\n"); | |
195 | return; | |
196 | } | |
197 | preload = tmp; | |
198 | ||
199 | if (preloadlen > 0) | |
200 | preload[preloadlen++] = ' '; | |
201 | memcpy(preload + preloadlen, ldpreload, ldpreloadlen); | |
202 | preloadlen += ldpreloadlen; | |
203 | preload[preloadlen] = '\0'; | |
204 | } | |
205 | ||
206 | if (preload != NULL) | |
207 | setenv("LD_PRELOAD", preload, 1); | |
208 | ||
209 | free(preload); | |
80f9e023 LDM |
210 | } |
211 | ||
3dbb8dea | 212 | static inline int test_run_child(const struct test *t, int fdout[2], |
ed8e93fd | 213 | int fderr[2], int fdmonitor[2]) |
80f9e023 | 214 | { |
45481ee2 LDM |
215 | /* kill child if parent dies */ |
216 | prctl(PR_SET_PDEATHSIG, SIGTERM); | |
217 | ||
218 | test_export_environ(t); | |
219 | ||
3dbb8dea LDM |
220 | /* Close read-fds and redirect std{out,err} to the write-fds */ |
221 | if (t->output.stdout != NULL) { | |
222 | close(fdout[0]); | |
223 | if (dup2(fdout[1], STDOUT_FILENO) < 0) { | |
050db08c | 224 | ERR("could not redirect stdout to pipe: %m\n"); |
3dbb8dea LDM |
225 | exit(EXIT_FAILURE); |
226 | } | |
227 | } | |
228 | ||
229 | if (t->output.stderr != NULL) { | |
230 | close(fderr[0]); | |
231 | if (dup2(fderr[1], STDERR_FILENO) < 0) { | |
050db08c | 232 | ERR("could not redirect stdout to pipe: %m\n"); |
3dbb8dea LDM |
233 | exit(EXIT_FAILURE); |
234 | } | |
235 | } | |
236 | ||
ed8e93fd LDM |
237 | close(fdmonitor[0]); |
238 | ||
0de690c9 LDM |
239 | if (t->config[TC_ROOTFS] != NULL) { |
240 | const char *stamp = TESTSUITE_ROOTFS "../stamp-rootfs"; | |
241 | const char *rootfs = t->config[TC_ROOTFS]; | |
242 | struct stat rootfsst, stampst; | |
243 | ||
244 | if (stat(stamp, &stampst) != 0) { | |
245 | ERR("could not stat %s\n - %m", stamp); | |
246 | exit(EXIT_FAILURE); | |
247 | } | |
248 | ||
249 | if (stat(rootfs, &rootfsst) != 0) { | |
250 | ERR("could not stat %s\n - %m", rootfs); | |
251 | exit(EXIT_FAILURE); | |
252 | } | |
253 | ||
254 | if (stat_mstamp(&rootfsst) > stat_mstamp(&stampst)) { | |
255 | ERR("rootfs %s is dirty, please run 'make rootfs' before runnning this test\n", | |
256 | rootfs); | |
257 | exit(EXIT_FAILURE); | |
258 | } | |
259 | } | |
260 | ||
45481ee2 LDM |
261 | if (t->need_spawn) |
262 | return test_spawn_test(t); | |
263 | else | |
264 | return test_run_spawned(t); | |
265 | } | |
266 | ||
3dbb8dea | 267 | static inline bool test_run_parent_check_outputs(const struct test *t, |
b8e344a6 | 268 | int fdout, int fderr, int fdmonitor, pid_t child) |
3dbb8dea | 269 | { |
ed8e93fd | 270 | struct epoll_event ep_outpipe, ep_errpipe, ep_monitor; |
3dbb8dea | 271 | int err, fd_ep, fd_matchout = -1, fd_matcherr = -1; |
b8e344a6 | 272 | unsigned long long end_usec, start_usec; |
3dbb8dea | 273 | |
3dbb8dea LDM |
274 | fd_ep = epoll_create1(EPOLL_CLOEXEC); |
275 | if (fd_ep < 0) { | |
276 | ERR("could not create epoll fd: %m\n"); | |
277 | return false; | |
278 | } | |
279 | ||
280 | if (t->output.stdout != NULL) { | |
281 | fd_matchout = open(t->output.stdout, O_RDONLY); | |
282 | if (fd_matchout < 0) { | |
283 | err = -errno; | |
284 | ERR("could not open %s for read: %m\n", | |
285 | t->output.stdout); | |
286 | goto out; | |
287 | } | |
288 | memset(&ep_outpipe, 0, sizeof(struct epoll_event)); | |
289 | ep_outpipe.events = EPOLLIN; | |
290 | ep_outpipe.data.ptr = &fdout; | |
291 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdout, &ep_outpipe) < 0) { | |
292 | err = -errno; | |
293 | ERR("could not add fd to epoll: %m\n"); | |
294 | goto out; | |
295 | } | |
296 | } else | |
297 | fdout = -1; | |
298 | ||
299 | if (t->output.stderr != NULL) { | |
300 | fd_matcherr = open(t->output.stderr, O_RDONLY); | |
301 | if (fd_matcherr < 0) { | |
302 | err = -errno; | |
303 | ERR("could not open %s for read: %m\n", | |
304 | t->output.stderr); | |
305 | goto out; | |
306 | ||
307 | } | |
308 | memset(&ep_errpipe, 0, sizeof(struct epoll_event)); | |
309 | ep_errpipe.events = EPOLLIN; | |
310 | ep_errpipe.data.ptr = &fderr; | |
311 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fderr, &ep_errpipe) < 0) { | |
312 | err = -errno; | |
313 | ERR("could not add fd to epoll: %m\n"); | |
314 | goto out; | |
315 | } | |
316 | } else | |
317 | fderr = -1; | |
318 | ||
ed8e93fd LDM |
319 | memset(&ep_monitor, 0, sizeof(struct epoll_event)); |
320 | ep_monitor.events = EPOLLHUP; | |
321 | ep_monitor.data.ptr = &fdmonitor; | |
322 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdmonitor, &ep_monitor) < 0) { | |
323 | err = -errno; | |
324 | ERR("could not add monitor fd to epoll: %m\n"); | |
325 | goto out; | |
326 | } | |
327 | ||
b8e344a6 LDM |
328 | start_usec = now_usec(); |
329 | end_usec = start_usec + TEST_TIMEOUT_USEC; | |
330 | ||
ed8e93fd | 331 | for (err = 0; fdmonitor >= 0 || fdout >= 0 || fderr >= 0;) { |
b8e344a6 | 332 | int fdcount, i, timeout; |
3dbb8dea | 333 | struct epoll_event ev[4]; |
b8e344a6 | 334 | unsigned long long curr_usec = now_usec(); |
3dbb8dea | 335 | |
b8e344a6 LDM |
336 | if (curr_usec > end_usec) |
337 | break; | |
338 | ||
339 | timeout = (end_usec - curr_usec) / USEC_PER_MSEC; | |
340 | fdcount = epoll_wait(fd_ep, ev, 4, timeout); | |
3dbb8dea LDM |
341 | if (fdcount < 0) { |
342 | if (errno == EINTR) | |
343 | continue; | |
344 | err = -errno; | |
345 | ERR("could not poll: %m\n"); | |
346 | goto out; | |
347 | } | |
348 | ||
349 | for (i = 0; i < fdcount; i++) { | |
350 | int *fd = ev[i].data.ptr; | |
351 | ||
352 | if (ev[i].events & EPOLLIN) { | |
353 | ssize_t r, done = 0; | |
354 | char buf[4096]; | |
355 | char bufmatch[4096]; | |
356 | int fd_match; | |
357 | ||
358 | /* | |
359 | * compare the output from child with the one | |
360 | * saved as correct | |
361 | */ | |
362 | ||
363 | r = read(*fd, buf, sizeof(buf) - 1); | |
364 | if (r <= 0) | |
365 | continue; | |
366 | ||
367 | if (*fd == fdout) | |
368 | fd_match = fd_matchout; | |
ed8e93fd | 369 | else if (*fd == fderr) |
3dbb8dea | 370 | fd_match = fd_matcherr; |
ed8e93fd LDM |
371 | else { |
372 | ERR("Unexpected activity on monitor pipe\n"); | |
373 | err = -EINVAL; | |
374 | goto out; | |
375 | } | |
3dbb8dea LDM |
376 | |
377 | for (;;) { | |
378 | int rmatch = read(fd_match, | |
379 | bufmatch + done, r - done); | |
380 | if (rmatch == 0) | |
381 | break; | |
382 | ||
383 | if (rmatch < 0) { | |
384 | if (errno == EINTR) | |
385 | continue; | |
386 | err = -errno; | |
387 | ERR("could not read match fd %d\n", | |
388 | fd_match); | |
389 | goto out; | |
390 | } | |
391 | ||
392 | done += rmatch; | |
393 | } | |
394 | ||
395 | buf[r] = '\0'; | |
396 | bufmatch[r] = '\0'; | |
397 | if (strcmp(buf, bufmatch) != 0) { | |
398 | ERR("Outputs do not match on %s:\n", | |
399 | fd_match == fd_matchout ? "stdout" : "stderr"); | |
400 | ERR("correct:\n%s\n", bufmatch); | |
401 | ERR("wrong:\n%s\n", buf); | |
402 | err = -1; | |
403 | goto out; | |
404 | } | |
405 | } else if (ev[i].events & EPOLLHUP) { | |
406 | if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, | |
407 | *fd, NULL) < 0) { | |
408 | ERR("could not remove fd %d from epoll: %m\n", | |
409 | *fd); | |
410 | } | |
411 | *fd = -1; | |
412 | } | |
413 | } | |
b8e344a6 | 414 | } |
3dbb8dea | 415 | |
b8e344a6 LDM |
416 | if (err == 0 && fdmonitor >= 0) { |
417 | err = -EINVAL; | |
418 | ERR("Test '%s' timed out, killing %d\n", t->name, child); | |
419 | kill(child, SIGKILL); | |
3dbb8dea | 420 | } |
b8e344a6 | 421 | |
3dbb8dea LDM |
422 | out: |
423 | if (fd_matchout >= 0) | |
424 | close(fd_matchout); | |
425 | if (fd_matcherr >= 0) | |
426 | close(fd_matcherr); | |
427 | if (fd_ep >= 0) | |
428 | close(fd_ep); | |
429 | return err == 0; | |
430 | } | |
431 | ||
432 | static inline int test_run_parent(const struct test *t, int fdout[2], | |
b8e344a6 | 433 | int fderr[2], int fdmonitor[2], pid_t child) |
45481ee2 LDM |
434 | { |
435 | pid_t pid; | |
80f9e023 | 436 | int err; |
3dbb8dea LDM |
437 | bool matchout; |
438 | ||
439 | /* Close write-fds */ | |
440 | if (t->output.stdout != NULL) | |
441 | close(fdout[1]); | |
442 | if (t->output.stderr != NULL) | |
443 | close(fderr[1]); | |
ed8e93fd | 444 | close(fdmonitor[1]); |
3dbb8dea | 445 | |
ed8e93fd | 446 | matchout = test_run_parent_check_outputs(t, fdout[0], fderr[0], |
b8e344a6 | 447 | fdmonitor[0], child); |
3dbb8dea LDM |
448 | |
449 | /* | |
450 | * break pipe on the other end: either child already closed or we want | |
451 | * to stop it | |
452 | */ | |
453 | if (t->output.stdout != NULL) | |
454 | close(fdout[0]); | |
455 | if (t->output.stderr != NULL) | |
456 | close(fderr[0]); | |
ed8e93fd | 457 | close(fdmonitor[0]); |
45481ee2 LDM |
458 | |
459 | do { | |
460 | pid = wait(&err); | |
461 | if (pid == -1) { | |
462 | ERR("error waitpid(): %m\n"); | |
463 | return EXIT_FAILURE; | |
464 | } | |
465 | } while (!WIFEXITED(err) && !WIFSIGNALED(err)); | |
466 | ||
3dbb8dea LDM |
467 | if (WIFEXITED(err)) { |
468 | if (WEXITSTATUS(err) != 0) | |
469 | ERR("'%s' [%u] exited with return code %d\n", | |
470 | t->name, pid, WEXITSTATUS(err)); | |
471 | else | |
472 | LOG("'%s' [%u] exited with return code %d\n", | |
473 | t->name, pid, WEXITSTATUS(err)); | |
474 | } else if (WIFSIGNALED(err)) { | |
475 | ERR("'%s' [%u] terminated by signal %d (%s)\n", t->name, pid, | |
476 | WTERMSIG(err), strsignal(WTERMSIG(err))); | |
b8e344a6 | 477 | return EXIT_FAILURE; |
3dbb8dea LDM |
478 | } |
479 | ||
fa0046ba DR |
480 | if (t->expected_fail == false) { |
481 | if (err == 0) { | |
482 | if (matchout) | |
483 | LOG("%sPASSED%s: %s\n", | |
484 | ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF, | |
485 | t->name); | |
486 | else { | |
487 | ERR("%sFAILED%s: exit ok but outputs do not match: %s\n", | |
488 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, | |
489 | t->name); | |
490 | err = EXIT_FAILURE; | |
491 | } | |
492 | } else | |
493 | ERR("%sFAILED%s: %s\n", | |
494 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, | |
495 | t->name); | |
496 | } else { | |
497 | if (err == 0) { | |
498 | if (matchout) { | |
499 | LOG("%sUNEXPECTED PASS%s: %s\n", | |
658e0471 | 500 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, |
fa0046ba DR |
501 | t->name); |
502 | err = EXIT_FAILURE; | |
503 | } else | |
504 | LOG("%sEXPECTED FAIL%s: exit ok but outputs do not match: %s\n", | |
505 | ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF, | |
506 | t->name); | |
507 | } else { | |
508 | ERR("%sEXPECTED FAIL%s: %s\n", | |
509 | ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF, | |
510 | t->name); | |
511 | err = EXIT_SUCCESS; | |
3dbb8dea | 512 | } |
fa0046ba | 513 | } |
45481ee2 | 514 | |
45481ee2 LDM |
515 | return err; |
516 | } | |
517 | ||
f31d49c8 DR |
518 | static int prepend_path(const char *extra) |
519 | { | |
520 | char *oldpath, *newpath; | |
521 | int r; | |
522 | ||
523 | if (extra == NULL) | |
524 | return 0; | |
525 | ||
526 | oldpath = getenv("PATH"); | |
527 | if (oldpath == NULL) | |
528 | return setenv("PATH", extra, 1); | |
529 | ||
530 | if (asprintf(&newpath, "%s:%s", extra, oldpath) < 0) { | |
050db08c | 531 | ERR("failed to allocate memory to new PATH\n"); |
f31d49c8 DR |
532 | return -1; |
533 | } | |
534 | ||
535 | r = setenv("PATH", newpath, 1); | |
536 | free(newpath); | |
537 | ||
538 | return r; | |
539 | } | |
540 | ||
45481ee2 LDM |
541 | int test_run(const struct test *t) |
542 | { | |
80f9e023 | 543 | pid_t pid; |
3dbb8dea LDM |
544 | int fdout[2]; |
545 | int fderr[2]; | |
ed8e93fd | 546 | int fdmonitor[2]; |
80f9e023 | 547 | |
ed2df4e9 LDM |
548 | if (t->need_spawn && oneshot) |
549 | test_run_spawned(t); | |
550 | ||
3dbb8dea LDM |
551 | if (t->output.stdout != NULL) { |
552 | if (pipe(fdout) != 0) { | |
553 | ERR("could not create out pipe for %s\n", t->name); | |
554 | return EXIT_FAILURE; | |
555 | } | |
556 | } | |
557 | ||
558 | if (t->output.stderr != NULL) { | |
559 | if (pipe(fderr) != 0) { | |
560 | ERR("could not create err pipe for %s\n", t->name); | |
561 | return EXIT_FAILURE; | |
562 | } | |
563 | } | |
564 | ||
ed8e93fd LDM |
565 | if (pipe(fdmonitor) != 0) { |
566 | ERR("could not create monitor pipe for %s\n", t->name); | |
567 | return EXIT_FAILURE; | |
568 | } | |
569 | ||
f31d49c8 DR |
570 | if (prepend_path(t->path) < 0) { |
571 | ERR("failed to prepend '%s' to PATH\n", t->path); | |
572 | return EXIT_FAILURE; | |
573 | } | |
574 | ||
80f9e023 LDM |
575 | LOG("running %s, in forked context\n", t->name); |
576 | ||
577 | pid = fork(); | |
578 | if (pid < 0) { | |
579 | ERR("could not fork(): %m\n"); | |
580 | LOG("FAILED: %s\n", t->name); | |
581 | return EXIT_FAILURE; | |
582 | } | |
583 | ||
45481ee2 | 584 | if (pid > 0) |
b8e344a6 | 585 | return test_run_parent(t, fdout, fderr, fdmonitor, pid); |
80f9e023 | 586 | |
ed8e93fd | 587 | return test_run_child(t, fdout, fderr, fdmonitor); |
80f9e023 | 588 | } |