1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2017 Facebook 3 */ 4 #define _GNU_SOURCE 5 #include "test_progs.h" 6 #include "cgroup_helpers.h" 7 #include "bpf_rlimit.h" 8 #include <argp.h> 9 #include <pthread.h> 10 #include <sched.h> 11 #include <signal.h> 12 #include <string.h> 13 #include <execinfo.h> /* backtrace */ 14 15 #define EXIT_NO_TEST 2 16 #define EXIT_ERR_SETUP_INFRA 3 17 18 /* defined in test_progs.h */ 19 struct test_env env = {}; 20 21 struct prog_test_def { 22 const char *test_name; 23 int test_num; 24 void (*run_test)(void); 25 bool force_log; 26 int error_cnt; 27 int skip_cnt; 28 bool tested; 29 bool need_cgroup_cleanup; 30 31 char *subtest_name; 32 int subtest_num; 33 34 /* store counts before subtest started */ 35 int old_error_cnt; 36 }; 37 38 /* Override C runtime library's usleep() implementation to ensure nanosleep() 39 * is always called. Usleep is frequently used in selftests as a way to 40 * trigger kprobe and tracepoints. 41 */ 42 int usleep(useconds_t usec) 43 { 44 struct timespec ts = { 45 .tv_sec = usec / 1000000, 46 .tv_nsec = (usec % 1000000) * 1000, 47 }; 48 49 return syscall(__NR_nanosleep, &ts, NULL); 50 } 51 52 static bool should_run(struct test_selector *sel, int num, const char *name) 53 { 54 int i; 55 56 for (i = 0; i < sel->blacklist.cnt; i++) { 57 if (strstr(name, sel->blacklist.strs[i])) 58 return false; 59 } 60 61 for (i = 0; i < sel->whitelist.cnt; i++) { 62 if (strstr(name, sel->whitelist.strs[i])) 63 return true; 64 } 65 66 if (!sel->whitelist.cnt && !sel->num_set) 67 return true; 68 69 return num < sel->num_set_len && sel->num_set[num]; 70 } 71 72 static void dump_test_log(const struct prog_test_def *test, bool failed) 73 { 74 if (stdout == env.stdout) 75 return; 76 77 fflush(stdout); /* exports env.log_buf & env.log_cnt */ 78 79 if (env.verbosity > VERBOSE_NONE || test->force_log || failed) { 80 if (env.log_cnt) { 81 env.log_buf[env.log_cnt] = '\0'; 82 fprintf(env.stdout, "%s", env.log_buf); 83 if (env.log_buf[env.log_cnt - 1] != '\n') 84 fprintf(env.stdout, "\n"); 85 } 86 } 87 88 fseeko(stdout, 0, SEEK_SET); /* rewind */ 89 } 90 91 static void skip_account(void) 92 { 93 if (env.test->skip_cnt) { 94 env.skip_cnt++; 95 env.test->skip_cnt = 0; 96 } 97 } 98 99 static void stdio_restore(void); 100 101 /* A bunch of tests set custom affinity per-thread and/or per-process. Reset 102 * it after each test/sub-test. 103 */ 104 static void reset_affinity() { 105 106 cpu_set_t cpuset; 107 int i, err; 108 109 CPU_ZERO(&cpuset); 110 for (i = 0; i < env.nr_cpus; i++) 111 CPU_SET(i, &cpuset); 112 113 err = sched_setaffinity(0, sizeof(cpuset), &cpuset); 114 if (err < 0) { 115 stdio_restore(); 116 fprintf(stderr, "Failed to reset process affinity: %d!\n", err); 117 exit(EXIT_ERR_SETUP_INFRA); 118 } 119 err = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); 120 if (err < 0) { 121 stdio_restore(); 122 fprintf(stderr, "Failed to reset thread affinity: %d!\n", err); 123 exit(EXIT_ERR_SETUP_INFRA); 124 } 125 } 126 127 static void save_netns(void) 128 { 129 env.saved_netns_fd = open("/proc/self/ns/net", O_RDONLY); 130 if (env.saved_netns_fd == -1) { 131 perror("open(/proc/self/ns/net)"); 132 exit(EXIT_ERR_SETUP_INFRA); 133 } 134 } 135 136 static void restore_netns(void) 137 { 138 if (setns(env.saved_netns_fd, CLONE_NEWNET) == -1) { 139 stdio_restore(); 140 perror("setns(CLONE_NEWNS)"); 141 exit(EXIT_ERR_SETUP_INFRA); 142 } 143 } 144 145 void test__end_subtest() 146 { 147 struct prog_test_def *test = env.test; 148 int sub_error_cnt = test->error_cnt - test->old_error_cnt; 149 150 if (sub_error_cnt) 151 env.fail_cnt++; 152 else if (test->skip_cnt == 0) 153 env.sub_succ_cnt++; 154 skip_account(); 155 156 dump_test_log(test, sub_error_cnt); 157 158 fprintf(env.stdout, "#%d/%d %s:%s\n", 159 test->test_num, test->subtest_num, test->subtest_name, 160 sub_error_cnt ? "FAIL" : (test->skip_cnt ? "SKIP" : "OK")); 161 162 free(test->subtest_name); 163 test->subtest_name = NULL; 164 } 165 166 bool test__start_subtest(const char *name) 167 { 168 struct prog_test_def *test = env.test; 169 170 if (test->subtest_name) 171 test__end_subtest(); 172 173 test->subtest_num++; 174 175 if (!name || !name[0]) { 176 fprintf(env.stderr, 177 "Subtest #%d didn't provide sub-test name!\n", 178 test->subtest_num); 179 return false; 180 } 181 182 if (!should_run(&env.subtest_selector, test->subtest_num, name)) 183 return false; 184 185 test->subtest_name = strdup(name); 186 if (!test->subtest_name) { 187 fprintf(env.stderr, 188 "Subtest #%d: failed to copy subtest name!\n", 189 test->subtest_num); 190 return false; 191 } 192 env.test->old_error_cnt = env.test->error_cnt; 193 194 return true; 195 } 196 197 void test__force_log() { 198 env.test->force_log = true; 199 } 200 201 void test__skip(void) 202 { 203 env.test->skip_cnt++; 204 } 205 206 void test__fail(void) 207 { 208 env.test->error_cnt++; 209 } 210 211 int test__join_cgroup(const char *path) 212 { 213 int fd; 214 215 if (!env.test->need_cgroup_cleanup) { 216 if (setup_cgroup_environment()) { 217 fprintf(stderr, 218 "#%d %s: Failed to setup cgroup environment\n", 219 env.test->test_num, env.test->test_name); 220 return -1; 221 } 222 223 env.test->need_cgroup_cleanup = true; 224 } 225 226 fd = create_and_get_cgroup(path); 227 if (fd < 0) { 228 fprintf(stderr, 229 "#%d %s: Failed to create cgroup '%s' (errno=%d)\n", 230 env.test->test_num, env.test->test_name, path, errno); 231 return fd; 232 } 233 234 if (join_cgroup(path)) { 235 fprintf(stderr, 236 "#%d %s: Failed to join cgroup '%s' (errno=%d)\n", 237 env.test->test_num, env.test->test_name, path, errno); 238 return -1; 239 } 240 241 return fd; 242 } 243 244 int bpf_find_map(const char *test, struct bpf_object *obj, const char *name) 245 { 246 struct bpf_map *map; 247 248 map = bpf_object__find_map_by_name(obj, name); 249 if (!map) { 250 fprintf(stdout, "%s:FAIL:map '%s' not found\n", test, name); 251 test__fail(); 252 return -1; 253 } 254 return bpf_map__fd(map); 255 } 256 257 static bool is_jit_enabled(void) 258 { 259 const char *jit_sysctl = "/proc/sys/net/core/bpf_jit_enable"; 260 bool enabled = false; 261 int sysctl_fd; 262 263 sysctl_fd = open(jit_sysctl, 0, O_RDONLY); 264 if (sysctl_fd != -1) { 265 char tmpc; 266 267 if (read(sysctl_fd, &tmpc, sizeof(tmpc)) == 1) 268 enabled = (tmpc != '0'); 269 close(sysctl_fd); 270 } 271 272 return enabled; 273 } 274 275 int compare_map_keys(int map1_fd, int map2_fd) 276 { 277 __u32 key, next_key; 278 char val_buf[PERF_MAX_STACK_DEPTH * 279 sizeof(struct bpf_stack_build_id)]; 280 int err; 281 282 err = bpf_map_get_next_key(map1_fd, NULL, &key); 283 if (err) 284 return err; 285 err = bpf_map_lookup_elem(map2_fd, &key, val_buf); 286 if (err) 287 return err; 288 289 while (bpf_map_get_next_key(map1_fd, &key, &next_key) == 0) { 290 err = bpf_map_lookup_elem(map2_fd, &next_key, val_buf); 291 if (err) 292 return err; 293 294 key = next_key; 295 } 296 if (errno != ENOENT) 297 return -1; 298 299 return 0; 300 } 301 302 int compare_stack_ips(int smap_fd, int amap_fd, int stack_trace_len) 303 { 304 __u32 key, next_key, *cur_key_p, *next_key_p; 305 char *val_buf1, *val_buf2; 306 int i, err = 0; 307 308 val_buf1 = malloc(stack_trace_len); 309 val_buf2 = malloc(stack_trace_len); 310 cur_key_p = NULL; 311 next_key_p = &key; 312 while (bpf_map_get_next_key(smap_fd, cur_key_p, next_key_p) == 0) { 313 err = bpf_map_lookup_elem(smap_fd, next_key_p, val_buf1); 314 if (err) 315 goto out; 316 err = bpf_map_lookup_elem(amap_fd, next_key_p, val_buf2); 317 if (err) 318 goto out; 319 for (i = 0; i < stack_trace_len; i++) { 320 if (val_buf1[i] != val_buf2[i]) { 321 err = -1; 322 goto out; 323 } 324 } 325 key = *next_key_p; 326 cur_key_p = &key; 327 next_key_p = &next_key; 328 } 329 if (errno != ENOENT) 330 err = -1; 331 332 out: 333 free(val_buf1); 334 free(val_buf2); 335 return err; 336 } 337 338 int extract_build_id(char *build_id, size_t size) 339 { 340 FILE *fp; 341 char *line = NULL; 342 size_t len = 0; 343 344 fp = popen("readelf -n ./urandom_read | grep 'Build ID'", "r"); 345 if (fp == NULL) 346 return -1; 347 348 if (getline(&line, &len, fp) == -1) 349 goto err; 350 fclose(fp); 351 352 if (len > size) 353 len = size; 354 memcpy(build_id, line, len); 355 build_id[len] = '\0'; 356 free(line); 357 return 0; 358 err: 359 fclose(fp); 360 return -1; 361 } 362 363 static int finit_module(int fd, const char *param_values, int flags) 364 { 365 return syscall(__NR_finit_module, fd, param_values, flags); 366 } 367 368 static int delete_module(const char *name, int flags) 369 { 370 return syscall(__NR_delete_module, name, flags); 371 } 372 373 static void unload_bpf_testmod(void) 374 { 375 if (delete_module("bpf_testmod", 0)) { 376 if (errno == ENOENT) { 377 if (env.verbosity > VERBOSE_NONE) 378 fprintf(stdout, "bpf_testmod.ko is already unloaded.\n"); 379 return; 380 } 381 fprintf(env.stderr, "Failed to unload bpf_testmod.ko from kernel: %d\n", -errno); 382 exit(1); 383 } 384 if (env.verbosity > VERBOSE_NONE) 385 fprintf(stdout, "Successfully unloaded bpf_testmod.ko.\n"); 386 } 387 388 static int load_bpf_testmod(void) 389 { 390 int fd; 391 392 /* ensure previous instance of the module is unloaded */ 393 unload_bpf_testmod(); 394 395 if (env.verbosity > VERBOSE_NONE) 396 fprintf(stdout, "Loading bpf_testmod.ko...\n"); 397 398 fd = open("bpf_testmod.ko", O_RDONLY); 399 if (fd < 0) { 400 fprintf(env.stderr, "Can't find bpf_testmod.ko kernel module: %d\n", -errno); 401 return -ENOENT; 402 } 403 if (finit_module(fd, "", 0)) { 404 fprintf(env.stderr, "Failed to load bpf_testmod.ko into the kernel: %d\n", -errno); 405 close(fd); 406 return -EINVAL; 407 } 408 close(fd); 409 410 if (env.verbosity > VERBOSE_NONE) 411 fprintf(stdout, "Successfully loaded bpf_testmod.ko.\n"); 412 return 0; 413 } 414 415 /* extern declarations for test funcs */ 416 #define DEFINE_TEST(name) extern void test_##name(void); 417 #include <prog_tests/tests.h> 418 #undef DEFINE_TEST 419 420 static struct prog_test_def prog_test_defs[] = { 421 #define DEFINE_TEST(name) { \ 422 .test_name = #name, \ 423 .run_test = &test_##name, \ 424 }, 425 #include <prog_tests/tests.h> 426 #undef DEFINE_TEST 427 }; 428 const int prog_test_cnt = ARRAY_SIZE(prog_test_defs); 429 430 const char *argp_program_version = "test_progs 0.1"; 431 const char *argp_program_bug_address = "<bpf@vger.kernel.org>"; 432 const char argp_program_doc[] = "BPF selftests test runner"; 433 434 enum ARG_KEYS { 435 ARG_TEST_NUM = 'n', 436 ARG_TEST_NAME = 't', 437 ARG_TEST_NAME_BLACKLIST = 'b', 438 ARG_VERIFIER_STATS = 's', 439 ARG_VERBOSE = 'v', 440 ARG_GET_TEST_CNT = 'c', 441 ARG_LIST_TEST_NAMES = 'l', 442 }; 443 444 static const struct argp_option opts[] = { 445 { "num", ARG_TEST_NUM, "NUM", 0, 446 "Run test number NUM only " }, 447 { "name", ARG_TEST_NAME, "NAMES", 0, 448 "Run tests with names containing any string from NAMES list" }, 449 { "name-blacklist", ARG_TEST_NAME_BLACKLIST, "NAMES", 0, 450 "Don't run tests with names containing any string from NAMES list" }, 451 { "verifier-stats", ARG_VERIFIER_STATS, NULL, 0, 452 "Output verifier statistics", }, 453 { "verbose", ARG_VERBOSE, "LEVEL", OPTION_ARG_OPTIONAL, 454 "Verbose output (use -vv or -vvv for progressively verbose output)" }, 455 { "count", ARG_GET_TEST_CNT, NULL, 0, 456 "Get number of selected top-level tests " }, 457 { "list", ARG_LIST_TEST_NAMES, NULL, 0, 458 "List test names that would run (without running them) " }, 459 {}, 460 }; 461 462 static int libbpf_print_fn(enum libbpf_print_level level, 463 const char *format, va_list args) 464 { 465 if (env.verbosity < VERBOSE_VERY && level == LIBBPF_DEBUG) 466 return 0; 467 vfprintf(stdout, format, args); 468 return 0; 469 } 470 471 static void free_str_set(const struct str_set *set) 472 { 473 int i; 474 475 if (!set) 476 return; 477 478 for (i = 0; i < set->cnt; i++) 479 free((void *)set->strs[i]); 480 free(set->strs); 481 } 482 483 static int parse_str_list(const char *s, struct str_set *set) 484 { 485 char *input, *state = NULL, *next, **tmp, **strs = NULL; 486 int cnt = 0; 487 488 input = strdup(s); 489 if (!input) 490 return -ENOMEM; 491 492 set->cnt = 0; 493 set->strs = NULL; 494 495 while ((next = strtok_r(state ? NULL : input, ",", &state))) { 496 tmp = realloc(strs, sizeof(*strs) * (cnt + 1)); 497 if (!tmp) 498 goto err; 499 strs = tmp; 500 501 strs[cnt] = strdup(next); 502 if (!strs[cnt]) 503 goto err; 504 505 cnt++; 506 } 507 508 set->cnt = cnt; 509 set->strs = (const char **)strs; 510 free(input); 511 return 0; 512 err: 513 free(strs); 514 free(input); 515 return -ENOMEM; 516 } 517 518 extern int extra_prog_load_log_flags; 519 520 static error_t parse_arg(int key, char *arg, struct argp_state *state) 521 { 522 struct test_env *env = state->input; 523 524 switch (key) { 525 case ARG_TEST_NUM: { 526 char *subtest_str = strchr(arg, '/'); 527 528 if (subtest_str) { 529 *subtest_str = '\0'; 530 if (parse_num_list(subtest_str + 1, 531 &env->subtest_selector.num_set, 532 &env->subtest_selector.num_set_len)) { 533 fprintf(stderr, 534 "Failed to parse subtest numbers.\n"); 535 return -EINVAL; 536 } 537 } 538 if (parse_num_list(arg, &env->test_selector.num_set, 539 &env->test_selector.num_set_len)) { 540 fprintf(stderr, "Failed to parse test numbers.\n"); 541 return -EINVAL; 542 } 543 break; 544 } 545 case ARG_TEST_NAME: { 546 char *subtest_str = strchr(arg, '/'); 547 548 if (subtest_str) { 549 *subtest_str = '\0'; 550 if (parse_str_list(subtest_str + 1, 551 &env->subtest_selector.whitelist)) 552 return -ENOMEM; 553 } 554 if (parse_str_list(arg, &env->test_selector.whitelist)) 555 return -ENOMEM; 556 break; 557 } 558 case ARG_TEST_NAME_BLACKLIST: { 559 char *subtest_str = strchr(arg, '/'); 560 561 if (subtest_str) { 562 *subtest_str = '\0'; 563 if (parse_str_list(subtest_str + 1, 564 &env->subtest_selector.blacklist)) 565 return -ENOMEM; 566 } 567 if (parse_str_list(arg, &env->test_selector.blacklist)) 568 return -ENOMEM; 569 break; 570 } 571 case ARG_VERIFIER_STATS: 572 env->verifier_stats = true; 573 break; 574 case ARG_VERBOSE: 575 env->verbosity = VERBOSE_NORMAL; 576 if (arg) { 577 if (strcmp(arg, "v") == 0) { 578 env->verbosity = VERBOSE_VERY; 579 extra_prog_load_log_flags = 1; 580 } else if (strcmp(arg, "vv") == 0) { 581 env->verbosity = VERBOSE_SUPER; 582 extra_prog_load_log_flags = 2; 583 } else { 584 fprintf(stderr, 585 "Unrecognized verbosity setting ('%s'), only -v and -vv are supported\n", 586 arg); 587 return -EINVAL; 588 } 589 } 590 591 if (env->verbosity > VERBOSE_NONE) { 592 if (setenv("SELFTESTS_VERBOSE", "1", 1) == -1) { 593 fprintf(stderr, 594 "Unable to setenv SELFTESTS_VERBOSE=1 (errno=%d)", 595 errno); 596 return -1; 597 } 598 } 599 600 break; 601 case ARG_GET_TEST_CNT: 602 env->get_test_cnt = true; 603 break; 604 case ARG_LIST_TEST_NAMES: 605 env->list_test_names = true; 606 break; 607 case ARGP_KEY_ARG: 608 argp_usage(state); 609 break; 610 case ARGP_KEY_END: 611 break; 612 default: 613 return ARGP_ERR_UNKNOWN; 614 } 615 return 0; 616 } 617 618 static void stdio_hijack(void) 619 { 620 #ifdef __GLIBC__ 621 env.stdout = stdout; 622 env.stderr = stderr; 623 624 if (env.verbosity > VERBOSE_NONE) { 625 /* nothing to do, output to stdout by default */ 626 return; 627 } 628 629 /* stdout and stderr -> buffer */ 630 fflush(stdout); 631 632 stdout = open_memstream(&env.log_buf, &env.log_cnt); 633 if (!stdout) { 634 stdout = env.stdout; 635 perror("open_memstream"); 636 return; 637 } 638 639 stderr = stdout; 640 #endif 641 } 642 643 static void stdio_restore(void) 644 { 645 #ifdef __GLIBC__ 646 if (stdout == env.stdout) 647 return; 648 649 fclose(stdout); 650 free(env.log_buf); 651 652 env.log_buf = NULL; 653 env.log_cnt = 0; 654 655 stdout = env.stdout; 656 stderr = env.stderr; 657 #endif 658 } 659 660 /* 661 * Determine if test_progs is running as a "flavored" test runner and switch 662 * into corresponding sub-directory to load correct BPF objects. 663 * 664 * This is done by looking at executable name. If it contains "-flavor" 665 * suffix, then we are running as a flavored test runner. 666 */ 667 int cd_flavor_subdir(const char *exec_name) 668 { 669 /* General form of argv[0] passed here is: 670 * some/path/to/test_progs[-flavor], where -flavor part is optional. 671 * First cut out "test_progs[-flavor]" part, then extract "flavor" 672 * part, if it's there. 673 */ 674 const char *flavor = strrchr(exec_name, '/'); 675 676 if (!flavor) 677 return 0; 678 flavor++; 679 flavor = strrchr(flavor, '-'); 680 if (!flavor) 681 return 0; 682 flavor++; 683 if (env.verbosity > VERBOSE_NONE) 684 fprintf(stdout, "Switching to flavor '%s' subdirectory...\n", flavor); 685 686 return chdir(flavor); 687 } 688 689 #define MAX_BACKTRACE_SZ 128 690 void crash_handler(int signum) 691 { 692 void *bt[MAX_BACKTRACE_SZ]; 693 size_t sz; 694 695 sz = backtrace(bt, ARRAY_SIZE(bt)); 696 697 if (env.test) 698 dump_test_log(env.test, true); 699 if (env.stdout) 700 stdio_restore(); 701 702 fprintf(stderr, "Caught signal #%d!\nStack trace:\n", signum); 703 backtrace_symbols_fd(bt, sz, STDERR_FILENO); 704 } 705 706 int main(int argc, char **argv) 707 { 708 static const struct argp argp = { 709 .options = opts, 710 .parser = parse_arg, 711 .doc = argp_program_doc, 712 }; 713 struct sigaction sigact = { 714 .sa_handler = crash_handler, 715 .sa_flags = SA_RESETHAND, 716 }; 717 int err, i; 718 719 sigaction(SIGSEGV, &sigact, NULL); 720 721 err = argp_parse(&argp, argc, argv, 0, NULL, &env); 722 if (err) 723 return err; 724 725 err = cd_flavor_subdir(argv[0]); 726 if (err) 727 return err; 728 729 libbpf_set_print(libbpf_print_fn); 730 731 srand(time(NULL)); 732 733 env.jit_enabled = is_jit_enabled(); 734 env.nr_cpus = libbpf_num_possible_cpus(); 735 if (env.nr_cpus < 0) { 736 fprintf(stderr, "Failed to get number of CPUs: %d!\n", 737 env.nr_cpus); 738 return -1; 739 } 740 741 save_netns(); 742 stdio_hijack(); 743 env.has_testmod = true; 744 if (load_bpf_testmod()) { 745 fprintf(env.stderr, "WARNING! Selftests relying on bpf_testmod.ko will be skipped.\n"); 746 env.has_testmod = false; 747 } 748 for (i = 0; i < prog_test_cnt; i++) { 749 struct prog_test_def *test = &prog_test_defs[i]; 750 751 env.test = test; 752 test->test_num = i + 1; 753 754 if (!should_run(&env.test_selector, 755 test->test_num, test->test_name)) 756 continue; 757 758 if (env.get_test_cnt) { 759 env.succ_cnt++; 760 continue; 761 } 762 763 if (env.list_test_names) { 764 fprintf(env.stdout, "%s\n", test->test_name); 765 env.succ_cnt++; 766 continue; 767 } 768 769 test->run_test(); 770 /* ensure last sub-test is finalized properly */ 771 if (test->subtest_name) 772 test__end_subtest(); 773 774 test->tested = true; 775 if (test->error_cnt) 776 env.fail_cnt++; 777 else 778 env.succ_cnt++; 779 skip_account(); 780 781 dump_test_log(test, test->error_cnt); 782 783 fprintf(env.stdout, "#%d %s:%s\n", 784 test->test_num, test->test_name, 785 test->error_cnt ? "FAIL" : "OK"); 786 787 reset_affinity(); 788 restore_netns(); 789 if (test->need_cgroup_cleanup) 790 cleanup_cgroup_environment(); 791 } 792 if (env.has_testmod) 793 unload_bpf_testmod(); 794 stdio_restore(); 795 796 if (env.get_test_cnt) { 797 printf("%d\n", env.succ_cnt); 798 goto out; 799 } 800 801 if (env.list_test_names) 802 goto out; 803 804 fprintf(stdout, "Summary: %d/%d PASSED, %d SKIPPED, %d FAILED\n", 805 env.succ_cnt, env.sub_succ_cnt, env.skip_cnt, env.fail_cnt); 806 807 out: 808 free_str_set(&env.test_selector.blacklist); 809 free_str_set(&env.test_selector.whitelist); 810 free(env.test_selector.num_set); 811 free_str_set(&env.subtest_selector.blacklist); 812 free_str_set(&env.subtest_selector.whitelist); 813 free(env.subtest_selector.num_set); 814 close(env.saved_netns_fd); 815 816 if (env.succ_cnt + env.fail_cnt + env.skip_cnt == 0) 817 return EXIT_NO_TEST; 818 819 return env.fail_cnt ? EXIT_FAILURE : EXIT_SUCCESS; 820 } 821