Home | History | Annotate | Download | only in lib
      1 /*
      2  * Copyright (c) 2015-2016 Cyril Hrubis <chrubis (at) 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 
     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 
    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 
    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 
    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 
    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 
    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 
    230 static void do_test_cleanup(void)
    231 {
    232 	if (tst_test->cleanup)
    233 		tst_test->cleanup();
    234 }
    235 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    672 static void alarm_handler(int sig LTP_ATTRIBUTE_UNUSED)
    673 {
    674 	kill(-test_pid, SIGKILL);
    675 }
    676 
    677 static void heartbeat_handler(int sig LTP_ATTRIBUTE_UNUSED)
    678 {
    679 	alarm(timeout);
    680 }
    681 
    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