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