blob: 37265fe50951e717a8ba093f420e43a0ba619310 [file] [log] [blame]
Cyril Hrubisbbdb9f72016-03-16 15:53:57 +01001/*
2 * Copyright (c) 2015-2016 Cyril Hrubis <chrubis@suse.cz>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <stdio.h>
19#include <stdarg.h>
20#include <unistd.h>
21#include <string.h>
22#include <stdlib.h>
23#include <errno.h>
24#include <sys/types.h>
25#include <sys/wait.h>
26#include <sys/time.h>
27
28#define TST_NO_DEFAULT_MAIN
29#include "tst_test.h"
30#include "tst_kvercmp.h"
31#include "tst_device.h"
32#include "lapi/futex.h"
33
34#include "old_resource.h"
35#include "old_device.h"
36#include "old_tmpdir.h"
37
38struct tst_test *tst_test;
39
40static char tmpdir_created;
41static int iterations = 1;
42static float duration = -1;
43static pid_t main_pid;
44
45struct results {
46 unsigned int passed;
47 unsigned int skipped;
48 unsigned int failed;
49 unsigned int warnings;
50};
51
52static struct results *results;
53
54static int ipc_fd;
55
56extern void *tst_futexes;
57extern unsigned int tst_max_futexes;
58
59#define IPC_ENV_VAR "LTP_IPC_PATH"
60
61static char ipc_path[1024];
62const char *tst_ipc_path = ipc_path;
63char *const tst_ipc_envp[] = {ipc_path, NULL};
64
65static char shm_path[1024];
66
67static void setup_ipc(void)
68{
69 size_t size = getpagesize();
70
71 //TODO: Fallback to tst_tmpdir() if /dev/shm does not exits?
72 snprintf(shm_path, sizeof(shm_path), "/dev/shm/ltp_%s_%d",
73 tst_test->tid, getpid());
74
75 ipc_fd = open(shm_path, O_CREAT | O_EXCL | O_RDWR, 0600);
76 if (ipc_fd < 0)
77 tst_brk(TBROK | TERRNO, "open(%s)", shm_path);
78
79 SAFE_FTRUNCATE(ipc_fd, size);
80
81 results = SAFE_MMAP(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ipc_fd, 0);
82
83 /* Checkpoints needs to be accessible from processes started by exec() */
84 if (tst_test->needs_checkpoints)
85 sprintf(ipc_path, IPC_ENV_VAR "=%s", shm_path);
86 else
87 SAFE_UNLINK(shm_path);
88
89 SAFE_CLOSE(ipc_fd);
90
91 if (tst_test->needs_checkpoints) {
92 tst_futexes = (char*)results + sizeof(struct results);
93 tst_max_futexes = (size - sizeof(struct results))/sizeof(futex_t);
94 }
95}
96
97static void cleanup_ipc(void)
98{
99 size_t size = getpagesize();
100
101 if (ipc_fd > 0 && close(ipc_fd))
102 tst_res(TWARN | TERRNO, "close(ipc_fd) failed");
103
104 if (!access(shm_path, F_OK) && unlink(shm_path))
105 tst_res(TWARN | TERRNO, "unlink(%s) failed", shm_path);
106
107 msync((void*)results, size, MS_SYNC);
108 munmap((void*)results, size);
109}
110
111void tst_reinit(void)
112{
113 const char *path = getenv("LTP_IPC_PATH");
114 size_t size = getpagesize();
115 int fd;
116 void *ptr;
117
118 if (!path)
119 tst_brk(TBROK, "LTP_IPC_PATH is not defined");
120
121 if (access(path, F_OK))
122 tst_brk(TBROK, "File %s does not exist!", path);
123
124 fd = SAFE_OPEN(path, O_RDWR);
125
126 ptr = SAFE_MMAP(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
127 tst_futexes = (char*)ptr + sizeof(struct results);
128 tst_max_futexes = (size - sizeof(struct results))/sizeof(futex_t);
129
130 SAFE_CLOSE(fd);
131}
132
133static void update_results(const char *file, unsigned int lineno, int ttype)
134{
135 if (!results) {
136 tst_brk(TBROK,
137 "%s: %d: Results IPC not initialized!", file, lineno);
138 }
139
140 switch (ttype) {
141 case TCONF:
142 tst_atomic_inc(&results->skipped);
143 break;
144 case TPASS:
145 tst_atomic_inc(&results->passed);
146 break;
147 case TWARN:
148 tst_atomic_inc(&results->warnings);
149 break;
150 case TFAIL:
151 tst_atomic_inc(&results->failed);
152 break;
153 }
154}
155
156static void print_result(const char *file, const int lineno, int ttype,
157 const char *fmt, va_list va)
158{
159 char buf[1024];
160 char *str = buf;
161 int ret, size = sizeof(buf);
162 const char *str_errno = NULL;
163 const char *res;
164
165 switch (TTYPE_RESULT(ttype)) {
166 case TPASS:
167 res = "PASS";
168 break;
169 case TFAIL:
170 res = "FAIL";
171 break;
172 case TBROK:
173 res = "BROK";
174 break;
175 case TCONF:
176 res = "CONF";
177 break;
178 case TWARN:
179 res = "WARN";
180 break;
181 case TINFO:
182 res = "INFO";
183 break;
184 default:
185 tst_brk(TBROK, "Invalid ttype value %i", ttype);
186 }
187
188 if (ttype & TERRNO)
189 str_errno = tst_strerrno(errno);
190
191 if (ttype & TTERRNO)
192 str_errno = tst_strerrno(TEST_ERRNO);
193
194 ret = snprintf(str, size, "%s:%i: %s: ", file, lineno, res);
195
196 str += ret;
197 size -= ret;
198
199 ret = vsnprintf(str, size, fmt, va);
200
201 str += ret;
202 size -= ret;
203
204 if (str_errno)
205 snprintf(str, size, ": %s\n", str_errno);
206 else
207 snprintf(str, size, "\n");
208
209 fputs(buf, stderr);
210}
211
212void tst_vres_(const char *file, const int lineno, int ttype,
213 const char *fmt, va_list va)
214{
215 print_result(file, lineno, ttype, fmt, va);
216
217 update_results(file, lineno, TTYPE_RESULT(ttype));
218}
219
220void tst_vbrk_(const char *file, const int lineno, int ttype,
221 const char *fmt, va_list va) __attribute__((noreturn));
222
223static void do_cleanup(void);
224
225void tst_vbrk_(const char *file, const int lineno, int ttype,
226 const char *fmt, va_list va)
227{
228 print_result(file, lineno, ttype, fmt, va);
229
230 if (getpid() == main_pid) {
231 do_cleanup();
232 cleanup_ipc();
233 }
234
235 exit(TTYPE_RESULT(ttype));
236}
237
238void tst_res_(const char *file, const int lineno, int ttype,
239 const char *fmt, ...)
240{
241 va_list va;
242
243 va_start(va, fmt);
244 tst_vres_(file, lineno, ttype, fmt, va);
245 va_end(va);
246}
247
248void tst_brk_(const char *file, const int lineno, int ttype,
249 const char *fmt, ...)
250{
251 va_list va;
252
253 va_start(va, fmt);
254 tst_vbrk_(file, lineno, ttype, fmt, va);
255 va_end(va);
256}
257
258static void check_child_status(pid_t pid, int status)
259{
260 int ret;
261
262 if (WIFSIGNALED(status)) {
263 tst_brk(TBROK, "Child (%i) killed by signal %s",
264 pid, tst_strsig(WTERMSIG(status)));
265 }
266
267 if (!(WIFEXITED(status)))
268 tst_brk(TBROK, "Child (%i) exitted abnormaly", pid);
269
270 ret = WEXITSTATUS(status);
271 switch (ret) {
272 case TPASS:
273 break;
274 case TBROK:
275 case TCONF:
276 tst_brk(ret, "Reported by child (%i)", pid);
277 default:
278 tst_brk(TBROK, "Invalid child (%i) exit value %i", pid, ret);
279 }
280}
281
282static void reap_children(void)
283{
284 int status;
285 pid_t pid;
286
287 for (;;) {
288 pid = wait(&status);
289
290 if (pid > 0) {
291 check_child_status(pid, status);
292 continue;
293 }
294
295 if (errno == ECHILD)
296 break;
297
298 if (errno == EINTR)
299 continue;
300
301 tst_brk(TBROK | TERRNO, "wait() failed");
302 }
303}
304
305
306pid_t safe_fork(const char *filename, unsigned int lineno)
307{
308 pid_t pid;
309
310 if (!tst_test->forks_child)
311 tst_brk(TBROK, "test.forks_child must be set!");
312
313 fflush(stdout);
314
315 pid = fork();
316 if (pid < 0)
317 tst_brk_(filename, lineno, TBROK | TERRNO, "fork() failed");
318
319 return pid;
320}
321
322static struct option {
323 char *optstr;
324 char *help;
325} options[] = {
326 {"h", "-h Prints this help"},
327 {"i:", "-i n Execute test n times"},
328 {"I:", "-I x Execute test for n seconds"},
329 {"C:", "-C ARG Run child process with ARG arguments (used internally)"},
330};
331
332static void print_help(void)
333{
334 unsigned int i;
335
336 for (i = 0; i < ARRAY_SIZE(options); i++)
337 fprintf(stderr, "%s\n", options[i].help);
338
339 if (!tst_test->options)
340 return;
341
342 for (i = 0; tst_test->options[i].optstr; i++)
343 fprintf(stderr, "%s", tst_test->options[i].help);
344}
345
346static void check_option_collision(void)
347{
348 unsigned int i, j;
349 struct tst_option *toptions = tst_test->options;
350
351 if (!toptions)
352 return;
353
354 for (i = 0; toptions[i].optstr; i++) {
355 for (j = 0; j < ARRAY_SIZE(options); j++) {
356 if (toptions[i].optstr[0] == options[j].optstr[0]) {
357 tst_brk(TBROK, "Option collision '%s'",
358 options[j].help);
359 }
360 }
361 }
362}
363
364static unsigned int count_options(void)
365{
366 unsigned int i;
367
368 if (!tst_test->options)
369 return 0;
370
371 for (i = 0; tst_test->options[i].optstr; i++);
372
373 return i;
374}
375
376static void parse_topt(unsigned int topts_len, int opt, char *optarg)
377{
378 unsigned int i;
379 struct tst_option *toptions = tst_test->options;
380
381 for (i = 0; i < topts_len; i++) {
382 if (toptions[i].optstr[0] == opt)
383 break;
384 }
385
386 if (i >= topts_len)
387 tst_brk(TBROK, "Invalid option '%c' (should not happen)", opt);
388
389 *(toptions[i].arg) = optarg;
390}
391
392/* see self_exec.c */
393#ifdef UCLINUX
394extern char *child_args;
395#endif
396
397static void parse_opts(int argc, char *argv[])
398{
399 unsigned int i, topts_len = count_options();
400 char optstr[2 * ARRAY_SIZE(options) + 2 * topts_len];
401 int opt;
402
403 check_option_collision();
404
405 optstr[0] = 0;
406
407 for (i = 0; i < ARRAY_SIZE(options); i++)
408 strcat(optstr, options[i].optstr);
409
410 for (i = 0; i < topts_len; i++)
411 strcat(optstr, tst_test->options[i].optstr);
412
413 while ((opt = getopt(argc, argv, optstr)) > 0) {
414 switch (opt) {
415 case '?':
416 print_help();
417 tst_brk(TBROK, "Invalid option");
418 case 'h':
419 print_help();
420 exit(0);
421 case 'i':
422 iterations = atoi(optarg);
423 break;
424 case 'I':
425 duration = atof(optarg);
426 break;
427 case 'C':
428#ifdef UCLINUX
429 child_args = optarg;
430#endif
431 break;
432 default:
433 parse_topt(topts_len, opt, optarg);
434 }
435 }
436}
437
438static void do_exit(void) __attribute__ ((noreturn));
439
440static void do_exit(void)
441{
442 int ret = 0;
443
444 printf("\nSummary:\n");
445 printf("passed %u\n", results->passed);
446 printf("failed %u\n", results->failed);
447 printf("skipped %u\n", results->skipped);
448 printf("warnings %u\n", results->warnings);
449
450 if (results->failed)
451 ret |= TFAIL;
452
453 if (results->skipped)
454 ret |= TCONF;
455
456 if (results->warnings)
457 ret |= TWARN;
458
459 cleanup_ipc();
460
461 exit(ret);
462}
463
464void check_kver(void)
465{
466 int v1, v2, v3;
467
468 tst_parse_kver(tst_test->min_kver, &v1, &v2, &v3);
469
470 if (tst_kvercmp(v1, v2, v3) < 0) {
471 tst_brk(TCONF, "The test requires kernel %s or newer",
472 tst_test->min_kver);
473 }
474}
475
476static int results_equal(struct results *a, struct results *b)
477{
478 if (a->passed != b->passed)
479 return 0;
480
481 if (a->failed != b->failed)
482 return 0;
483
484 if (a->skipped != b->skipped)
485 return 0;
486
487 return 1;
488}
489
490static int needs_tmpdir(void)
491{
492 return tst_test->needs_tmpdir ||
493 tst_test->needs_device ||
494 tst_test->resource_files ||
495 tst_test->needs_checkpoints;
496}
497
498static void copy_resources(void)
499{
500 unsigned int i;
501
502 for (i = 0; tst_test->resource_files[i]; i++)
503 TST_RESOURCE_COPY(NULL, tst_test->resource_files[i], NULL);
504}
505
506static struct tst_device tdev;
507struct tst_device *tst_device;
508
509static void do_setup(int argc, char *argv[])
510{
511 if (!tst_test)
512 tst_brk(TBROK, "No tests to run");
513
514 if (!tst_test->test && !tst_test->test_all)
515 tst_brk(TBROK, "No test function speficied");
516
517 if (tst_test->test && tst_test->test_all)
518 tst_brk(TBROK, "You can define either test() or test_all()");
519
520 if (tst_test->test && !tst_test->tcnt)
521 tst_brk(TBROK, "Number of tests (tcnt) must not be > 0");
522
523 if (tst_test->test_all && tst_test->tcnt)
524 tst_brk(TBROK, "You can't define tcnt for test_all()");
525
526 if (tst_test->needs_root && geteuid() != 0)
527 tst_brk(TCONF, "Test needs to be run as root");
528
529 if (tst_test->min_kver)
530 check_kver();
531
532 parse_opts(argc, argv);
533
534 setup_ipc();
535
536 if (needs_tmpdir()) {
537 tst_tmpdir();
538 tmpdir_created = 1;
539 }
540
541 if (tst_test->needs_device) {
542 tdev.dev = tst_acquire_device(NULL);
543 tdev.fs_type = tst_dev_fs_type();
544
545 if (!tdev.dev)
546 tst_brk(TCONF, "Failed to acquire device");
547
548 tst_device = &tdev;
549 }
550
551 if (tst_test->resource_files)
552 copy_resources();
553
554 main_pid = getpid();
555
556 if (tst_test->setup)
557 tst_test->setup();
558
559 if (main_pid != getpid())
560 tst_brk(TBROK, "Runaway child in setup()!");
561}
562
563static void do_cleanup(void)
564{
565 if (tst_test->cleanup)
566 tst_test->cleanup();
567
568 if (tst_test->needs_device && tdev.dev)
569 tst_release_device(tdev.dev);
570
571 if (needs_tmpdir() && tmpdir_created) {
572 /* avoid munmap() on wrong pointer in tst_rmdir() */
573 tst_futexes = NULL;
574 tst_rmdir();
575 }
576}
577
578static void run_tests(void)
579{
580 unsigned int i;
581 struct results saved_results;
582
583 if (!tst_test->test) {
584 saved_results = *results;
585 tst_test->test_all();
586
587 if (getpid() != main_pid) {
588 exit(0);
589 }
590
591 reap_children();
592
593 if (results_equal(&saved_results, results))
594 tst_brk(TBROK, "Test haven't reported results!");
595 return;
596 }
597
598 for (i = 0; i < tst_test->tcnt; i++) {
599 saved_results = *results;
600 tst_test->test(i);
601
602 if (getpid() != main_pid) {
603 exit(0);
604 }
605
606 reap_children();
607
608 if (results_equal(&saved_results, results))
609 tst_brk(TBROK, "Test %i haven't reported results!", i);
610 }
611}
612
613static unsigned long long get_time_ms(void)
614{
615 struct timeval tv;
616
617 gettimeofday(&tv, NULL);
618
619 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
620}
621
622void tst_run_tcases(int argc, char *argv[], struct tst_test *self)
623{
624 unsigned int i = 0;
625 unsigned long long stop_time = 0;
626 int cont = 1;
627
628 tst_test = self;
629 TCID = tst_test->tid;
630
631 do_setup(argc, argv);
632
633 if (duration > 0)
634 stop_time = get_time_ms() + (unsigned long long)(duration * 1000);
635
636 for (;;) {
637 cont = 0;
638
639 if (i < (unsigned int)iterations) {
640 i++;
641 cont = 1;
642 }
643
644 if (stop_time && get_time_ms() < stop_time)
645 cont = 1;
646
647 if (!cont)
648 break;
649
650 run_tests();
651 }
652
653 do_cleanup();
654 do_exit();
655}