]>
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 | ||
3dbb8dea | 275 | static inline bool test_run_parent_check_outputs(const struct test *t, |
b8e344a6 | 276 | int fdout, int fderr, int fdmonitor, pid_t child) |
3dbb8dea | 277 | { |
ed8e93fd | 278 | struct epoll_event ep_outpipe, ep_errpipe, ep_monitor; |
3dbb8dea | 279 | int err, fd_ep, fd_matchout = -1, fd_matcherr = -1; |
b8e344a6 | 280 | unsigned long long end_usec, start_usec; |
3dbb8dea | 281 | |
3dbb8dea LDM |
282 | fd_ep = epoll_create1(EPOLL_CLOEXEC); |
283 | if (fd_ep < 0) { | |
284 | ERR("could not create epoll fd: %m\n"); | |
285 | return false; | |
286 | } | |
287 | ||
bd4e7340 JS |
288 | if (t->output.out != NULL) { |
289 | fd_matchout = open(t->output.out, O_RDONLY); | |
3dbb8dea LDM |
290 | if (fd_matchout < 0) { |
291 | err = -errno; | |
292 | ERR("could not open %s for read: %m\n", | |
bd4e7340 | 293 | t->output.out); |
3dbb8dea LDM |
294 | goto out; |
295 | } | |
296 | memset(&ep_outpipe, 0, sizeof(struct epoll_event)); | |
297 | ep_outpipe.events = EPOLLIN; | |
298 | ep_outpipe.data.ptr = &fdout; | |
299 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdout, &ep_outpipe) < 0) { | |
300 | err = -errno; | |
301 | ERR("could not add fd to epoll: %m\n"); | |
302 | goto out; | |
303 | } | |
304 | } else | |
305 | fdout = -1; | |
306 | ||
bd4e7340 JS |
307 | if (t->output.err != NULL) { |
308 | fd_matcherr = open(t->output.err, O_RDONLY); | |
3dbb8dea LDM |
309 | if (fd_matcherr < 0) { |
310 | err = -errno; | |
311 | ERR("could not open %s for read: %m\n", | |
bd4e7340 | 312 | t->output.err); |
3dbb8dea LDM |
313 | goto out; |
314 | ||
315 | } | |
316 | memset(&ep_errpipe, 0, sizeof(struct epoll_event)); | |
317 | ep_errpipe.events = EPOLLIN; | |
318 | ep_errpipe.data.ptr = &fderr; | |
319 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fderr, &ep_errpipe) < 0) { | |
320 | err = -errno; | |
321 | ERR("could not add fd to epoll: %m\n"); | |
322 | goto out; | |
323 | } | |
324 | } else | |
325 | fderr = -1; | |
326 | ||
ed8e93fd LDM |
327 | memset(&ep_monitor, 0, sizeof(struct epoll_event)); |
328 | ep_monitor.events = EPOLLHUP; | |
329 | ep_monitor.data.ptr = &fdmonitor; | |
330 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdmonitor, &ep_monitor) < 0) { | |
331 | err = -errno; | |
332 | ERR("could not add monitor fd to epoll: %m\n"); | |
333 | goto out; | |
334 | } | |
335 | ||
b8e344a6 LDM |
336 | start_usec = now_usec(); |
337 | end_usec = start_usec + TEST_TIMEOUT_USEC; | |
338 | ||
ed8e93fd | 339 | for (err = 0; fdmonitor >= 0 || fdout >= 0 || fderr >= 0;) { |
b8e344a6 | 340 | int fdcount, i, timeout; |
3dbb8dea | 341 | struct epoll_event ev[4]; |
b8e344a6 | 342 | unsigned long long curr_usec = now_usec(); |
3dbb8dea | 343 | |
b8e344a6 LDM |
344 | if (curr_usec > end_usec) |
345 | break; | |
346 | ||
347 | timeout = (end_usec - curr_usec) / USEC_PER_MSEC; | |
348 | fdcount = epoll_wait(fd_ep, ev, 4, timeout); | |
3dbb8dea LDM |
349 | if (fdcount < 0) { |
350 | if (errno == EINTR) | |
351 | continue; | |
352 | err = -errno; | |
353 | ERR("could not poll: %m\n"); | |
354 | goto out; | |
355 | } | |
356 | ||
357 | for (i = 0; i < fdcount; i++) { | |
358 | int *fd = ev[i].data.ptr; | |
359 | ||
360 | if (ev[i].events & EPOLLIN) { | |
361 | ssize_t r, done = 0; | |
362 | char buf[4096]; | |
363 | char bufmatch[4096]; | |
364 | int fd_match; | |
365 | ||
366 | /* | |
367 | * compare the output from child with the one | |
368 | * saved as correct | |
369 | */ | |
370 | ||
371 | r = read(*fd, buf, sizeof(buf) - 1); | |
372 | if (r <= 0) | |
373 | continue; | |
374 | ||
375 | if (*fd == fdout) | |
376 | fd_match = fd_matchout; | |
ed8e93fd | 377 | else if (*fd == fderr) |
3dbb8dea | 378 | fd_match = fd_matcherr; |
ed8e93fd LDM |
379 | else { |
380 | ERR("Unexpected activity on monitor pipe\n"); | |
381 | err = -EINVAL; | |
382 | goto out; | |
383 | } | |
3dbb8dea LDM |
384 | |
385 | for (;;) { | |
386 | int rmatch = read(fd_match, | |
387 | bufmatch + done, r - done); | |
388 | if (rmatch == 0) | |
389 | break; | |
390 | ||
391 | if (rmatch < 0) { | |
392 | if (errno == EINTR) | |
393 | continue; | |
394 | err = -errno; | |
395 | ERR("could not read match fd %d\n", | |
396 | fd_match); | |
397 | goto out; | |
398 | } | |
399 | ||
400 | done += rmatch; | |
401 | } | |
402 | ||
403 | buf[r] = '\0'; | |
404 | bufmatch[r] = '\0'; | |
5c42c5fc | 405 | if (!streq(buf, bufmatch)) { |
3dbb8dea LDM |
406 | ERR("Outputs do not match on %s:\n", |
407 | fd_match == fd_matchout ? "stdout" : "stderr"); | |
408 | ERR("correct:\n%s\n", bufmatch); | |
409 | ERR("wrong:\n%s\n", buf); | |
410 | err = -1; | |
411 | goto out; | |
412 | } | |
413 | } else if (ev[i].events & EPOLLHUP) { | |
414 | if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, | |
415 | *fd, NULL) < 0) { | |
416 | ERR("could not remove fd %d from epoll: %m\n", | |
417 | *fd); | |
418 | } | |
419 | *fd = -1; | |
420 | } | |
421 | } | |
b8e344a6 | 422 | } |
3dbb8dea | 423 | |
b8e344a6 LDM |
424 | if (err == 0 && fdmonitor >= 0) { |
425 | err = -EINVAL; | |
426 | ERR("Test '%s' timed out, killing %d\n", t->name, child); | |
427 | kill(child, SIGKILL); | |
3dbb8dea | 428 | } |
b8e344a6 | 429 | |
3dbb8dea LDM |
430 | out: |
431 | if (fd_matchout >= 0) | |
432 | close(fd_matchout); | |
433 | if (fd_matcherr >= 0) | |
434 | close(fd_matcherr); | |
435 | if (fd_ep >= 0) | |
436 | close(fd_ep); | |
437 | return err == 0; | |
438 | } | |
439 | ||
3e451bfe LDM |
440 | static inline int safe_read(int fd, void *buf, size_t count) |
441 | { | |
442 | int r; | |
443 | ||
444 | while (1) { | |
445 | r = read(fd, buf, count); | |
2ad09ccf | 446 | if (r == -1 && errno == EINTR) |
3e451bfe LDM |
447 | continue; |
448 | break; | |
449 | } | |
450 | ||
451 | return r; | |
452 | } | |
453 | ||
454 | static bool check_generated_files(const struct test *t) | |
455 | { | |
456 | const struct keyval *k; | |
457 | ||
458 | /* This is not meant to be a diff replacement, just stupidly check if | |
459 | * the files match. Bear in mind they can be binary files */ | |
460 | for (k = t->output.files; k && k->key; k++) { | |
461 | struct stat sta, stb; | |
462 | int fda = -1, fdb = -1; | |
463 | char bufa[4096]; | |
464 | char bufb[4096]; | |
465 | ||
466 | fda = open(k->key, O_RDONLY); | |
467 | if (fda < 0) { | |
468 | ERR("could not open %s\n - %m\n", k->key); | |
469 | goto fail; | |
470 | } | |
471 | ||
472 | fdb = open(k->val, O_RDONLY); | |
473 | if (fdb < 0) { | |
474 | ERR("could not open %s\n - %m\n", k->val); | |
475 | goto fail; | |
476 | } | |
477 | ||
478 | if (fstat(fda, &sta) != 0) { | |
479 | ERR("could not fstat %d %s\n - %m\n", fda, k->key); | |
480 | goto fail; | |
481 | } | |
482 | ||
483 | if (fstat(fdb, &stb) != 0) { | |
484 | ERR("could not fstat %d %s\n - %m\n", fdb, k->key); | |
485 | goto fail; | |
486 | } | |
487 | ||
488 | if (sta.st_size != stb.st_size) { | |
489 | ERR("sizes do not match %s %s\n", k->key, k->val); | |
490 | goto fail; | |
491 | } | |
492 | ||
493 | for (;;) { | |
494 | int r, done; | |
495 | ||
496 | r = safe_read(fda, bufa, sizeof(bufa)); | |
497 | if (r < 0) | |
498 | goto fail; | |
499 | ||
500 | if (r == 0) | |
501 | /* size is already checked, go to next file */ | |
502 | goto next; | |
503 | ||
504 | for (done = 0; done < r;) { | |
505 | int r2 = safe_read(fdb, bufb + done, r - done); | |
506 | ||
507 | if (r2 <= 0) | |
508 | goto fail; | |
509 | ||
510 | done += r2; | |
511 | } | |
512 | ||
513 | if (memcmp(bufa, bufb, r) != 0) | |
514 | goto fail; | |
515 | } | |
516 | ||
517 | next: | |
518 | close(fda); | |
519 | close(fdb); | |
520 | continue; | |
521 | ||
522 | fail: | |
523 | if (fda >= 0) | |
524 | close(fda); | |
525 | if (fdb >= 0) | |
526 | close(fdb); | |
527 | ||
528 | return false; | |
529 | } | |
530 | ||
531 | return true; | |
532 | } | |
533 | ||
88ac4084 MM |
534 | static int cmp_modnames(const void *m1, const void *m2) |
535 | { | |
536 | const char *s1 = *(char *const *)m1; | |
537 | const char *s2 = *(char *const *)m2; | |
538 | int i; | |
539 | ||
540 | for (i = 0; s1[i] || s2[i]; i++) { | |
541 | char c1 = s1[i], c2 = s2[i]; | |
542 | if (c1 == '-') | |
543 | c1 = '_'; | |
544 | if (c2 == '-') | |
545 | c2 = '_'; | |
546 | if (c1 != c2) | |
547 | return c1 - c2; | |
548 | } | |
549 | return 0; | |
550 | } | |
551 | ||
552 | /* | |
553 | * Store the expected module names in buf and return a list of pointers to | |
554 | * them. | |
555 | */ | |
556 | static const char **read_expected_modules(const struct test *t, | |
557 | char **buf, int *count) | |
558 | { | |
559 | const char **res; | |
560 | int len; | |
561 | int i; | |
562 | char *p; | |
563 | ||
564 | if (t->modules_loaded[0] == '\0') { | |
565 | *count = 0; | |
566 | *buf = NULL; | |
567 | return NULL; | |
568 | } | |
569 | *buf = strdup(t->modules_loaded); | |
570 | if (!*buf) { | |
571 | *count = -1; | |
572 | return NULL; | |
573 | } | |
574 | len = 1; | |
575 | for (p = *buf; *p; p++) | |
576 | if (*p == ',') | |
577 | len++; | |
578 | res = malloc(sizeof(char *) * len); | |
579 | if (!res) { | |
580 | perror("malloc"); | |
581 | *count = -1; | |
582 | free(*buf); | |
583 | *buf = NULL; | |
584 | return NULL; | |
585 | } | |
586 | i = 0; | |
587 | res[i++] = *buf; | |
588 | for (p = *buf; i < len; p++) | |
589 | if (*p == ',') { | |
590 | *p = '\0'; | |
591 | res[i++] = p + 1; | |
592 | } | |
593 | *count = len; | |
594 | return res; | |
595 | } | |
596 | ||
597 | static char **read_loaded_modules(const struct test *t, char **buf, int *count) | |
598 | { | |
599 | char dirname[PATH_MAX]; | |
600 | DIR *dir; | |
601 | struct dirent *dirent; | |
602 | int i; | |
603 | int len = 0, bufsz; | |
604 | char **res = NULL; | |
605 | char *p; | |
606 | const char *rootfs = t->config[TC_ROOTFS] ? t->config[TC_ROOTFS] : ""; | |
607 | ||
608 | /* Store the entries in /sys/module to res */ | |
609 | if (snprintf(dirname, sizeof(dirname), "%s/sys/module", rootfs) | |
610 | >= (int)sizeof(dirname)) { | |
611 | ERR("rootfs path too long: %s\n", rootfs); | |
612 | *buf = NULL; | |
613 | len = -1; | |
614 | goto out; | |
615 | } | |
616 | dir = opendir(dirname); | |
617 | /* not an error, simply return empty list */ | |
618 | if (!dir) { | |
619 | *buf = NULL; | |
620 | goto out; | |
621 | } | |
622 | bufsz = 0; | |
623 | while ((dirent = readdir(dir))) { | |
624 | if (dirent->d_name[0] == '.') | |
625 | continue; | |
626 | len++; | |
627 | bufsz += strlen(dirent->d_name) + 1; | |
628 | } | |
629 | res = malloc(sizeof(char *) * len); | |
630 | if (!res) { | |
631 | perror("malloc"); | |
632 | len = -1; | |
633 | goto out_dir; | |
634 | } | |
635 | *buf = malloc(bufsz); | |
636 | if (!*buf) { | |
637 | perror("malloc"); | |
638 | free(res); | |
639 | res = NULL; | |
640 | len = -1; | |
641 | goto out_dir; | |
642 | } | |
643 | rewinddir(dir); | |
644 | i = 0; | |
645 | p = *buf; | |
646 | while ((dirent = readdir(dir))) { | |
647 | int size; | |
648 | ||
649 | if (dirent->d_name[0] == '.') | |
650 | continue; | |
651 | size = strlen(dirent->d_name) + 1; | |
652 | memcpy(p, dirent->d_name, size); | |
653 | res[i++] = p; | |
654 | p += size; | |
655 | } | |
656 | out_dir: | |
657 | closedir(dir); | |
658 | out: | |
659 | *count = len; | |
660 | return res; | |
661 | } | |
662 | ||
663 | static int check_loaded_modules(const struct test *t) | |
664 | { | |
665 | int l1, l2, i1, i2; | |
666 | const char **a1; | |
667 | char **a2; | |
668 | char *buf1, *buf2; | |
669 | int err = false; | |
670 | ||
671 | a1 = read_expected_modules(t, &buf1, &l1); | |
672 | if (l1 < 0) | |
673 | return err; | |
674 | a2 = read_loaded_modules(t, &buf2, &l2); | |
675 | if (l2 < 0) | |
676 | goto out_a1; | |
677 | qsort(a1, l1, sizeof(char *), cmp_modnames); | |
678 | qsort(a2, l2, sizeof(char *), cmp_modnames); | |
679 | i1 = i2 = 0; | |
680 | err = true; | |
681 | while (i1 < l1 || i2 < l2) { | |
682 | int cmp; | |
683 | ||
684 | if (i1 >= l1) | |
685 | cmp = 1; | |
686 | else if (i2 >= l2) | |
687 | cmp = -1; | |
688 | else | |
689 | cmp = cmp_modnames(&a1[i1], &a2[i2]); | |
690 | if (cmp == 0) { | |
691 | i1++; | |
692 | i2++; | |
693 | } else if (cmp < 0) { | |
694 | err = false; | |
695 | ERR("module %s not loaded\n", a1[i1]); | |
696 | i1++; | |
697 | } else { | |
698 | err = false; | |
699 | ERR("module %s is loaded but should not be \n", a2[i2]); | |
700 | i2++; | |
701 | } | |
702 | } | |
703 | free(a2); | |
704 | free(buf2); | |
705 | out_a1: | |
706 | free(a1); | |
707 | free(buf1); | |
708 | return err; | |
709 | } | |
710 | ||
3dbb8dea | 711 | static inline int test_run_parent(const struct test *t, int fdout[2], |
b8e344a6 | 712 | int fderr[2], int fdmonitor[2], pid_t child) |
45481ee2 LDM |
713 | { |
714 | pid_t pid; | |
80f9e023 | 715 | int err; |
88ac4084 | 716 | bool matchout, match_modules; |
3dbb8dea LDM |
717 | |
718 | /* Close write-fds */ | |
bd4e7340 | 719 | if (t->output.out != NULL) |
3dbb8dea | 720 | close(fdout[1]); |
bd4e7340 | 721 | if (t->output.err != NULL) |
3dbb8dea | 722 | close(fderr[1]); |
ed8e93fd | 723 | close(fdmonitor[1]); |
3dbb8dea | 724 | |
ed8e93fd | 725 | matchout = test_run_parent_check_outputs(t, fdout[0], fderr[0], |
b8e344a6 | 726 | fdmonitor[0], child); |
3dbb8dea LDM |
727 | |
728 | /* | |
729 | * break pipe on the other end: either child already closed or we want | |
730 | * to stop it | |
731 | */ | |
bd4e7340 | 732 | if (t->output.out != NULL) |
3dbb8dea | 733 | close(fdout[0]); |
bd4e7340 | 734 | if (t->output.err != NULL) |
3dbb8dea | 735 | close(fderr[0]); |
ed8e93fd | 736 | close(fdmonitor[0]); |
45481ee2 LDM |
737 | |
738 | do { | |
739 | pid = wait(&err); | |
740 | if (pid == -1) { | |
741 | ERR("error waitpid(): %m\n"); | |
f988e25c LDM |
742 | err = EXIT_FAILURE; |
743 | goto exit; | |
45481ee2 LDM |
744 | } |
745 | } while (!WIFEXITED(err) && !WIFSIGNALED(err)); | |
746 | ||
3dbb8dea LDM |
747 | if (WIFEXITED(err)) { |
748 | if (WEXITSTATUS(err) != 0) | |
749 | ERR("'%s' [%u] exited with return code %d\n", | |
750 | t->name, pid, WEXITSTATUS(err)); | |
751 | else | |
752 | LOG("'%s' [%u] exited with return code %d\n", | |
753 | t->name, pid, WEXITSTATUS(err)); | |
754 | } else if (WIFSIGNALED(err)) { | |
755 | ERR("'%s' [%u] terminated by signal %d (%s)\n", t->name, pid, | |
756 | WTERMSIG(err), strsignal(WTERMSIG(err))); | |
f988e25c LDM |
757 | err = t->expected_fail ? EXIT_SUCCESS : EXIT_FAILURE; |
758 | goto exit; | |
3dbb8dea LDM |
759 | } |
760 | ||
3e451bfe LDM |
761 | if (matchout) |
762 | matchout = check_generated_files(t); | |
88ac4084 MM |
763 | if (t->modules_loaded) |
764 | match_modules = check_loaded_modules(t); | |
765 | else | |
766 | match_modules = true; | |
3e451bfe | 767 | |
fa0046ba DR |
768 | if (t->expected_fail == false) { |
769 | if (err == 0) { | |
88ac4084 | 770 | if (matchout && match_modules) |
fa0046ba DR |
771 | LOG("%sPASSED%s: %s\n", |
772 | ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF, | |
773 | t->name); | |
774 | else { | |
88ac4084 | 775 | ERR("%sFAILED%s: exit ok but %s do not match: %s\n", |
fa0046ba | 776 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, |
88ac4084 | 777 | matchout ? "loaded modules" : "outputs", |
fa0046ba DR |
778 | t->name); |
779 | err = EXIT_FAILURE; | |
780 | } | |
f429113a | 781 | } else { |
fa0046ba DR |
782 | ERR("%sFAILED%s: %s\n", |
783 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, | |
784 | t->name); | |
f429113a | 785 | } |
fa0046ba DR |
786 | } else { |
787 | if (err == 0) { | |
788 | if (matchout) { | |
d7293a16 | 789 | ERR("%sUNEXPECTED PASS%s: exit with 0: %s\n", |
658e0471 | 790 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, |
fa0046ba DR |
791 | t->name); |
792 | err = EXIT_FAILURE; | |
d7293a16 LDM |
793 | } else { |
794 | ERR("%sUNEXPECTED PASS%s: exit with 0 and outputs do not match: %s\n", | |
795 | ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF, | |
fa0046ba | 796 | t->name); |
d7293a16 LDM |
797 | err = EXIT_FAILURE; |
798 | } | |
fa0046ba | 799 | } else { |
d7293a16 LDM |
800 | if (matchout) { |
801 | LOG("%sEXPECTED FAIL%s: %s\n", | |
fa0046ba DR |
802 | ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF, |
803 | t->name); | |
d7293a16 LDM |
804 | err = EXIT_SUCCESS; |
805 | } else { | |
806 | LOG("%sEXPECTED FAIL%s: exit with %d but outputs do not match: %s\n", | |
807 | ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF, | |
808 | WEXITSTATUS(err), t->name); | |
809 | err = EXIT_FAILURE; | |
810 | } | |
3dbb8dea | 811 | } |
fa0046ba | 812 | } |
45481ee2 | 813 | |
f988e25c LDM |
814 | exit: |
815 | LOG("------\n"); | |
45481ee2 LDM |
816 | return err; |
817 | } | |
818 | ||
f31d49c8 DR |
819 | static int prepend_path(const char *extra) |
820 | { | |
821 | char *oldpath, *newpath; | |
822 | int r; | |
823 | ||
824 | if (extra == NULL) | |
825 | return 0; | |
826 | ||
827 | oldpath = getenv("PATH"); | |
828 | if (oldpath == NULL) | |
829 | return setenv("PATH", extra, 1); | |
830 | ||
831 | if (asprintf(&newpath, "%s:%s", extra, oldpath) < 0) { | |
050db08c | 832 | ERR("failed to allocate memory to new PATH\n"); |
f31d49c8 DR |
833 | return -1; |
834 | } | |
835 | ||
836 | r = setenv("PATH", newpath, 1); | |
837 | free(newpath); | |
838 | ||
839 | return r; | |
840 | } | |
841 | ||
45481ee2 LDM |
842 | int test_run(const struct test *t) |
843 | { | |
80f9e023 | 844 | pid_t pid; |
3dbb8dea LDM |
845 | int fdout[2]; |
846 | int fderr[2]; | |
ed8e93fd | 847 | int fdmonitor[2]; |
80f9e023 | 848 | |
ed2df4e9 LDM |
849 | if (t->need_spawn && oneshot) |
850 | test_run_spawned(t); | |
851 | ||
bd4e7340 | 852 | if (t->output.out != NULL) { |
3dbb8dea LDM |
853 | if (pipe(fdout) != 0) { |
854 | ERR("could not create out pipe for %s\n", t->name); | |
855 | return EXIT_FAILURE; | |
856 | } | |
857 | } | |
858 | ||
bd4e7340 | 859 | if (t->output.err != NULL) { |
3dbb8dea LDM |
860 | if (pipe(fderr) != 0) { |
861 | ERR("could not create err pipe for %s\n", t->name); | |
862 | return EXIT_FAILURE; | |
863 | } | |
864 | } | |
865 | ||
ed8e93fd LDM |
866 | if (pipe(fdmonitor) != 0) { |
867 | ERR("could not create monitor pipe for %s\n", t->name); | |
868 | return EXIT_FAILURE; | |
869 | } | |
870 | ||
f31d49c8 DR |
871 | if (prepend_path(t->path) < 0) { |
872 | ERR("failed to prepend '%s' to PATH\n", t->path); | |
873 | return EXIT_FAILURE; | |
874 | } | |
875 | ||
80f9e023 LDM |
876 | LOG("running %s, in forked context\n", t->name); |
877 | ||
878 | pid = fork(); | |
879 | if (pid < 0) { | |
880 | ERR("could not fork(): %m\n"); | |
881 | LOG("FAILED: %s\n", t->name); | |
882 | return EXIT_FAILURE; | |
883 | } | |
884 | ||
45481ee2 | 885 | if (pid > 0) |
b8e344a6 | 886 | return test_run_parent(t, fdout, fderr, fdmonitor, pid); |
80f9e023 | 887 | |
ed8e93fd | 888 | return test_run_child(t, fdout, fderr, fdmonitor); |
80f9e023 | 889 | } |