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