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