]>
Commit | Line | Data |
---|---|---|
e701e381 | 1 | /* |
e6b0e49b | 2 | * Copyright (C) 2012-2013 ProFUSION embedded systems |
e701e381 | 3 | * |
e1b1ab24 LDM |
4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public | |
6 | * License as published by the Free Software Foundation; either | |
7 | * version 2.1 of the License, or (at your option) any later version. | |
e701e381 LDM |
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 | |
e1b1ab24 LDM |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. | |
e701e381 | 13 | * |
e1b1ab24 | 14 | * You should have received a copy of the GNU Lesser General Public |
dea2dfee | 15 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
e701e381 LDM |
16 | */ |
17 | ||
c2e4286b | 18 | #include <dirent.h> |
80f9e023 LDM |
19 | #include <errno.h> |
20 | #include <fcntl.h> | |
21 | #include <getopt.h> | |
22 | #include <limits.h> | |
c0937526 | 23 | #include <regex.h> |
80f9e023 | 24 | #include <stdarg.h> |
c2e4286b | 25 | #include <stdio.h> |
80f9e023 LDM |
26 | #include <stdlib.h> |
27 | #include <string.h> | |
b8e344a6 | 28 | #include <time.h> |
80f9e023 | 29 | #include <unistd.h> |
3dbb8dea | 30 | #include <sys/epoll.h> |
80f9e023 | 31 | #include <sys/prctl.h> |
0de690c9 | 32 | #include <sys/stat.h> |
80f9e023 LDM |
33 | #include <sys/wait.h> |
34 | ||
96573a02 LDM |
35 | #include <shared/util.h> |
36 | ||
80f9e023 LDM |
37 | #include "testsuite.h" |
38 | ||
51b1d1ab LDM |
39 | static const char *ANSI_HIGHLIGHT_GREEN_ON = "\x1B[1;32m"; |
40 | static const char *ANSI_HIGHLIGHT_RED_ON = "\x1B[1;31m"; | |
41 | static const char *ANSI_HIGHLIGHT_OFF = "\x1B[0m"; | |
42 | ||
80f9e023 | 43 | static const char *progname; |
ed2df4e9 | 44 | static int oneshot = 0; |
80f9e023 LDM |
45 | static const char options_short[] = "lhn"; |
46 | static const struct option options[] = { | |
47 | { "list", no_argument, 0, 'l' }, | |
48 | { "help", no_argument, 0, 'h' }, | |
49 | { NULL, 0, 0, 0 } | |
50 | }; | |
51 | ||
d2c2b8b5 LDM |
52 | #define OVERRIDE_LIBDIR ABS_TOP_BUILDDIR "/testsuite/.libs/" |
53 | ||
395478cb LDM |
54 | struct _env_config { |
55 | const char *key; | |
56 | const char *ldpreload; | |
57 | } env_config[_TC_LAST] = { | |
d2c2b8b5 LDM |
58 | [TC_UNAME_R] = { S_TC_UNAME_R, OVERRIDE_LIBDIR "uname.so" }, |
59 | [TC_ROOTFS] = { S_TC_ROOTFS, OVERRIDE_LIBDIR "path.so" }, | |
60 | [TC_INIT_MODULE_RETCODES] = { S_TC_INIT_MODULE_RETCODES, OVERRIDE_LIBDIR "init_module.so" }, | |
f6ef5d6b | 61 | [TC_DELETE_MODULE_RETCODES] = { S_TC_DELETE_MODULE_RETCODES, OVERRIDE_LIBDIR "delete_module.so" }, |
395478cb LDM |
62 | }; |
63 | ||
b8e344a6 LDM |
64 | #define USEC_PER_SEC 1000000ULL |
65 | #define USEC_PER_MSEC 1000ULL | |
66 | #define TEST_TIMEOUT_USEC 2 * USEC_PER_SEC | |
67 | static unsigned long long now_usec(void) | |
68 | { | |
69 | struct timespec ts; | |
70 | ||
71 | if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) | |
72 | return 0; | |
73 | ||
74 | return ts_usec(&ts); | |
75 | } | |
76 | ||
80f9e023 LDM |
77 | static void help(void) |
78 | { | |
79 | const struct option *itr; | |
80 | const char *itr_short; | |
81 | ||
82 | printf("Usage:\n" | |
83 | "\t%s [options] <test>\n" | |
84 | "Options:\n", basename(progname)); | |
85 | ||
86 | for (itr = options, itr_short = options_short; | |
87 | itr->name != NULL; itr++, itr_short++) | |
88 | printf("\t-%c, --%s\n", *itr_short, itr->name); | |
89 | } | |
90 | ||
c5798fea | 91 | static void test_list(const struct test *start, const struct test *stop) |
80f9e023 | 92 | { |
c5798fea | 93 | const struct test *t; |
80f9e023 LDM |
94 | |
95 | printf("Available tests:\n"); | |
c5798fea LDM |
96 | for (t = start; t < stop; t++) |
97 | printf("\t%s, %s\n", t->name, t->description); | |
80f9e023 LDM |
98 | } |
99 | ||
c5798fea LDM |
100 | int test_init(const struct test *start, const struct test *stop, |
101 | int argc, char *const argv[]) | |
80f9e023 LDM |
102 | { |
103 | progname = argv[0]; | |
104 | ||
105 | for (;;) { | |
106 | int c, idx = 0; | |
107 | c = getopt_long(argc, argv, options_short, options, &idx); | |
108 | if (c == -1) | |
109 | break; | |
110 | switch (c) { | |
111 | case 'l': | |
c5798fea | 112 | test_list(start, stop); |
80f9e023 LDM |
113 | return 0; |
114 | case 'h': | |
115 | help(); | |
116 | return 0; | |
117 | case 'n': | |
118 | oneshot = 1; | |
119 | break; | |
120 | case '?': | |
121 | return -1; | |
122 | default: | |
123 | ERR("unexpected getopt_long() value %c\n", c); | |
124 | return -1; | |
125 | } | |
126 | } | |
127 | ||
51b1d1ab LDM |
128 | if (isatty(STDOUT_FILENO) == 0) { |
129 | ANSI_HIGHLIGHT_OFF = ""; | |
130 | ANSI_HIGHLIGHT_RED_ON = ""; | |
131 | ANSI_HIGHLIGHT_GREEN_ON = ""; | |
132 | } | |
133 | ||
80f9e023 LDM |
134 | return optind; |
135 | } | |
136 | ||
c5798fea LDM |
137 | const struct test *test_find(const struct test *start, |
138 | const struct test *stop, const char *name) | |
80f9e023 | 139 | { |
c5798fea | 140 | const struct test *t; |
80f9e023 | 141 | |
c5798fea | 142 | for (t = start; t < stop; t++) { |
5c42c5fc | 143 | if (streq(t->name, name)) |
c5798fea | 144 | return t; |
80f9e023 LDM |
145 | } |
146 | ||
147 | return NULL; | |
148 | } | |
149 | ||
ed2df4e9 | 150 | static int test_spawn_test(const struct test *t) |
80f9e023 LDM |
151 | { |
152 | const char *const args[] = { progname, "-n", t->name, NULL }; | |
153 | ||
154 | execv(progname, (char *const *) args); | |
155 | ||
156 | ERR("failed to spawn %s for %s: %m\n", progname, t->name); | |
157 | return EXIT_FAILURE; | |
158 | } | |
159 | ||
ed2df4e9 LDM |
160 | static int test_run_spawned(const struct test *t) |
161 | { | |
162 | int err = t->func(t); | |
163 | exit(err); | |
164 | ||
165 | return EXIT_FAILURE; | |
166 | } | |
167 | ||
9e3b9d2e | 168 | int test_spawn_prog(const char *prog, const char *const args[]) |
80f9e023 LDM |
169 | { |
170 | execv(prog, (char *const *) args); | |
171 | ||
9e3b9d2e LDM |
172 | ERR("failed to spawn %s\n", prog); |
173 | ERR("did you forget to build tools?\n"); | |
80f9e023 LDM |
174 | return EXIT_FAILURE; |
175 | } | |
176 | ||
395478cb | 177 | static void test_export_environ(const struct test *t) |
80f9e023 | 178 | { |
395478cb LDM |
179 | char *preload = NULL; |
180 | size_t preloadlen = 0; | |
181 | size_t i; | |
34db3f2d | 182 | const struct keyval *env; |
395478cb LDM |
183 | |
184 | unsetenv("LD_PRELOAD"); | |
185 | ||
186 | for (i = 0; i < _TC_LAST; i++) { | |
187 | const char *ldpreload; | |
188 | size_t ldpreloadlen; | |
189 | char *tmp; | |
190 | ||
191 | if (t->config[i] == NULL) | |
192 | continue; | |
193 | ||
194 | setenv(env_config[i].key, t->config[i], 1); | |
195 | ||
196 | ldpreload = env_config[i].ldpreload; | |
197 | ldpreloadlen = strlen(ldpreload); | |
198 | tmp = realloc(preload, preloadlen + 2 + ldpreloadlen); | |
199 | if (tmp == NULL) { | |
200 | ERR("oom: test_export_environ()\n"); | |
201 | return; | |
202 | } | |
203 | preload = tmp; | |
204 | ||
205 | if (preloadlen > 0) | |
206 | preload[preloadlen++] = ' '; | |
207 | memcpy(preload + preloadlen, ldpreload, ldpreloadlen); | |
208 | preloadlen += ldpreloadlen; | |
209 | preload[preloadlen] = '\0'; | |
210 | } | |
211 | ||
212 | if (preload != NULL) | |
213 | setenv("LD_PRELOAD", preload, 1); | |
214 | ||
215 | free(preload); | |
34db3f2d LDM |
216 | |
217 | for (env = t->env_vars; env && env->key; env++) | |
218 | setenv(env->key, env->val, 1); | |
80f9e023 LDM |
219 | } |
220 | ||
3dbb8dea | 221 | static inline int test_run_child(const struct test *t, int fdout[2], |
ed8e93fd | 222 | int fderr[2], int fdmonitor[2]) |
80f9e023 | 223 | { |
45481ee2 LDM |
224 | /* kill child if parent dies */ |
225 | prctl(PR_SET_PDEATHSIG, SIGTERM); | |
226 | ||
227 | test_export_environ(t); | |
228 | ||
3dbb8dea | 229 | /* Close read-fds and redirect std{out,err} to the write-fds */ |
bd4e7340 | 230 | if (t->output.out != NULL) { |
3dbb8dea LDM |
231 | close(fdout[0]); |
232 | if (dup2(fdout[1], STDOUT_FILENO) < 0) { | |
050db08c | 233 | ERR("could not redirect stdout to pipe: %m\n"); |
3dbb8dea LDM |
234 | exit(EXIT_FAILURE); |
235 | } | |
236 | } | |
237 | ||
bd4e7340 | 238 | if (t->output.err != NULL) { |
3dbb8dea LDM |
239 | close(fderr[0]); |
240 | if (dup2(fderr[1], STDERR_FILENO) < 0) { | |
bd4e7340 | 241 | ERR("could not redirect stderr to pipe: %m\n"); |
3dbb8dea LDM |
242 | exit(EXIT_FAILURE); |
243 | } | |
244 | } | |
245 | ||
ed8e93fd LDM |
246 | close(fdmonitor[0]); |
247 | ||
0de690c9 LDM |
248 | if (t->config[TC_ROOTFS] != NULL) { |
249 | const char *stamp = TESTSUITE_ROOTFS "../stamp-rootfs"; | |
250 | const char *rootfs = t->config[TC_ROOTFS]; | |
251 | struct stat rootfsst, stampst; | |
252 | ||
253 | if (stat(stamp, &stampst) != 0) { | |
254 | ERR("could not stat %s\n - %m", stamp); | |
255 | exit(EXIT_FAILURE); | |
256 | } | |
257 | ||
258 | if (stat(rootfs, &rootfsst) != 0) { | |
259 | ERR("could not stat %s\n - %m", rootfs); | |
260 | exit(EXIT_FAILURE); | |
261 | } | |
262 | ||
263 | if (stat_mstamp(&rootfsst) > stat_mstamp(&stampst)) { | |
264 | ERR("rootfs %s is dirty, please run 'make rootfs' before runnning this test\n", | |
265 | rootfs); | |
266 | exit(EXIT_FAILURE); | |
267 | } | |
268 | } | |
269 | ||
45481ee2 LDM |
270 | if (t->need_spawn) |
271 | return test_spawn_test(t); | |
272 | else | |
273 | return test_run_spawned(t); | |
274 | } | |
275 | ||
03a5079f LDM |
276 | static int check_activity(int fd, bool activity, const char *path, |
277 | const char *stream) | |
278 | { | |
279 | struct stat st; | |
280 | ||
281 | /* not monitoring or monitoring and it has activity */ | |
282 | if (fd < 0 || activity) | |
283 | return 0; | |
284 | ||
285 | /* monitoring, there was no activity and size matches */ | |
286 | if (stat(path, &st) == 0 && st.st_size == 0) | |
287 | return 0; | |
288 | ||
289 | ERR("Expecting output on %s, but test didn't produce any\n", stream); | |
290 | ||
291 | return -1; | |
292 | } | |
293 | ||
a5cc3521 LDM |
294 | #define BUFSZ 4096 |
295 | struct buffer { | |
296 | char buf[BUFSZ]; | |
c0937526 | 297 | unsigned int head; |
a5cc3521 LDM |
298 | }; |
299 | ||
c0937526 LDM |
300 | |
301 | static bool cmpbuf_regex_one(const char *pattern, const char *s) | |
302 | { | |
303 | _cleanup_(regfree) regex_t re = { }; | |
304 | ||
305 | return !regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) && | |
306 | !regexec(&re, s, 0, NULL, 0); | |
307 | } | |
308 | ||
309 | /* | |
310 | * read fd and fd_match, checking the first matches the regex of the second, | |
311 | * line by line | |
312 | */ | |
313 | static bool cmpbuf_regex(const struct test *t, const char *prefix, | |
314 | int fd, int fd_match, struct buffer *buf, | |
315 | struct buffer *buf_match) | |
316 | { | |
317 | char *p, *p_match; | |
318 | int done = 0, done_match = 0, r; | |
319 | ||
320 | if (buf->head >= sizeof(buf->buf)) { | |
321 | ERR("Read %zu bytes without a newline\n", sizeof(buf->buf)); | |
322 | ERR("output: %.*s", (int)sizeof(buf->buf), buf->buf); | |
323 | return false; | |
324 | } | |
325 | ||
326 | r = read(fd, buf->buf + buf->head, sizeof(buf->buf) - buf->head); | |
327 | if (r <= 0) | |
328 | return true; | |
329 | ||
330 | buf->head += r; | |
331 | ||
332 | /* | |
333 | * Process as many lines as read from fd and that fits in the buffer - | |
334 | * it's assumed that if we get N lines from fd, we should be able to | |
335 | * get the same amount from fd_match | |
336 | */ | |
337 | for (;;) { | |
338 | p = memchr(buf->buf + done, '\n', buf->head - done); | |
339 | if (!p) | |
340 | break; | |
341 | *p = '\0'; | |
342 | ||
343 | p_match = memchr(buf_match->buf + done_match, '\n', | |
344 | buf_match->head - done_match); | |
345 | if (!p_match) { | |
346 | if (buf_match->head >= sizeof(buf_match->buf)) { | |
347 | ERR("Read %zu bytes without a match\n", sizeof(buf_match->buf)); | |
348 | ERR("output: %.*s", (int)sizeof(buf_match->buf), buf_match->buf); | |
349 | return false; | |
350 | } | |
351 | ||
352 | /* pump more data from file */ | |
353 | r = read(fd_match, buf_match->buf + buf_match->head, | |
354 | sizeof(buf_match->buf) - buf_match->head); | |
355 | if (r <= 0) { | |
356 | ERR("could not read match fd %d\n", fd_match); | |
357 | return false; | |
358 | } | |
359 | buf_match->head += r; | |
360 | p_match = memchr(buf_match->buf + done_match, '\n', | |
361 | buf_match->head - done_match); | |
362 | if (!p_match) { | |
363 | ERR("could not find match line from fd %d\n", fd_match); | |
364 | return false; | |
365 | } | |
366 | } | |
367 | *p_match = '\0'; | |
368 | ||
369 | if (!cmpbuf_regex_one(buf_match->buf + done_match, buf->buf + done)) { | |
370 | ERR("Output does not match pattern on %s:\n", prefix); | |
371 | ERR("pattern: %s\n", buf_match->buf + done_match); | |
372 | ERR("output : %s\n", buf->buf + done); | |
373 | return false; | |
374 | } | |
375 | ||
376 | done = p - buf->buf + 1; | |
377 | done_match = p_match - buf_match->buf + 1; | |
378 | } | |
379 | ||
380 | /* | |
381 | * Prepare for the next call: anything we processed we remove from the | |
382 | * buffer by memmoving the remaining bytes up to the beginning | |
383 | */ | |
384 | if (done) { | |
385 | if (buf->head - done) | |
386 | memmove(buf->buf, buf->buf + done, buf->head - done); | |
387 | buf->head -= done; | |
388 | } | |
389 | ||
390 | if (done_match) { | |
391 | if (buf_match->head - done_match) | |
392 | memmove(buf_match->buf, buf_match->buf + done_match, | |
393 | buf_match->head - done_match); | |
394 | buf_match->head -= done_match; | |
395 | } | |
396 | ||
397 | return true; | |
398 | } | |
399 | ||
a5cc3521 LDM |
400 | /* read fd and fd_match, checking they match exactly */ |
401 | static bool cmpbuf_exact(const struct test *t, const char *prefix, | |
402 | int fd, int fd_match, struct buffer *buf, | |
403 | struct buffer *buf_match) | |
404 | { | |
405 | int r, rmatch, done = 0; | |
406 | ||
407 | r = read(fd, buf->buf, sizeof(buf->buf) - 1); | |
408 | if (r <= 0) | |
409 | /* try again later */ | |
410 | return true; | |
411 | ||
412 | /* read as much data from fd_match as we read from fd */ | |
413 | for (;;) { | |
414 | rmatch = read(fd_match, buf_match->buf + done, r - done); | |
415 | if (rmatch == 0) | |
416 | break; | |
417 | ||
418 | if (rmatch < 0) { | |
419 | if (errno == EINTR) | |
420 | continue; | |
421 | ERR("could not read match fd %d\n", fd_match); | |
422 | return false; | |
423 | } | |
424 | ||
425 | done += rmatch; | |
426 | } | |
427 | ||
428 | buf->buf[r] = '\0'; | |
429 | buf_match->buf[r] = '\0'; | |
430 | ||
431 | if (t->print_outputs) | |
432 | printf("%s: %s\n", prefix, buf->buf); | |
433 | ||
434 | if (!streq(buf->buf, buf_match->buf)) { | |
435 | ERR("Outputs do not match on %s:\n", prefix); | |
436 | ERR("correct:\n%s\n", buf_match->buf); | |
437 | ERR("wrong:\n%s\n", buf->buf); | |
438 | return false; | |
439 | } | |
440 | ||
441 | return true; | |
442 | } | |
443 | ||
444 | static bool test_run_parent_check_outputs(const struct test *t, | |
445 | int fdout, int fderr, int fdmonitor, | |
446 | pid_t child) | |
3dbb8dea | 447 | { |
ed8e93fd | 448 | struct epoll_event ep_outpipe, ep_errpipe, ep_monitor; |
3dbb8dea | 449 | int err, fd_ep, fd_matchout = -1, fd_matcherr = -1; |
03a5079f | 450 | bool fd_activityout = false, fd_activityerr = false; |
b8e344a6 | 451 | unsigned long long end_usec, start_usec; |
a5cc3521 | 452 | _cleanup_free_ struct buffer *buf_out = NULL, *buf_err = NULL; |
3dbb8dea | 453 | |
3dbb8dea LDM |
454 | fd_ep = epoll_create1(EPOLL_CLOEXEC); |
455 | if (fd_ep < 0) { | |
456 | ERR("could not create epoll fd: %m\n"); | |
457 | return false; | |
458 | } | |
459 | ||
bd4e7340 JS |
460 | if (t->output.out != NULL) { |
461 | fd_matchout = open(t->output.out, O_RDONLY); | |
3dbb8dea LDM |
462 | if (fd_matchout < 0) { |
463 | err = -errno; | |
464 | ERR("could not open %s for read: %m\n", | |
bd4e7340 | 465 | t->output.out); |
3dbb8dea LDM |
466 | goto out; |
467 | } | |
468 | memset(&ep_outpipe, 0, sizeof(struct epoll_event)); | |
469 | ep_outpipe.events = EPOLLIN; | |
470 | ep_outpipe.data.ptr = &fdout; | |
471 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdout, &ep_outpipe) < 0) { | |
472 | err = -errno; | |
473 | ERR("could not add fd to epoll: %m\n"); | |
474 | goto out; | |
475 | } | |
a5cc3521 | 476 | buf_out = calloc(2, sizeof(*buf_out)); |
3dbb8dea LDM |
477 | } else |
478 | fdout = -1; | |
479 | ||
bd4e7340 JS |
480 | if (t->output.err != NULL) { |
481 | fd_matcherr = open(t->output.err, O_RDONLY); | |
3dbb8dea LDM |
482 | if (fd_matcherr < 0) { |
483 | err = -errno; | |
484 | ERR("could not open %s for read: %m\n", | |
bd4e7340 | 485 | t->output.err); |
3dbb8dea LDM |
486 | goto out; |
487 | ||
488 | } | |
489 | memset(&ep_errpipe, 0, sizeof(struct epoll_event)); | |
490 | ep_errpipe.events = EPOLLIN; | |
491 | ep_errpipe.data.ptr = &fderr; | |
492 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fderr, &ep_errpipe) < 0) { | |
493 | err = -errno; | |
494 | ERR("could not add fd to epoll: %m\n"); | |
495 | goto out; | |
496 | } | |
a5cc3521 | 497 | buf_err = calloc(2, sizeof(*buf_err)); |
3dbb8dea LDM |
498 | } else |
499 | fderr = -1; | |
500 | ||
ed8e93fd LDM |
501 | memset(&ep_monitor, 0, sizeof(struct epoll_event)); |
502 | ep_monitor.events = EPOLLHUP; | |
503 | ep_monitor.data.ptr = &fdmonitor; | |
504 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdmonitor, &ep_monitor) < 0) { | |
505 | err = -errno; | |
506 | ERR("could not add monitor fd to epoll: %m\n"); | |
507 | goto out; | |
508 | } | |
509 | ||
b8e344a6 LDM |
510 | start_usec = now_usec(); |
511 | end_usec = start_usec + TEST_TIMEOUT_USEC; | |
512 | ||
ed8e93fd | 513 | for (err = 0; fdmonitor >= 0 || fdout >= 0 || fderr >= 0;) { |
b8e344a6 | 514 | int fdcount, i, timeout; |
3dbb8dea | 515 | struct epoll_event ev[4]; |
b8e344a6 | 516 | unsigned long long curr_usec = now_usec(); |
3dbb8dea | 517 | |
b8e344a6 LDM |
518 | if (curr_usec > end_usec) |
519 | break; | |
520 | ||
521 | timeout = (end_usec - curr_usec) / USEC_PER_MSEC; | |
522 | fdcount = epoll_wait(fd_ep, ev, 4, timeout); | |
3dbb8dea LDM |
523 | if (fdcount < 0) { |
524 | if (errno == EINTR) | |
525 | continue; | |
526 | err = -errno; | |
527 | ERR("could not poll: %m\n"); | |
528 | goto out; | |
529 | } | |
530 | ||
531 | for (i = 0; i < fdcount; i++) { | |
a5cc3521 | 532 | int fd = *(int *)ev[i].data.ptr; |
c0937526 | 533 | bool ret; |
3dbb8dea LDM |
534 | |
535 | if (ev[i].events & EPOLLIN) { | |
3dbb8dea | 536 | int fd_match; |
a5cc3521 LDM |
537 | struct buffer *buf, *buf_match; |
538 | const char *prefix; | |
3dbb8dea | 539 | |
a5cc3521 | 540 | if (fd == fdout) { |
3dbb8dea | 541 | fd_match = fd_matchout; |
a5cc3521 | 542 | buf = buf_out; |
03a5079f | 543 | fd_activityout = true; |
a5cc3521 LDM |
544 | prefix = "STDOUT"; |
545 | } else if (fd == fderr) { | |
3dbb8dea | 546 | fd_match = fd_matcherr; |
a5cc3521 | 547 | buf = buf_err; |
03a5079f | 548 | fd_activityerr = true; |
a5cc3521 | 549 | prefix = "STDERR"; |
03a5079f | 550 | } else { |
ed8e93fd LDM |
551 | ERR("Unexpected activity on monitor pipe\n"); |
552 | err = -EINVAL; | |
553 | goto out; | |
554 | } | |
3dbb8dea | 555 | |
a5cc3521 | 556 | buf_match = buf + 1; |
f6dc239e | 557 | |
c0937526 LDM |
558 | if (t->output.regex) |
559 | ret = cmpbuf_regex(t, prefix, fd, fd_match, | |
560 | buf, buf_match); | |
561 | else | |
562 | ret = cmpbuf_exact(t, prefix, fd, fd_match, | |
563 | buf, buf_match); | |
564 | ||
565 | if (!ret) { | |
566 | err = -1; | |
3dbb8dea | 567 | goto out; |
c0937526 | 568 | } |
3dbb8dea | 569 | } else if (ev[i].events & EPOLLHUP) { |
a5cc3521 LDM |
570 | if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd, NULL) < 0) { |
571 | ERR("could not remove fd %d from epoll: %m\n", fd); | |
3dbb8dea | 572 | } |
a5cc3521 | 573 | *(int *)ev[i].data.ptr = -1; |
3dbb8dea LDM |
574 | } |
575 | } | |
b8e344a6 | 576 | } |
3dbb8dea | 577 | |
03a5079f LDM |
578 | err = check_activity(fd_matchout, fd_activityout, t->output.out, "stdout"); |
579 | err |= check_activity(fd_matcherr, fd_activityerr, t->output.err, "stderr"); | |
580 | ||
b8e344a6 LDM |
581 | if (err == 0 && fdmonitor >= 0) { |
582 | err = -EINVAL; | |
583 | ERR("Test '%s' timed out, killing %d\n", t->name, child); | |
584 | kill(child, SIGKILL); | |
3dbb8dea | 585 | } |
b8e344a6 | 586 | |
3dbb8dea LDM |
587 | out: |
588 | if (fd_matchout >= 0) | |
589 | close(fd_matchout); | |
590 | if (fd_matcherr >= 0) | |
591 | close(fd_matcherr); | |
592 | if (fd_ep >= 0) | |
593 | close(fd_ep); | |
594 | return err == 0; | |
595 | } | |
596 | ||
3e451bfe LDM |
597 | static inline int safe_read(int fd, void *buf, size_t count) |
598 | { | |
599 | int r; | |
600 | ||
601 | while (1) { | |
602 | r = read(fd, buf, count); | |
2ad09ccf | 603 | if (r == -1 && errno == EINTR) |
3e451bfe LDM |
604 | continue; |
605 | break; | |
606 | } | |
607 | ||
608 | return r; | |
609 | } | |
610 | ||
611 | static bool check_generated_files(const struct test *t) | |
612 | { | |
613 | const struct keyval *k; | |
614 | ||
615 | /* This is not meant to be a diff replacement, just stupidly check if | |
616 | * the files match. Bear in mind they can be binary files */ | |
617 | for (k = t->output.files; k && k->key; k++) { | |
618 | struct stat sta, stb; | |
619 | int fda = -1, fdb = -1; | |
620 | char bufa[4096]; | |
621 | char bufb[4096]; | |
622 | ||
623 | fda = open(k->key, O_RDONLY); | |
624 | if (fda < 0) { | |
625 | ERR("could not open %s\n - %m\n", k->key); | |
626 | goto fail; | |
627 | } | |
628 | ||
629 | fdb = open(k->val, O_RDONLY); | |
630 | if (fdb < 0) { | |
631 | ERR("could not open %s\n - %m\n", k->val); | |
632 | goto fail; | |
633 | } | |
634 | ||
635 | if (fstat(fda, &sta) != 0) { | |
636 | ERR("could not fstat %d %s\n - %m\n", fda, k->key); | |
637 | goto fail; | |
638 | } | |
639 | ||
640 | if (fstat(fdb, &stb) != 0) { | |
641 | ERR("could not fstat %d %s\n - %m\n", fdb, k->key); | |
642 | goto fail; | |
643 | } | |
644 | ||
645 | if (sta.st_size != stb.st_size) { | |
646 | ERR("sizes do not match %s %s\n", k->key, k->val); | |
647 | goto fail; | |
648 | } | |
649 | ||
650 | for (;;) { | |
651 | int r, done; | |
652 | ||
653 | r = safe_read(fda, bufa, sizeof(bufa)); | |
654 | if (r < 0) | |
655 | goto fail; | |
656 | ||
657 | if (r == 0) | |
658 | /* size is already checked, go to next file */ | |
659 | goto next; | |
660 | ||
661 | for (done = 0; done < r;) { | |
662 | int r2 = safe_read(fdb, bufb + done, r - done); | |
663 | ||
664 | if (r2 <= 0) | |
665 | goto fail; | |
666 | ||
667 | done += r2; | |
668 | } | |
669 | ||
670 | if (memcmp(bufa, bufb, r) != 0) | |
671 | goto fail; | |
672 | } | |
673 | ||
674 | next: | |
675 | close(fda); | |
676 | close(fdb); | |
677 | continue; | |
678 | ||
679 | fail: | |
680 | if (fda >= 0) | |
681 | close(fda); | |
682 | if (fdb >= 0) | |
683 | close(fdb); | |
684 | ||
685 | return false; | |
686 | } | |
687 | ||
688 | return true; | |
689 | } | |
690 | ||
88ac4084 MM |
691 | static int cmp_modnames(const void *m1, const void *m2) |
692 | { | |
693 | const char *s1 = *(char *const *)m1; | |
694 | const char *s2 = *(char *const *)m2; | |
695 | int i; | |
696 | ||
697 | for (i = 0; s1[i] || s2[i]; i++) { | |
698 | char c1 = s1[i], c2 = s2[i]; | |
699 | if (c1 == '-') | |
700 | c1 = '_'; | |
701 | if (c2 == '-') | |
702 | c2 = '_'; | |
703 | if (c1 != c2) | |
704 | return c1 - c2; | |
705 | } | |
706 | return 0; | |
707 | } | |
708 | ||
709 | /* | |
710 | * Store the expected module names in buf and return a list of pointers to | |
711 | * them. | |
712 | */ | |
713 | static const char **read_expected_modules(const struct test *t, | |
714 | char **buf, int *count) | |
715 | { | |
716 | const char **res; | |
717 | int len; | |
718 | int i; | |
719 | char *p; | |
720 | ||
721 | if (t->modules_loaded[0] == '\0') { | |
722 | *count = 0; | |
723 | *buf = NULL; | |
724 | return NULL; | |
725 | } | |
726 | *buf = strdup(t->modules_loaded); | |
727 | if (!*buf) { | |
728 | *count = -1; | |
729 | return NULL; | |
730 | } | |
731 | len = 1; | |
732 | for (p = *buf; *p; p++) | |
733 | if (*p == ',') | |
734 | len++; | |
735 | res = malloc(sizeof(char *) * len); | |
736 | if (!res) { | |
737 | perror("malloc"); | |
738 | *count = -1; | |
739 | free(*buf); | |
740 | *buf = NULL; | |
741 | return NULL; | |
742 | } | |
743 | i = 0; | |
744 | res[i++] = *buf; | |
745 | for (p = *buf; i < len; p++) | |
746 | if (*p == ',') { | |
747 | *p = '\0'; | |
748 | res[i++] = p + 1; | |
749 | } | |
750 | *count = len; | |
751 | return res; | |
752 | } | |
753 | ||
754 | static char **read_loaded_modules(const struct test *t, char **buf, int *count) | |
755 | { | |
756 | char dirname[PATH_MAX]; | |
757 | DIR *dir; | |
758 | struct dirent *dirent; | |
759 | int i; | |
760 | int len = 0, bufsz; | |
761 | char **res = NULL; | |
762 | char *p; | |
763 | const char *rootfs = t->config[TC_ROOTFS] ? t->config[TC_ROOTFS] : ""; | |
764 | ||
765 | /* Store the entries in /sys/module to res */ | |
766 | if (snprintf(dirname, sizeof(dirname), "%s/sys/module", rootfs) | |
767 | >= (int)sizeof(dirname)) { | |
768 | ERR("rootfs path too long: %s\n", rootfs); | |
769 | *buf = NULL; | |
770 | len = -1; | |
771 | goto out; | |
772 | } | |
773 | dir = opendir(dirname); | |
774 | /* not an error, simply return empty list */ | |
775 | if (!dir) { | |
776 | *buf = NULL; | |
777 | goto out; | |
778 | } | |
779 | bufsz = 0; | |
780 | while ((dirent = readdir(dir))) { | |
781 | if (dirent->d_name[0] == '.') | |
782 | continue; | |
783 | len++; | |
784 | bufsz += strlen(dirent->d_name) + 1; | |
785 | } | |
786 | res = malloc(sizeof(char *) * len); | |
787 | if (!res) { | |
788 | perror("malloc"); | |
789 | len = -1; | |
790 | goto out_dir; | |
791 | } | |
792 | *buf = malloc(bufsz); | |
793 | if (!*buf) { | |
794 | perror("malloc"); | |
795 | free(res); | |
796 | res = NULL; | |
797 | len = -1; | |
798 | goto out_dir; | |
799 | } | |
800 | rewinddir(dir); | |
801 | i = 0; | |
802 | p = *buf; | |
803 | while ((dirent = readdir(dir))) { | |
804 | int size; | |
805 | ||
806 | if (dirent->d_name[0] == '.') | |
807 | continue; | |
808 | size = strlen(dirent->d_name) + 1; | |
809 | memcpy(p, dirent->d_name, size); | |
810 | res[i++] = p; | |
811 | p += size; | |
812 | } | |
813 | out_dir: | |
814 | closedir(dir); | |
815 | out: | |
816 | *count = len; | |
817 | return res; | |
818 | } | |
819 | ||
820 | static int check_loaded_modules(const struct test *t) | |
821 | { | |
822 | int l1, l2, i1, i2; | |
823 | const char **a1; | |
824 | char **a2; | |
825 | char *buf1, *buf2; | |
826 | int err = false; | |
827 | ||
828 | a1 = read_expected_modules(t, &buf1, &l1); | |
829 | if (l1 < 0) | |
830 | return err; | |
831 | a2 = read_loaded_modules(t, &buf2, &l2); | |
832 | if (l2 < 0) | |
833 | goto out_a1; | |
834 | qsort(a1, l1, sizeof(char *), cmp_modnames); | |
835 | qsort(a2, l2, sizeof(char *), cmp_modnames); | |
836 | i1 = i2 = 0; | |
837 | err = true; | |
838 | while (i1 < l1 || i2 < l2) { | |
839 | int cmp; | |
840 | ||
841 | if (i1 >= l1) | |
842 | cmp = 1; | |
843 | else if (i2 >= l2) | |
844 | cmp = -1; | |
845 | else | |
846 | cmp = cmp_modnames(&a1[i1], &a2[i2]); | |
847 | if (cmp == 0) { | |
848 | i1++; | |
849 | i2++; | |
850 | } else if (cmp < 0) { | |
851 | err = false; | |
852 | ERR("module %s not loaded\n", a1[i1]); | |
853 | i1++; | |
854 | } else { | |
855 | err = false; | |
856 | ERR("module %s is loaded but should not be \n", a2[i2]); | |
857 | i2++; | |
858 | } | |
859 | } | |
860 | free(a2); | |
861 | free(buf2); | |
862 | out_a1: | |
863 | free(a1); | |
864 | free(buf1); | |
865 | return err; | |
866 | } | |
867 | ||
3dbb8dea | 868 | static inline int test_run_parent(const struct test *t, int fdout[2], |
b8e344a6 | 869 | int fderr[2], int fdmonitor[2], pid_t child) |
45481ee2 LDM |
870 | { |
871 | pid_t pid; | |
80f9e023 | 872 | int err; |
88ac4084 | 873 | bool matchout, match_modules; |
3dbb8dea LDM |
874 | |
875 | /* Close write-fds */ | |
bd4e7340 | 876 | if (t->output.out != NULL) |
3dbb8dea | 877 | close(fdout[1]); |
bd4e7340 | 878 | if (t->output.err != NULL) |
3dbb8dea | 879 | close(fderr[1]); |
ed8e93fd | 880 | close(fdmonitor[1]); |
3dbb8dea | 881 | |
ed8e93fd | 882 | matchout = test_run_parent_check_outputs(t, fdout[0], fderr[0], |
b8e344a6 | 883 | fdmonitor[0], child); |
3dbb8dea LDM |
884 | |
885 | /* | |
886 | * break pipe on the other end: either child already closed or we want | |
887 | * to stop it | |
888 | */ | |
bd4e7340 | 889 | if (t->output.out != NULL) |
3dbb8dea | 890 | close(fdout[0]); |
bd4e7340 | 891 | if (t->output.err != NULL) |
3dbb8dea | 892 | close(fderr[0]); |
ed8e93fd | 893 | close(fdmonitor[0]); |
45481ee2 LDM |
894 | |
895 | do { | |
896 | pid = wait(&err); | |
897 | if (pid == -1) { | |
898 | ERR("error waitpid(): %m\n"); | |
f988e25c LDM |
899 | err = EXIT_FAILURE; |
900 | goto exit; | |
45481ee2 LDM |
901 | } |
902 | } while (!WIFEXITED(err) && !WIFSIGNALED(err)); | |
903 | ||
3dbb8dea LDM |
904 | if (WIFEXITED(err)) { |
905 | if (WEXITSTATUS(err) != 0) | |
906 | ERR("'%s' [%u] exited with return code %d\n", | |
907 | t->name, pid, WEXITSTATUS(err)); | |
908 | else | |
909 | LOG("'%s' [%u] exited with return code %d\n", | |
910 | t->name, pid, WEXITSTATUS(err)); | |
911 | } else if (WIFSIGNALED(err)) { | |
912 | ERR("'%s' [%u] terminated by signal %d (%s)\n", t->name, pid, | |
913 | WTERMSIG(err), strsignal(WTERMSIG(err))); | |
f988e25c LDM |
914 | err = t->expected_fail ? EXIT_SUCCESS : EXIT_FAILURE; |
915 | goto exit; | |
3dbb8dea LDM |
916 | } |
917 | ||
3e451bfe LDM |
918 | if (matchout) |
919 | matchout = check_generated_files(t); | |
88ac4084 MM |
920 | if (t->modules_loaded) |
921 | match_modules = check_loaded_modules(t); | |
922 | else | |
923 | match_modules = true; | |
3e451bfe | 924 | |
fa0046ba DR |
925 | if (t->expected_fail == false) { |
926 | if (err == 0) { | |
88ac4084 | 927 | if (matchout && match_modules) |
fa0046ba DR |
928 | LOG("%sPASSED%s: %s\n", |
929 | ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF, | |
930 | t->name); | |
931 | else { | |
88ac4084 | 932 | ERR("%sFAILED%s: exit ok but %s do not match: %s\n", |
fa0046ba | 933 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, |
88ac4084 | 934 | matchout ? "loaded modules" : "outputs", |
fa0046ba DR |
935 | t->name); |
936 | err = EXIT_FAILURE; | |
937 | } | |
f429113a | 938 | } else { |
fa0046ba DR |
939 | ERR("%sFAILED%s: %s\n", |
940 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, | |
941 | t->name); | |
f429113a | 942 | } |
fa0046ba DR |
943 | } else { |
944 | if (err == 0) { | |
945 | if (matchout) { | |
d7293a16 | 946 | ERR("%sUNEXPECTED PASS%s: exit with 0: %s\n", |
658e0471 | 947 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, |
fa0046ba DR |
948 | t->name); |
949 | err = EXIT_FAILURE; | |
d7293a16 LDM |
950 | } else { |
951 | ERR("%sUNEXPECTED PASS%s: exit with 0 and outputs do not match: %s\n", | |
952 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, | |
fa0046ba | 953 | t->name); |
d7293a16 LDM |
954 | err = EXIT_FAILURE; |
955 | } | |
fa0046ba | 956 | } else { |
d7293a16 LDM |
957 | if (matchout) { |
958 | LOG("%sEXPECTED FAIL%s: %s\n", | |
fa0046ba DR |
959 | ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF, |
960 | t->name); | |
d7293a16 LDM |
961 | err = EXIT_SUCCESS; |
962 | } else { | |
963 | LOG("%sEXPECTED FAIL%s: exit with %d but outputs do not match: %s\n", | |
964 | ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF, | |
965 | WEXITSTATUS(err), t->name); | |
966 | err = EXIT_FAILURE; | |
967 | } | |
3dbb8dea | 968 | } |
fa0046ba | 969 | } |
45481ee2 | 970 | |
f988e25c LDM |
971 | exit: |
972 | LOG("------\n"); | |
45481ee2 LDM |
973 | return err; |
974 | } | |
975 | ||
f31d49c8 DR |
976 | static int prepend_path(const char *extra) |
977 | { | |
978 | char *oldpath, *newpath; | |
979 | int r; | |
980 | ||
981 | if (extra == NULL) | |
982 | return 0; | |
983 | ||
984 | oldpath = getenv("PATH"); | |
985 | if (oldpath == NULL) | |
986 | return setenv("PATH", extra, 1); | |
987 | ||
988 | if (asprintf(&newpath, "%s:%s", extra, oldpath) < 0) { | |
050db08c | 989 | ERR("failed to allocate memory to new PATH\n"); |
f31d49c8 DR |
990 | return -1; |
991 | } | |
992 | ||
993 | r = setenv("PATH", newpath, 1); | |
994 | free(newpath); | |
995 | ||
996 | return r; | |
997 | } | |
998 | ||
45481ee2 LDM |
999 | int test_run(const struct test *t) |
1000 | { | |
80f9e023 | 1001 | pid_t pid; |
3dbb8dea LDM |
1002 | int fdout[2]; |
1003 | int fderr[2]; | |
ed8e93fd | 1004 | int fdmonitor[2]; |
80f9e023 | 1005 | |
ed2df4e9 LDM |
1006 | if (t->need_spawn && oneshot) |
1007 | test_run_spawned(t); | |
1008 | ||
bd4e7340 | 1009 | if (t->output.out != NULL) { |
3dbb8dea LDM |
1010 | if (pipe(fdout) != 0) { |
1011 | ERR("could not create out pipe for %s\n", t->name); | |
1012 | return EXIT_FAILURE; | |
1013 | } | |
1014 | } | |
1015 | ||
bd4e7340 | 1016 | if (t->output.err != NULL) { |
3dbb8dea LDM |
1017 | if (pipe(fderr) != 0) { |
1018 | ERR("could not create err pipe for %s\n", t->name); | |
1019 | return EXIT_FAILURE; | |
1020 | } | |
1021 | } | |
1022 | ||
ed8e93fd LDM |
1023 | if (pipe(fdmonitor) != 0) { |
1024 | ERR("could not create monitor pipe for %s\n", t->name); | |
1025 | return EXIT_FAILURE; | |
1026 | } | |
1027 | ||
f31d49c8 DR |
1028 | if (prepend_path(t->path) < 0) { |
1029 | ERR("failed to prepend '%s' to PATH\n", t->path); | |
1030 | return EXIT_FAILURE; | |
1031 | } | |
1032 | ||
80f9e023 LDM |
1033 | LOG("running %s, in forked context\n", t->name); |
1034 | ||
1035 | pid = fork(); | |
1036 | if (pid < 0) { | |
1037 | ERR("could not fork(): %m\n"); | |
1038 | LOG("FAILED: %s\n", t->name); | |
1039 | return EXIT_FAILURE; | |
1040 | } | |
1041 | ||
45481ee2 | 1042 | if (pid > 0) |
b8e344a6 | 1043 | return test_run_parent(t, fdout, fderr, fdmonitor, pid); |
80f9e023 | 1044 | |
ed8e93fd | 1045 | return test_run_child(t, fdout, fderr, fdmonitor); |
80f9e023 | 1046 | } |