From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: multipart/mixed; boundary="===============3391692558395236834==" MIME-Version: 1.0 From: James Prestwood Subject: [PATCH 15/16] tools: post test-runner rewrite cleanup Date: Thu, 27 Aug 2020 10:32:28 -0700 Message-ID: <20200827173229.26466-15-prestwoj@gmail.com> In-Reply-To: <20200827173229.26466-1-prestwoj@gmail.com> List-Id: To: iwd@lists.01.org --===============3391692558395236834== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Removed test-runner.c, and renamed py_runner to test-runner. Removed tools/test-runner from .gitignore. This was done as a separate commit to avoid a nasty diff between the existing test runner, and the new python version --- .gitignore | 1 - Makefile.am | 7 - tools/{py_runner.py =3D> test-runner} | 0 tools/test-runner.c | 3310 --------------------------- 4 files changed, 3318 deletions(-) rename tools/{py_runner.py =3D> test-runner} (100%) delete mode 100644 tools/test-runner.c diff --git a/.gitignore b/.gitignore index 93fec4f7..33405af8 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ wired/ead.8 wired/ead.service tools/hwsim tools/hwsim.1 -tools/test-runner tools/probe-req unit/test-cmac-aes unit/test-arc4 diff --git a/Makefile.am b/Makefile.am index e83dbeee..5f16920f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -366,13 +366,6 @@ man_MANS +=3D tools/hwsim.1 endif endif = -if TOOLS -noinst_PROGRAMS +=3D tools/test-runner - -tools_test_runner_SOURCES =3D tools/test-runner.c -tools_test_runner_LDADD =3D $(ell_ldadd) -endif - unit_tests =3D unit/test-cmac-aes \ unit/test-hmac-md5 unit/test-hmac-sha1 unit/test-hmac-sha256 \ unit/test-prf-sha1 unit/test-kdf-sha256 \ diff --git a/tools/py_runner.py b/tools/test-runner similarity index 100% rename from tools/py_runner.py rename to tools/test-runner diff --git a/tools/test-runner.c b/tools/test-runner.c deleted file mode 100644 index 40148241..00000000 --- a/tools/test-runner.c +++ /dev/null @@ -1,3310 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2012-2019 Intel Corporation. All rights reserved. - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 = USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "linux/nl80211.h" - -#ifndef WAIT_ANY -#define WAIT_ANY (-1) -#endif - -#define CMDLINE_MAX 2048 - -#define BIN_IW "iw" -#define BIN_HWSIM "hwsim" -#define BIN_OFONO "ofonod" -#define BIN_PHONESIM "phonesim" -#define BIN_HOSTAPD "hostapd" -#define BIN_IWD "iwd" - -#define HWSIM_RADIOS_MAX 100 -#define TEST_MAX_EXEC_TIME_SEC 20 - -static enum action { - ACTION_AUTO_TEST, - ACTION_UNIT_TEST, -} test_action; - -static const char *own_binary; -static char **test_argv; -static int test_argc; -static char **verbose_apps; -static char *verbose_opt; -static bool valgrind; -static char *gdb_opt; -static bool enable_debug; -const char *debug_filter; -static struct l_settings *hw_config; -static bool native_hw; -static bool shell; -static bool log; -static char log_dir[PATH_MAX]; -static uid_t log_uid; -static gid_t log_gid; -static const char *qemu_binary; -static const char *kernel_image; -static const char *exec_home; -static const char *test_action_params; -static char top_level_path[PATH_MAX]; -static struct l_queue *wiphy_list; - -#if defined(__i386__) -/* - * If iwd is being compiled for i386, prefer the i386 qemu but try the - * X86-64 version as a fallback. - */ -static const char * const qemu_table[] =3D { - "qemu-system-i386", - "/usr/bin/qemu-system-i386", - "qemu-system-x86_64", - "/usr/bin/qemu-system-x86_64", - NULL -}; -#elif defined(__x86_64__) -/* - * If iwd is being built for X86-64 bits there's no point booting a 32-bit - * only system. - */ -static const char * const qemu_table[] =3D { - "qemu-system-x86_64", - "/usr/bin/qemu-system-x86_64", - NULL -}; -#elif defined(__arm__) -/* - * If iwd is being built for ARM look for 32-bit version. - */ -static const char * const qemu_table[] =3D { - "qemu-system-arm", - "/usr/bin/qemu-system-arm", - NULL -}; -#elif defined(__aarch64__) -/* - * If iwd is being built for AARCH64 look for 64-bit version. - */ -static const char * const qemu_table[] =3D { - "qemu-system-aarch64", - "/usr/bin/qemu-system-aarch64", - NULL -}; -#elif defined(__powerpc__) -/* - * If iwd is being built for PowerPC look for 32-bit version. - */ -static const char * const qemu_table[] =3D { - "qemu-system-ppc", - "/usr/bin/qemu-system-ppc", - NULL -}; -#elif defined(__powerpc64__) -/* - * If iwd is being built for PowerPC-64 look for 64-bit version. - */ -static const char * const qemu_table[] =3D { - "qemu-system-ppc64", - "/usr/bin/qemu-system-ppc64", - NULL -}; -#else -#warning Qemu binary name not defined for this architecture yet -static const char * const qemu_table[] =3D { NULL }; -#endif - -struct wiphy { - char name[20]; - int id; - unsigned int interface_index; - bool interface_created : 1; - bool used_by_hostapd : 1; - char *interface_name; - char *hostapd_ctrl_interface; - char *hostapd_config; - bool can_ap; -}; - -static bool check_verbosity(const char *app) -{ - char **apps =3D verbose_apps; - - /* - * All processes are verbose if logging is enabled. Kernel is a bit - * different as we just pipe dmesg into a log file at the end of - * execution. - */ - if (log && strcmp(app, "kernel") !=3D 0) - return true; - - /* - * Turn on output if this is a unit test run. Nothing should output - * anything except the tests themselves and the kernel. - */ - if (test_action =3D=3D ACTION_UNIT_TEST && strcmp(app, "kernel") !=3D 0) - return true; - - if (!apps) - return false; - - while (*apps) { - if (!strcmp(app, *apps)) - return true; - - apps++; - } - - return false; -} - -static bool path_exist(const char *path_name) -{ - struct stat st; - - if (!stat(path_name, &st)) - return true; - - return false; -} - -static const char *find_qemu(void) -{ - int i; - - for (i =3D 0; qemu_table[i]; i++) - if (path_exist(qemu_table[i])) - return qemu_table[i]; - - return NULL; -} - -static const char * const kernel_table[] =3D { - "bzImage", - "arch/x86/boot/bzImage", - "vmlinux", - "arch/x86/boot/vmlinux", - NULL -}; - -static const char *find_kernel(void) -{ - int i; - - for (i =3D 0; kernel_table[i]; i++) - if (path_exist(kernel_table[i])) - return kernel_table[i]; - - return NULL; -} - -static const struct { - const char *target; - const char *linkpath; -} dev_table[] =3D { - { "/proc/self/fd", "/dev/fd" }, - { "/proc/self/fd/0", "/dev/stdin" }, - { "/proc/self/fd/1", "/dev/stdout" }, - { "/proc/self/fd/2", "/dev/stderr" }, - { } -}; - -static const struct { - const char *fstype; - const char *target; - const char *options; - unsigned long flags; -} mount_table[] =3D { - { "sysfs", "/sys", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV }, - { "proc", "/proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV }, - { "devpts", "/dev/pts", "mode=3D0620", MS_NOSUID|MS_NOEXEC }, - { "tmpfs", "/dev/shm", "mode=3D1777", - MS_NOSUID|MS_NODEV|MS_STRICTATIME }, - { "tmpfs", "/run", "mode=3D0755", - MS_NOSUID|MS_NODEV|MS_STRICTATIME }, - { "tmpfs", "/var/lib/iwd", "mode=3D0755", 0 }, - { "tmpfs", "/tmp", NULL, 0 }, - { "debugfs", "/sys/kernel/debug", NULL, 0 }, - { } -}; - -static const char * const config_table[] =3D { - "/usr/share/dbus-1", - NULL -}; - -static void prepare_sandbox(void) -{ - int i; - - for (i =3D 0; mount_table[i].fstype; i++) { - struct stat st; - - if (lstat(mount_table[i].target, &st) < 0) { - mkdir(mount_table[i].target, 0755); - } - - if (mount(mount_table[i].fstype, - mount_table[i].target, - mount_table[i].fstype, - mount_table[i].flags, - mount_table[i].options) < 0) { - l_error("Error: Failed to mount filesystem %s", - mount_table[i].target); - } - } - - for (i =3D 0; dev_table[i].target; i++) { - - if (symlink(dev_table[i].target, dev_table[i].linkpath) < 0) - l_error("Failed to create device symlink: %s", - strerror(errno)); - } - - setsid(); - - ioctl(STDIN_FILENO, TIOCSCTTY, 1); - - for (i =3D 0; config_table[i]; i++) { - if (mount("tmpfs", config_table[i], "tmpfs", - MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, - "mode=3D0755") < 0) - l_error("Failed to create filesystem: %s", - strerror(errno)); - } -} - -static char *const qemu_argv[] =3D { - "", - "-machine", "type=3Dq35,accel=3Dkvm:tcg", - "-nodefaults", - "-no-user-config", - "-monitor", "none", - "-display", "none", - "-m", "192M", - "-nographic", - "-vga", "none", - "-net", "none", - "-no-acpi", - "-no-hpet", - "-no-reboot", - "-fsdev", "local,id=3Dfsdev-root,path=3D/,readonly,security_model=3Dnone", - "-device", "virtio-9p-pci,fsdev=3Dfsdev-root,mount_tag=3D/dev/root", - "-chardev", "stdio,id=3Dchardev-serial0,signal=3Doff", - "-device", "pci-serial,chardev=3Dchardev-serial0", - "-device", "virtio-rng-pci", - NULL -}; - -static char *const qemu_envp[] =3D { - "HOME=3D/", - NULL -}; - -static bool check_virtualization(void) -{ -#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__)) - uint32_t ecx; - - __asm__ __volatile__("cpuid" : "=3Dc" (ecx) : - "a" (1) : "%ebx", "%edx"); - - if (!!(ecx & (1 << 5))) { - l_info("Found support for Virtual Machine eXtensions"); - return true; - } - - __asm__ __volatile__("cpuid" : "=3Dc" (ecx) : - "a" (0x80000001) : "%ebx", "%edx"); - - if (ecx & (1 << 2)) { - l_info("Found support for Secure Virtual Machine extension"); - return true; - } -#endif - return false; -} - -static bool start_qemu(void) -{ - char cwd[PATH_MAX], testargs[PATH_MAX]; - char *initcmd, *cmdline; - char **argv; - int i, pos; - bool has_virt; - int num_pci =3D 0, num_usb =3D 0; - char **pci_keys =3D NULL; - char **usb_keys =3D NULL; - L_AUTO_FREE_VAR(char *, log_option) =3D NULL; - - has_virt =3D check_virtualization(); - - if (!getcwd(cwd, sizeof(cwd))) - strcat(cwd, "/"); - - if (own_binary[0] =3D=3D '/') - initcmd =3D l_strdup_printf("%s", own_binary); - else - initcmd =3D l_strdup_printf("%s/%s", cwd, own_binary); - - pos =3D snprintf(testargs, sizeof(testargs), "%s", test_argv[0]); - - for (i =3D 1; i < test_argc; i++) { - int len; - - len =3D sizeof(testargs) - pos; - pos +=3D snprintf(testargs + pos, len, " %s", test_argv[i]); - } - - cmdline =3D l_strdup_printf( - "console=3DttyS0,115200n8 earlyprintk=3Dserial " - "rootfstype=3D9p " - "root=3D/dev/root " - "rootflags=3Dtrans=3Dvirtio,version=3D9p2000.u " - "acpi=3Doff pci=3Dnoacpi noapic %s ro " - "mac80211_hwsim.radios=3D0 init=3D%s TESTHOME=3D%s " - "TESTVERBOUT=3D\'%s\' DEBUG_FILTER=3D\'%s\'" - "TEST_ACTION=3D%u TEST_ACTION_PARAMS=3D\'%s\' " - "TESTARGS=3D\'%s\' PATH=3D\'%s\' VALGRIND=3D%u " - "GDB=3D\'%s\' HW=3D\'%s\' SHELL=3D%u " - "LOG_PATH=3D\'%s\' LOG_UID=3D\'%d\' LOG_GID=3D\'%d\'", - check_verbosity("kernel") ? "ignore_loglevel" : "quiet", - initcmd, cwd, verbose_opt ? verbose_opt : "none", - enable_debug ? debug_filter : "", - test_action, - test_action_params ? test_action_params : "", - testargs, - getenv("PATH"), - valgrind, - gdb_opt ? gdb_opt : "none", - hw_config ? "real" : "virtual", - shell, - log ? log_dir : "none", - log_uid, log_gid); - - if (hw_config) { - if (l_settings_has_group(hw_config, "PCIAdapters")) { - pci_keys =3D l_settings_get_keys(hw_config, "PCIAdapters"); - - for (num_pci =3D 0; pci_keys[num_pci]; num_pci++); - } - - if (l_settings_has_group(hw_config, "USBAdapters")) { - usb_keys =3D l_settings_get_keys(hw_config, "USBAdapters"); - - for (num_usb =3D 0; usb_keys[num_usb]; num_usb++); - } - - if (!pci_keys && !usb_keys) { - l_error("hs config had no PCIAdapters or USBAdapters"); - l_free(initcmd); - l_free(cmdline); - return false; - } - } - - /* - * This got quite confusing. We need enough room for: - * - * qemu_argv (static list above with default parameters) - * -kernel,-append,-cpu,-host parameters (7) - * -enable-kvm and/or -usb (2) - * PCI and/or USB parameters (num_pci * 2) (num_usb * 2) - * Logging directory/device (2) - */ - argv =3D alloca(sizeof(qemu_argv) + sizeof(char *) * - (7 + (2 + (num_pci * 2) + (num_usb * 2) + 2))); - memcpy(argv, qemu_argv, sizeof(qemu_argv)); - - pos =3D (sizeof(qemu_argv) / sizeof(char *)) - 1; - - argv[0] =3D (char *) qemu_binary; - - argv[pos++] =3D "-kernel"; - argv[pos++] =3D (char *) kernel_image; - argv[pos++] =3D "-append"; - argv[pos++] =3D (char *) cmdline; - argv[pos++] =3D "-cpu"; - argv[pos++] =3D has_virt ? "host" : "max"; - - if (pci_keys) { - argv[pos++] =3D "-enable-kvm"; - for (i =3D 0; pci_keys[i]; i++) { - argv[pos++] =3D "-device"; - argv[pos] =3D alloca(22); - sprintf(argv[pos], "vfio-pci,host=3D%s", - l_settings_get_value(hw_config, - "PCIAdapters", pci_keys[i])); - pos++; - } - } - - if (usb_keys) { - argv[pos++] =3D "-usb"; - for (i =3D 0; usb_keys[i]; i++) { - const char *value =3D l_settings_get_value(hw_config, - "USBAdapters", usb_keys[i]); - char **info =3D l_strsplit(value, ','); - - if (l_strv_length(info) !=3D 2) { - l_error("hw config formatting error"); - l_strv_free(info); - return false; - } - - argv[pos++] =3D "-device"; - argv[pos] =3D alloca(32); - sprintf(argv[pos], "usb-host,hostbus=3D%s,hostaddr=3D%s", - info[0], info[1]); - pos++; - - l_strv_free(info); - } - } - - if (log) { - /* - * Create a virtfs device and tag it. This allows the guest to - * mount 'logdir' in the path specified with --log. - */ - log_option =3D l_strdup_printf("local,path=3D%s,mount_tag=3Dlogdir," - "security_model=3Dpassthrough,id=3Dlogdir", - log_dir); - argv[pos++] =3D "-virtfs"; - argv[pos++] =3D log_option; - } - - argv[pos] =3D NULL; - - execve(argv[0], argv, qemu_envp); - - /* Don't expect to reach here */ - free(initcmd); - free(cmdline); - - return true; -} - -static pid_t execute_program(char *argv[], char *envp[], bool wait, - const char *test_name) -{ - int status; - pid_t pid, child_pid; - char *str; - bool verbose; - char *log_name =3D argv[0]; - - if (!argv[0]) - return -1; - - /* - * We have a few special cases here: - * - * Since execute_program automatically logs to .log this would - * put all iwd output into valgrind.log rather than iwd.log. Since we - * are explicitly having valgrind output to a log file we can assume any - * output from this is only IWD, and not valgrind. - * - * python3 is special cased so that tests which start IWD manually can - * still show IWD output when using -v iwd. - */ - if (!strcmp(log_name, "valgrind") || !strcmp(log_name, "python3")) - log_name =3D "iwd"; - - str =3D l_strjoinv(argv, ' '); - l_debug("Executing: %s", str); - l_free(str); - - child_pid =3D fork(); - if (child_pid < 0) { - l_error("Failed to fork new process"); - return -1; - } - - if (child_pid =3D=3D 0) { - int fd =3D -1; - L_AUTO_FREE_VAR(char *, log_file) =3D NULL; - - verbose =3D check_verbosity(log_name); - - /* No stdout and no logging */ - if (!verbose && !log) - fd =3D open("/dev/null", O_WRONLY); - else if (log && verbose) { - /* - * Create the log file for this process. If no test name - * was specified this is a 'global' process (only run - * once, not per-test). - */ - if (test_name) { - log_file =3D l_strdup_printf("%s/%s/%s.log", - log_dir, test_name, log_name); - } else - log_file =3D l_strdup_printf("%s/%s.log", - log_dir, log_name); - - fd =3D open(log_file, O_WRONLY | O_CREAT | O_APPEND, - S_IRUSR | S_IWUSR); - if (fchown(fd, log_uid, log_gid) < 0) - l_error("failed to fchown %s", log_file); - } - - if (fd > -1) { - dup2(fd, 1); - dup2(fd, 2); - - close(fd); - } - - execvpe(argv[0], argv, envp); - - l_error("Failed to call execvpe for: %s. Error: %s", argv[0], - strerror(errno)); - - exit(EXIT_FAILURE); - } - - if (!wait) - goto exit; - - do { - pid =3D waitpid(child_pid, &status, 0); - } while (!WIFEXITED(status) && pid =3D=3D child_pid); - - if (WEXITSTATUS(status) !=3D EXIT_SUCCESS) - return -1; - -exit: - return child_pid; -} - -static void kill_process(pid_t pid) -{ - int status; - int i =3D 0; - - l_debug("Terminate pid: %d", pid); - - kill(pid, SIGTERM); - - do { - if (waitpid(pid, &status, WNOHANG) =3D=3D pid) - return; - - usleep(100000); - } while (!WIFEXITED(status) && !WIFSIGNALED(status) && i++ < 20); - - l_error("Failed to kill process %d gracefully", pid); - kill(pid, SIGKILL); - - /* SIGKILL shouldn't need WNOHANG */ - waitpid(pid, &status, 0); -} - -static bool wait_for_socket(const char *socket, useconds_t wait_time) -{ - int i =3D 0; - - do { - if (path_exist(socket)) - return true; - - usleep(wait_time); - } while ((wait_time > 0) ? i++ < 20 : true); - - l_error("Error: cannot find socket: %s", socket); - return false; -} - -static void create_dbus_system_conf(void) -{ - FILE *fp; - - fp =3D fopen("/usr/share/dbus-1/system.conf", "we"); - if (!fp) - return; - - fputs("\n", fp); - fputs("\n", fp); - fputs("system\n", fp); - fputs("unix:path=3D/run/dbus/system_bus_socket\n", fp); - fputs("2147483647", fp); - fputs("ANONYMOUS\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - fputs("\n", fp); - - fclose(fp); - - mkdir("/run/dbus", 0755); -} - -static bool start_dbus_daemon(void) -{ - char *argv[4]; - pid_t pid; - - argv[0] =3D "dbus-daemon"; - argv[1] =3D "--system"; - argv[2] =3D "--nosyslog"; - argv[3] =3D NULL; - - if (check_verbosity("dbus")) - setenv("DBUS_VERBOSE", "1", true); - - pid =3D execute_program(argv, environ, false, NULL); - if (pid < 0) - return false; - - if (!wait_for_socket("/run/dbus/system_bus_socket", 25 * 10000)) - return false; - - if (check_verbosity("dbus-monitor")) { - argv[0] =3D "dbus-monitor"; - argv[1] =3D "--system"; - argv[2] =3D NULL; - execute_program(argv, environ, false, NULL); - } - - l_debug("D-Bus is running"); - - return true; -} - -static bool start_haveged(void) -{ - char *argv[2]; - pid_t pid; - - argv[0] =3D "haveged"; - argv[1] =3D NULL; - - pid =3D execute_program(argv, environ, true, NULL); - if (pid < 0) - return false; - - return true; -} - -static bool set_interface_state(const char *if_name, bool isUp) -{ - char *state, *argv[4]; - pid_t pid; - - if (isUp) - state =3D "up"; - else - state =3D "down"; - - argv[0] =3D "ifconfig"; - argv[1] =3D (char *) if_name; - argv[2] =3D state; - argv[3] =3D NULL; - - pid =3D execute_program(argv, environ, true, NULL); - if (pid < 0) - return false; - - return true; -} - -static bool create_interface(const char *if_name, const char *phy_name) -{ - char *argv[9]; - pid_t pid; - - argv[0] =3D BIN_IW; - argv[1] =3D "phy"; - argv[2] =3D (char *) phy_name; - argv[3] =3D "interface"; - argv[4] =3D "add"; - argv[5] =3D (char *) if_name; - argv[6] =3D "type"; - argv[7] =3D "managed"; - argv[8] =3D NULL; - - pid =3D execute_program(argv, environ, true, NULL); - if (pid < 0) - return false; - - return true; -} - -static bool delete_interface(const char *if_name) -{ - char *argv[5]; - pid_t pid; - - argv[0] =3D BIN_IW; - argv[1] =3D "dev"; - argv[2] =3D (char *) if_name; - argv[3] =3D "del"; - argv[4] =3D NULL; - - pid =3D execute_program(argv, environ, true, NULL); - if (pid < 0) - return false; - - return true; -} - -static bool list_interfaces(void) -{ - char *argv[3]; - pid_t pid; - - argv[0] =3D "ifconfig"; - argv[1] =3D "-a"; - argv[2] =3D NULL; - - pid =3D execute_program(argv, environ, true, NULL); - if (pid < 0) - return false; - - return true; -} - -static bool list_hwsim_radios(void) -{ - char *argv[3]; - pid_t pid; - - argv[0] =3D BIN_HWSIM; - argv[1] =3D "--list"; - argv[2] =3D NULL; - - pid =3D execute_program(argv, environ, true, NULL); - if (pid < 0) - return false; - - return true; -} - -static int read_radio_id(void) -{ - static int current_radio_id; - - return current_radio_id++; -} - -struct hwsim_radio_params { - unsigned int channels; - bool p2p_device; - bool use_chanctx; - char *iftype_disable; - char *cipher_disable; -}; - -static int create_hwsim_radio(const char *radio_name, - struct hwsim_radio_params *params) -{ - char *argv[10]; - pid_t pid; - int idx =3D 0; - - /*TODO add the rest of params*/ - argv[idx++] =3D BIN_HWSIM; - argv[idx++] =3D "--create"; - argv[idx++] =3D "--name"; - argv[idx++] =3D (char *) radio_name; - argv[idx++] =3D "--nointerface"; - - if (params->iftype_disable) { - argv[idx++] =3D "--iftype-disable"; - argv[idx++] =3D params->iftype_disable; - } - - if (params->cipher_disable) { - argv[idx++] =3D "--cipher-disable"; - argv[idx++] =3D params->cipher_disable; - } - - argv[idx] =3D NULL; - - pid =3D execute_program(argv, environ, true, NULL); - if (pid < 0) - return -1; - - return read_radio_id(); -} - -static bool destroy_hwsim_radio(int radio_id) -{ - char *argv[4]; - char destroy_param[20]; - pid_t pid; - - sprintf(destroy_param, "--destroy=3D%d", radio_id); - - argv[0] =3D BIN_HWSIM; - argv[1] =3D destroy_param; - argv[2] =3D NULL; - - pid =3D execute_program(argv, environ, true, NULL); - if (pid < 0) - return false; - - return true; -} - -static pid_t register_hwsim_as_trans_medium(void) -{ - char *argv[16]; - unsigned int idx =3D 0; - - if (strcmp(gdb_opt, "hwsim") =3D=3D 0) { - argv[idx++] =3D "gdb"; - argv[idx++] =3D "--args"; - } - - argv[idx++] =3D BIN_HWSIM; - argv[idx++] =3D NULL; - - return execute_program(argv, environ, false, NULL); -} - -static void terminate_medium(pid_t medium_pid) -{ - kill_process(medium_pid); -} - -#define HOSTAPD_CTRL_INTERFACE_PREFIX "/var/run/hostapd" - -static bool loopback_started; - -static void start_loopback(void) -{ - char *argv[7]; - - if (loopback_started) - return; - - argv[0] =3D "ifconfig"; - argv[1] =3D "lo"; - argv[2] =3D "127.0.0.1"; - argv[3] =3D "up"; - argv[4] =3D NULL; - execute_program(argv, environ, false, NULL); - - argv[0] =3D "route"; - argv[1] =3D "add"; - argv[2] =3D "127.0.0.1"; - argv[3] =3D NULL; - execute_program(argv, environ, false, NULL); - - loopback_started =3D true; -} - -static pid_t start_phonesim(const char *test_name) -{ - char *argv[5]; - - argv[0] =3D BIN_PHONESIM; - argv[1] =3D "-p"; - argv[2] =3D "12345"; - argv[3] =3D "/usr/share/phonesim/default.xml"; - argv[4] =3D NULL; - - start_loopback(); - - setenv("OFONO_PHONESIM_CONFIG", "/tmp/phonesim.conf", true); - - return execute_program(argv, environ, false, test_name); -} - -static void stop_phonesim(pid_t pid) -{ - kill_process(pid); -} - -static pid_t start_ofono(const char *test_name) -{ - char *argv[5]; - bool verbose =3D check_verbosity(BIN_OFONO); - - argv[0] =3D BIN_OFONO; - argv[1] =3D "-n"; - argv[2] =3D "--plugin=3Datmodem,phonesim"; - - if (verbose) - argv[3] =3D "-d"; - else - argv[3] =3D NULL; - - argv[4] =3D NULL; - - start_loopback(); - - return execute_program(argv, environ, false, test_name); -} - -static void stop_ofono(pid_t pid) -{ - kill_process(pid); -} - -static pid_t start_hostapd(char **config_files, struct wiphy **wiphys, - const char *test_name, const char *radius_conf) -{ - char **argv; - pid_t pid; - int idx =3D 0; - uint32_t wait =3D 25 * 10000; - bool verbose =3D check_verbosity(BIN_HOSTAPD); - size_t ifnames_size; - char *ifnames; - int i; - - for (i =3D 0, ifnames_size =3D 0; wiphys[i]; i++) - ifnames_size +=3D 1 + strlen(wiphys[i]->interface_name); - - argv =3D alloca(sizeof(char *) * (9 + i)); - - if (strcmp(gdb_opt, "hostapd") =3D=3D 0) { - argv[idx++] =3D "gdb"; - argv[idx++] =3D "--args"; - wait =3D 0; - } - - argv[idx++] =3D BIN_HOSTAPD; - - ifnames =3D alloca(ifnames_size); - argv[idx++] =3D "-i"; - argv[idx++] =3D ifnames; - - argv[idx++] =3D "-g"; - argv[idx++] =3D wiphys[0]->hostapd_ctrl_interface; - - for (i =3D 0, ifnames_size =3D 0; wiphys[i]; i++) { - if (ifnames_size) - ifnames[ifnames_size++] =3D ','; - strcpy(ifnames + ifnames_size, wiphys[i]->interface_name); - ifnames_size +=3D strlen(wiphys[i]->interface_name); - - argv[idx++] =3D config_files[i]; - } - - if (radius_conf) - argv[idx++] =3D (void *)radius_conf; - - if (verbose) { - argv[idx++] =3D "-d"; - argv[idx++] =3D NULL; - } else { - argv[idx++] =3D NULL; - } - - pid =3D execute_program(argv, environ, false, log ? test_name : NULL); - if (pid < 0) { - goto exit; - } - - if (!wait_for_socket(wiphys[0]->hostapd_ctrl_interface, wait)) - pid =3D -1; -exit: - return pid; -} - -static void destroy_hostapd_instances(pid_t hostapd_pids[]) -{ - int i =3D 0; - - while (hostapd_pids[i] !=3D -1) { - kill_process(hostapd_pids[i]); - - l_debug("hostapd instance with pid=3D%d is destroyed", - hostapd_pids[i]); - - hostapd_pids[i] =3D -1; - - i++; - } -} - -#define TEST_TOP_DIR_DEFAULT_NAME "autotests" -#define TEST_DIR_PREFIX "test" - -static bool is_test_file(const char *file) -{ - size_t i; - static const char * const test_file_extension_table[] =3D { - "test", - "test.py", - "Test", - "Test.py", - NULL - }; - - for (i =3D 0; test_file_extension_table[i]; i++) { - if (l_str_has_suffix(file, test_file_extension_table[i])) - return true; - } - - return false; -} - -static int is_test_dir(const char *dir) -{ - return strncmp(dir, TEST_DIR_PREFIX, strlen(TEST_DIR_PREFIX)) =3D=3D 0; -} - -static bool find_test_configuration(const char *path, int level, - struct l_queue *config_queue); - -struct test_entry { - struct l_queue *test_queue; - char *path; -}; - -static int insert_py_test(const void *a, const void *b, void *user_data) -{ - return strcmp((const char *)a, (const char *)b); -} - -static int insert_test_entry(const void *a, const void *b, void *user_data) -{ - const struct test_entry *entry_a =3D a; - const struct test_entry *entry_b =3D b; - - return strcmp(entry_a->path, entry_b->path); -} - -static bool add_path(const char *path, int level, struct l_queue *config_q= ueue) -{ - DIR *dir =3D NULL; - struct l_queue *py_test_queue =3D NULL; - struct dirent *entry; - char *npath; - - dir =3D opendir(path); - if (!dir) { - l_error("Test directory does not exist: %s", path); - return false; - } - - while ((entry =3D readdir(dir))) { - if (entry->d_type =3D=3D DT_DIR) { - if (!strcmp(entry->d_name, ".") || - !strcmp(entry->d_name, "..")) - continue; - - if (level =3D=3D 0 && is_test_dir(entry->d_name)) { - npath =3D l_strdup_printf("%s/%s", path, - entry->d_name); - - find_test_configuration(npath, 1, config_queue); - - l_free(npath); - } - } else if (level =3D=3D 1 && is_test_file(entry->d_name)) { - if (!py_test_queue) - py_test_queue =3D l_queue_new(); - - l_queue_insert(py_test_queue, l_strdup(entry->d_name), - insert_py_test, NULL); - } - } - - if (py_test_queue && !l_queue_isempty(py_test_queue)) { - struct test_entry *entry =3D l_new(struct test_entry, 1); - - entry->test_queue =3D py_test_queue; - entry->path =3D l_strdup(path); - - l_queue_insert(config_queue, entry, insert_test_entry, NULL); - } - - closedir(dir); - return true; -} - -static bool find_test_configuration(const char *path, int level, - struct l_queue *config_queue) -{ - glob_t glist; - int i =3D 0; - int ret; - - if (!config_queue) - return false; - - ret =3D glob(path, 0, NULL, &glist); - if (ret !=3D 0) { - l_error("Could not match glob %s", path); - return false; - } - - while (glist.gl_pathv[i]) { - if (!add_path(glist.gl_pathv[i], level, config_queue)) - return false; - - i++; - } - - return true; -} - -#define HW_CONFIG_FILE_NAME "hw.conf" -#define HW_CONFIG_GROUP_HOSTAPD "HOSTAPD" -#define HW_CONFIG_GROUP_SETUP "SETUP" - -#define HW_CONFIG_SETUP_NUM_RADIOS "num_radios" -#define HW_CONFIG_SETUP_RADIO_CONFS "radio_confs" -#define HW_CONFIG_SETUP_MAX_EXEC_SEC "max_test_exec_interval_sec" -#define HW_CONFIG_SETUP_TMPFS_EXTRAS "tmpfs_extra_stuff" -#define HW_CONFIG_SETUP_START_IWD "start_iwd" -#define HW_CONFIG_SETUP_IWD_CONF_DIR "iwd_config_dir" -#define HW_CONFIG_SETUP_REG_DOMAIN "reg_domain" -#define HW_CONFIG_SETUP_NEEDS_HWSIM "needs_hwsim" - -static struct l_settings *read_hw_config(const char *test_dir_path) -{ - struct l_settings *hw_settings; - char *hw_file; - - hw_file =3D l_strdup_printf("%s/%s", test_dir_path, HW_CONFIG_FILE_NAME); - - hw_settings =3D l_settings_new(); - - if (!l_settings_load_from_file(hw_settings, hw_file)) { - l_error("No %s file found", HW_CONFIG_FILE_NAME); - goto error_exit; - } - - if (!l_settings_has_group(hw_settings, HW_CONFIG_GROUP_SETUP)) { - l_error("No %s setting group found in %s", - HW_CONFIG_GROUP_SETUP, hw_file); - goto error_exit; - } - - l_free(hw_file); - return hw_settings; - -error_exit: - l_free(hw_file); - l_settings_free(hw_settings); - return NULL; -} - -#define HW_CONFIG_PHY_CHANNELS "channels" -#define HW_CONFIG_PHY_CHANCTX "use_chanctx" -#define HW_CONFIG_PHY_P2P "p2p_device" -#define HW_CONFIG_PHY_IFTYPE_DISABLE "iftype_disable" -#define HW_CONFIG_PHY_CIPHER_DISABLE "cipher_disable" - -#define HW_MIN_NUM_RADIOS 1 - -#define HW_INTERFACE_PREFIX "wln" -#define HW_INTERFACE_STATE_UP true -#define HW_INTERFACE_STATE_DOWN false - -static bool configure_hw_radios(struct l_settings *hw_settings, - struct l_queue *wiphy_list) -{ - char **radio_conf_list; - int i, num_radios_requested; - bool status =3D false; - - l_settings_get_int(hw_settings, HW_CONFIG_GROUP_SETUP, - HW_CONFIG_SETUP_NUM_RADIOS, - &num_radios_requested); - - if (num_radios_requested < HW_MIN_NUM_RADIOS) { - l_error("%s must be greater or equal to %d", - HW_CONFIG_SETUP_NUM_RADIOS, HW_MIN_NUM_RADIOS); - return false; - } - - radio_conf_list =3D - l_settings_get_string_list(hw_settings, HW_CONFIG_GROUP_SETUP, - HW_CONFIG_SETUP_RADIO_CONFS, - ':'); - for (i =3D 0; i < num_radios_requested; i++) { - struct wiphy *wiphy; - struct hwsim_radio_params params =3D { 0 }; - - wiphy =3D l_new(struct wiphy, 1); - - sprintf(wiphy->name, "rad%d", i); - - /* radio not in radio_confs, use default parameters */ - if (!l_strv_contains(radio_conf_list, wiphy->name)) { - params.channels =3D 1; - params.p2p_device =3D true; - params.use_chanctx =3D true; - goto create; - } - - if (!l_settings_get_uint(hw_settings, wiphy->name, - HW_CONFIG_PHY_CHANNELS, - ¶ms.channels)) - params.channels =3D 1; - - if (!l_settings_get_bool(hw_settings, wiphy->name, - HW_CONFIG_PHY_P2P, ¶ms.p2p_device)) - params.p2p_device =3D true; - - if (!l_settings_get_bool(hw_settings, wiphy->name, - HW_CONFIG_PHY_CHANCTX, - ¶ms.use_chanctx)) - params.use_chanctx =3D true; - - params.iftype_disable =3D l_settings_get_string(hw_settings, - wiphy->name, - HW_CONFIG_PHY_IFTYPE_DISABLE); - params.cipher_disable =3D l_settings_get_string(hw_settings, - wiphy->name, - HW_CONFIG_PHY_CIPHER_DISABLE); - -create: - wiphy->id =3D create_hwsim_radio(wiphy->name, ¶ms); - wiphy->can_ap =3D true; - - if (wiphy->id < 0) { - l_free(wiphy); - goto exit; - } - - l_queue_push_tail(wiphy_list, wiphy); - } - - status =3D true; - -exit: - l_strfreev(radio_conf_list); - return status; -} - -static void wiphy_free(void *data) -{ - struct wiphy *wiphy =3D data; - - if (wiphy->interface_created) { - set_interface_state(wiphy->interface_name, - HW_INTERFACE_STATE_DOWN); - - if (delete_interface(wiphy->interface_name)) - l_debug("Removed interface %s", wiphy->interface_name); - else - l_error("Failed to remove interface %s", - wiphy->interface_name); - } - - /* Native interfaces cannot be destroyed */ - if (native_hw) { - set_interface_state(wiphy->interface_name, - HW_INTERFACE_STATE_DOWN); - } else { - destroy_hwsim_radio(wiphy->id); - l_debug("Removed radio id %d", wiphy->id); - } - - l_free(wiphy->hostapd_config); - l_free(wiphy->hostapd_ctrl_interface); - l_free(wiphy->interface_name); - - l_free(wiphy); -} - -static bool configure_hostapd_instances(struct l_settings *hw_settings, - char *config_dir_path, - struct l_queue *wiphy_list, - pid_t hostapd_pids_out[], - int *phys_used) -{ - char **hostap_keys; - int i; - char **hostapd_config_file_paths; - struct wiphy **wiphys; - const char *radius_config =3D NULL; - - *phys_used =3D 0; - - if (!l_settings_has_group(hw_settings, HW_CONFIG_GROUP_HOSTAPD)) { - l_info("No hostapd instances to create"); - return true; - } - - hostap_keys =3D - l_settings_get_keys(hw_settings, HW_CONFIG_GROUP_HOSTAPD); - - for (i =3D 0; hostap_keys[i]; i++); - - hostapd_config_file_paths =3D l_new(char *, i + 1); - wiphys =3D alloca(sizeof(struct wiphy *) * (i + 1)); - memset(wiphys, 0, sizeof(struct wiphy *) * (i + 1)); - - hostapd_pids_out[0] =3D -1; - - for (i =3D 0; hostap_keys[i]; i++) { - const struct l_queue_entry *wiphy_entry; - const char *hostapd_config_file; - unsigned wiphy_idx =3D 0; - - hostapd_config_file =3D - l_settings_get_value(hw_settings, - HW_CONFIG_GROUP_HOSTAPD, - hostap_keys[i]); - - hostapd_config_file_paths[i] =3D - l_strdup_printf("%s/%s", config_dir_path, - hostapd_config_file); - - if (!path_exist(hostapd_config_file_paths[i])) { - l_error("%s : hostapd configuration file [%s] " - "does not exist.", HW_CONFIG_FILE_NAME, - hostapd_config_file_paths[i]); - goto done; - } - - if (!strcmp(hostap_keys[i], "radius_server")) { - radius_config =3D l_settings_get_value(hw_settings, - HW_CONFIG_GROUP_HOSTAPD, - "radius_server"); - continue; - } - - for (wiphy_entry =3D l_queue_get_entries(wiphy_list); - wiphy_entry; - wiphy_entry =3D wiphy_entry->next, - wiphy_idx++) { - struct wiphy *wiphy =3D wiphy_entry->data; - - /* - * We can skip this check in native mode since we have - * no control over the phy name. Any test requiring a - * "special" radio should not be ran in native mode. - */ - if (!native_hw && strcmp(wiphy->name, hostap_keys[i])) - continue; - - if (wiphy->used_by_hostapd) { - /* - * Since we bypass the above check in native - * mode we could still get here. We can just - * continue searching for more adapters if this - * one is already in use. - */ - if (native_hw) - continue; - - l_error("Wiphy %s already used by hostapd", - wiphy->name); - goto done; - } - - if (!wiphy->can_ap) - continue; - - wiphys[i] =3D wiphy; - break; - } - - if (!wiphy_entry) { - l_error("Failed to find available wiphy."); - goto done; - } - - if (native_hw) - goto hostapd_done; - - wiphys[i]->interface_name =3D l_strdup_printf("%s%d", - HW_INTERFACE_PREFIX, - wiphy_idx); - if (!create_interface(wiphys[i]->interface_name, - wiphys[i]->name)) { - l_error("Failed to create hostapd interface %s on " - "radio %s", - wiphys[i]->interface_name, wiphys[i]->name); - goto done; - } - - wiphys[i]->interface_created =3D true; - l_info("Created hostapd interface %s on %s radio", - wiphys[i]->interface_name, wiphys[i]->name); - - if (!native_hw && !set_interface_state(wiphys[i]->interface_name, - HW_INTERFACE_STATE_UP)) { - l_error("Failed to set %s state UP", - wiphys[i]->interface_name); - goto done; - } - -hostapd_done: - wiphys[i]->used_by_hostapd =3D true; - wiphys[i]->hostapd_ctrl_interface =3D - l_strdup_printf("%s/%s", HOSTAPD_CTRL_INTERFACE_PREFIX, - wiphys[0]->interface_name); - wiphys[i]->hostapd_config =3D l_strdup(hostapd_config_file); - - (*phys_used)++; - } - - hostapd_pids_out[0] =3D start_hostapd(hostapd_config_file_paths, wiphys, - basename(config_dir_path), - radius_config); - hostapd_pids_out[1] =3D -1; - -done: - l_strfreev(hostapd_config_file_paths); - - if (hostapd_pids_out[0] < 1) - return false; - - return true; -} - -static pid_t start_iwd(const char *config_dir, struct l_queue *wiphy_list, - const char *ext_options, int num_phys, const char *test_name) -{ - char *argv[13], **envp; - char *iwd_phys =3D NULL; - pid_t ret; - int idx =3D 0; - L_AUTO_FREE_VAR(char *, fd_option) =3D NULL; - - if (valgrind) { - L_AUTO_FREE_VAR(char *, valgrind_log); - int fd; - - argv[idx++] =3D "valgrind"; - argv[idx++] =3D "--leak-check=3Dfull"; - - /* - * Valgrind needs --log-fd if we want both stderr and stdout - */ - if (log) - valgrind_log =3D l_strdup_printf("%s/%s/valgrind.log", - log_dir, test_name); - else - valgrind_log =3D l_strdup("/tmp/valgrind.log"); - - fd =3D open(valgrind_log, O_WRONLY | O_CREAT | O_APPEND, - S_IRUSR | S_IWUSR); - - if (log) { - if (fchown(fd, log_uid, log_gid) < 0) - l_error("chown failed"); - } - - fd_option =3D l_strdup_printf("--log-fd=3D%d", fd); - argv[idx++] =3D fd_option; - } - - if (strcmp(gdb_opt, "iwd") =3D=3D 0) { - argv[idx++] =3D "gdb"; - argv[idx++] =3D "--args"; - } - - argv[idx++] =3D BIN_IWD; - - if (check_verbosity(BIN_IWD) || shell) - argv[idx++] =3D "-d"; - - argv[idx] =3D NULL; - - if (wiphy_list) { - const struct l_queue_entry *wiphy_entry; - struct l_string *list =3D l_string_new(64); - - for (wiphy_entry =3D l_queue_get_entries(wiphy_list); - wiphy_entry; - wiphy_entry =3D wiphy_entry->next) { - struct wiphy *wiphy =3D wiphy_entry->data; - - if (wiphy->used_by_hostapd) - continue; - - /* - * Break out, only adding the required number of phys - * for this test. - */ - if (num_phys =3D=3D 0) - break; - - l_string_append_printf(list, "%s,", wiphy->name); - - num_phys--; - } - - iwd_phys =3D l_string_unwrap(list); - /* Take care of last comma */ - iwd_phys[strlen(iwd_phys) - 1] =3D '\0'; - - argv[idx++] =3D "-p"; - argv[idx++] =3D iwd_phys; - argv[idx] =3D NULL; - } - - argv[idx++] =3D (char *)ext_options; - argv[idx] =3D NULL; - - envp =3D l_strv_copy(environ); - envp =3D l_strv_append_printf(envp, "CONFIGURATION_DIRECTORY=3D%s", - config_dir); - envp =3D l_strv_append_printf(envp, "STATE_DIRECTORY=3D%s", - DAEMON_STORAGEDIR); - - ret =3D execute_program(argv, envp, false, test_name); - - l_strv_free(envp); - - l_free(iwd_phys); - - return ret; -} - -static void terminate_iwd(pid_t iwd_pid) -{ - kill_process(iwd_pid); -} - -static pid_t start_monitor(const char *test_name) -{ - char *argv[6]; - char *write_arg; - pid_t pid; - - write_arg =3D l_strdup_printf("%s/%s/monitor.pcap", log_dir, test_name); - - argv[0] =3D "iwmon"; - argv[1] =3D "--nortnl"; - argv[2] =3D "--nowiphy"; - argv[3] =3D "--write"; - argv[4] =3D write_arg; - argv[5] =3D NULL; - - pid =3D execute_program(argv, environ, false, test_name); - - l_free(write_arg); - - return pid; -} - -static bool create_tmpfs_extra_stuff(char **tmpfs_extra_stuff) -{ - size_t i =3D 0; - - if (!tmpfs_extra_stuff) - return true; - - while (tmpfs_extra_stuff[i]) { - char *link_dir; - char *target_dir; - - target_dir =3D realpath(tmpfs_extra_stuff[i], NULL); - - if (!path_exist(target_dir)) { - l_error("No such directory: %s", target_dir); - l_free(target_dir); - return false; - } - - link_dir =3D l_strdup_printf("%s%s", "/tmp", - rindex(target_dir, '/')); - - if (symlink(target_dir, link_dir) < 0) { - l_error("Failed to create symlink %s for %s: %s", - link_dir, target_dir, strerror(errno)); - - l_free(target_dir); - l_free(link_dir); - return false; - } - - l_free(tmpfs_extra_stuff[i]); - l_free(target_dir); - - tmpfs_extra_stuff[i] =3D link_dir; - i++; - } - - return true; -} - -static bool remove_absolute_path_dirs(char **tmpfs_extra_stuff) -{ - size_t i =3D 0; - - if (!tmpfs_extra_stuff) - return true; - - while (tmpfs_extra_stuff[i]) { - if (unlink(tmpfs_extra_stuff[i]) < 0) { - l_error("Failed to remove symlink for %s: %s", - tmpfs_extra_stuff[i], strerror(errno)); - - return false; - } - - i++; - } - - return true; -} - -#define CONSOLE_LN_DEFAULT "\x1B[0m" -#define CONSOLE_LN_RED "\x1B[31m" -#define CONSOLE_LN_GREEN "\x1B[32m" -#define CONSOLE_LN_BLACK "\x1B[30m" -#define CONSOLE_LN_YELLOW "\x1B[33m" -#define CONSOLE_LN_RESET "\033[0m" - -#define CONSOLE_LN_BOLD "\x1b[1m" - -#define CONSOLE_BG_WHITE "\e[47m" -#define CONSOLE_BG_DEFAULT "\e[0m" - -enum test_status { - TEST_STATUS_STARTED, - TEST_STATUS_PASSED, - TEST_STATUS_FAILED, - TEST_STATUS_TIMEDOUT, -}; - -static void print_test_status(char *test_name, enum test_status ts, - double interval) -{ - const char *clear_line =3D "\r"; - int int_len; - char *color_str; - char *status_str; - char *interval_str; - char *line_end =3D ""; - - switch (ts) { - case TEST_STATUS_STARTED: - color_str =3D CONSOLE_LN_RESET; - status_str =3D "STARTED "; - - if (strcmp(verbose_opt, "none")) - line_end =3D "\n"; - - break; - case TEST_STATUS_PASSED: - printf("%s", clear_line); - color_str =3D CONSOLE_LN_GREEN; - status_str =3D "PASSED "; - line_end =3D "\n"; - - break; - case TEST_STATUS_FAILED: - printf("%s", clear_line); - color_str =3D CONSOLE_LN_RED; - status_str =3D "FAILED "; - line_end =3D "\n"; - - break; - case TEST_STATUS_TIMEDOUT: - printf("%s", clear_line); - color_str =3D CONSOLE_LN_YELLOW; - status_str =3D "TIMED OUT "; - line_end =3D "\n"; - - break; - } - - if (interval > 0) - int_len =3D snprintf(NULL, 0, "%.3f", interval); - else - int_len =3D 3; - - int_len++; - - interval_str =3D l_malloc(int_len); - memset(interval_str, ' ', int_len); - interval_str[int_len - 1] =3D '\0'; - - if (interval > 0) - sprintf(interval_str, "%.3f sec", interval); - else - sprintf(interval_str, "%s", "..."); - - printf("%s%s%s%-60s%7s%s", color_str, status_str, CONSOLE_LN_RESET, - test_name, interval_str, line_end); - - fflush(stdout); - - l_free(interval_str); -} - -static void test_timeout_timer_tick(struct l_timeout *timeout, void *user_= data) -{ - pid_t *test_exec_pid =3D (pid_t *) user_data; - - kill_process(*test_exec_pid); - - l_main_quit(); -} - -static void test_timeout_signal_handler(uint32_t signo, void *user_data) -{ - switch (signo) { - case SIGINT: - case SIGTERM: - l_main_quit(); - break; - } -} - -static pid_t start_execution_timeout_timer(unsigned int max_exec_interval_= sec, - pid_t *test_exec_pid) -{ - struct l_timeout *test_exec_timeout; - pid_t test_timer_pid; - - test_timer_pid =3D fork(); - if (test_timer_pid < 0) { - l_error("Failed to fork new process"); - return -1; - } - - if (test_timer_pid =3D=3D 0) { - if (!l_main_init()) - exit(EXIT_FAILURE); - - test_exec_timeout =3D - l_timeout_create(max_exec_interval_sec, - test_timeout_timer_tick, - test_exec_pid, - NULL); - - l_main_run_with_signal(test_timeout_signal_handler, NULL); - - l_timeout_remove(test_exec_timeout); - - l_main_exit(); - - exit(EXIT_SUCCESS); - } - - return test_timer_pid; -} - -struct test_stats { - char *config_cycle_name; - unsigned int num_passed; - unsigned int num_failed; - unsigned int num_timedout; - double py_run_time; -}; - -static void run_py_tests(struct l_settings *hw_settings, - struct l_queue *test_queue, - struct l_queue *test_stats_queue, - const char *test_name) -{ - char *argv[3]; - pid_t test_exec_pid, test_timer_pid =3D -1; - struct timeval time_before, time_after, time_elapsed; - unsigned int max_exec_interval; - char *py_test =3D NULL; - struct test_stats *test_stats; - pid_t monitor_pid =3D -1; - - if (!l_settings_get_uint(hw_settings, HW_CONFIG_GROUP_SETUP, - HW_CONFIG_SETUP_MAX_EXEC_SEC, - &max_exec_interval)) - max_exec_interval =3D TEST_MAX_EXEC_TIME_SEC; - - l_info(CONSOLE_LN_BOLD "%-10s%-60s%s" CONSOLE_LN_RESET, "Status", - "Test", "Duration"); - -start_next_test: - - if (l_queue_isempty(test_queue)) - return; - - py_test =3D (char *) l_queue_pop_head(test_queue); - if (!py_test) - return; - - if (log) { - char *test_path; - char *ext; - char *full_path; - - test_path =3D l_strdup_printf("%s/%s", test_name, py_test); - ext =3D strchr(test_path, '.'); - ext[0] =3D '\0'; - - full_path =3D l_strdup_printf("%s/%s", log_dir, test_path); - - mkdir(full_path, 0755); - if (chown(full_path, log_uid, log_gid) < 0) - l_error("chown failed %s", full_path); - - l_free(full_path); - - monitor_pid =3D start_monitor(test_path); - - l_free(test_path); - } - - argv[0] =3D "python3"; - argv[1] =3D py_test; - argv[2] =3D NULL; - - print_test_status(py_test, TEST_STATUS_STARTED, 0); - test_exec_pid =3D execute_program(argv, environ, false, test_name); - - gettimeofday(&time_before, NULL); - - if (!strcmp(gdb_opt, "none")) - test_timer_pid =3D start_execution_timeout_timer( - max_exec_interval, &test_exec_pid); - - test_stats =3D (struct test_stats *) l_queue_peek_tail(test_stats_queue); - - while (true) { - pid_t corpse; - int status; - double interval; - - corpse =3D waitpid(WAIT_ANY, &status, 0); - - if (corpse < 0 || corpse =3D=3D 0) - continue; - - if (test_exec_pid =3D=3D corpse) { - gettimeofday(&time_after, NULL); - - if (test_timer_pid !=3D -1) - kill_process(test_timer_pid); - - timersub(&time_after, &time_before, &time_elapsed); - interval =3D time_elapsed.tv_sec + - 1e-6 * time_elapsed.tv_usec; - - if (WIFEXITED(status) && - WEXITSTATUS(status) =3D=3D EXIT_SUCCESS) { - print_test_status(py_test, TEST_STATUS_PASSED, - interval); - test_stats->num_passed++; - } else if (WIFSIGNALED(status)) { - print_test_status(py_test, TEST_STATUS_TIMEDOUT, - interval); - test_stats->num_timedout++; - } else { - print_test_status(py_test, TEST_STATUS_FAILED, - interval); - test_stats->num_failed++; - } - - test_stats->py_run_time +=3D interval; - - break; - } else if (WIFSTOPPED(status)) - l_info("Process %d stopped with signal %d", corpse, - WSTOPSIG(status)); - else if (WIFCONTINUED(status)) - l_info("Process %d continued", corpse); - } - - l_free(py_test); - py_test =3D NULL; - - if (monitor_pid !=3D -1) { - kill_process(monitor_pid); - monitor_pid =3D -1; - } - - goto start_next_test; -} - -static void set_config_cycle_info(const char *config_dir_path, - struct l_queue *test_stats_queue) -{ - char sep_line[80]; - char *config_name_ptr; - struct test_stats *test_stats; - - memset(sep_line, '_', sizeof(sep_line) - 1); - sep_line[sizeof(sep_line) - 1] =3D '\0'; - - config_name_ptr =3D strrchr(config_dir_path, '/'); - config_name_ptr++; - - l_info("%s", sep_line); - l_info(CONSOLE_LN_BOLD "Starting configuration cycle No: %d [%s]" - CONSOLE_LN_RESET, l_queue_length(test_stats_queue) + 1, - config_name_ptr); - - test_stats =3D l_new(struct test_stats, 1); - test_stats->config_cycle_name =3D strdup(config_name_ptr); - - l_queue_push_tail(test_stats_queue, test_stats); -} - -static void set_wiphy_list(struct l_queue *wiphy_list) -{ - const struct l_queue_entry *wiphy_entry; - int size =3D 32; - char *var; - - for (wiphy_entry =3D l_queue_get_entries(wiphy_list); - wiphy_entry; wiphy_entry =3D wiphy_entry->next) { - struct wiphy *wiphy =3D wiphy_entry->data; - - size +=3D 32 + strlen(wiphy->name); - if (wiphy->used_by_hostapd) { - size +=3D 32 + strlen(wiphy->interface_name) + - strlen(wiphy->hostapd_ctrl_interface) + - strlen(wiphy->hostapd_config); - } - } - - var =3D alloca(size); - size =3D 0; - - for (wiphy_entry =3D l_queue_get_entries(wiphy_list); - wiphy_entry; wiphy_entry =3D wiphy_entry->next) { - struct wiphy *wiphy =3D wiphy_entry->data; - - if (size) - var[size++] =3D '\n'; - - size +=3D sprintf(var + size, "%s=3D", wiphy->name); - - if (wiphy->used_by_hostapd) - size +=3D sprintf(var + size, - "hostapd,name=3D%s,ctrl_interface=3D%s," - "config=3D%s", - wiphy->interface_name, - wiphy->hostapd_ctrl_interface, - wiphy->hostapd_config); - else - size +=3D sprintf(var + size, "iwd"); - } - - var[size++] =3D '\0'; - - setenv("TEST_WIPHY_LIST", var, true); -} - -static void set_reg_domain(const char *domain) -{ - char *argv[5]; - - argv[0] =3D "iw"; - argv[1] =3D "reg"; - argv[2] =3D "set"; - argv[3] =3D (char *) domain; - argv[4] =3D NULL; - - execute_program(argv, environ, false, NULL); -} - -static void wiphy_up(void *data, void *user_data) -{ - struct wiphy *wiphy =3D data; - - set_interface_state(wiphy->interface_name, true); -} - -static void wiphy_reset(void *data, void *user_data) -{ - struct wiphy *wiphy =3D data; - - wiphy->used_by_hostapd =3D false; - - l_free(wiphy->hostapd_config); - wiphy->hostapd_config =3D NULL; - l_free(wiphy->hostapd_ctrl_interface); - wiphy->hostapd_ctrl_interface =3D NULL; -} - -static void create_network_and_run_tests(void *data, void *user_data) -{ - pid_t hostapd_pids[HWSIM_RADIOS_MAX]; - pid_t iwd_pid =3D -1; - pid_t medium_pid =3D -1; - pid_t ofono_pid =3D -1; - pid_t phonesim_pid =3D -1; - char *config_dir_path; - char *iwd_config_dir; - char **tmpfs_extra_stuff =3D NULL; - struct l_settings *hw_settings; - struct l_queue *test_queue; - struct l_queue *test_stats_queue; - bool start_iwd_daemon =3D true; - bool needs_hwsim =3D false; - bool ofono_req =3D false; - const char *sim_keys; - const char *iwd_ext_options =3D NULL; - const char *reg_domain; - int phys_used; - int num_radios; - struct test_entry *entry =3D data; - char *test_name =3D NULL; - - memset(hostapd_pids, -1, sizeof(hostapd_pids)); - - config_dir_path =3D (char *) entry->path; - test_queue =3D (struct l_queue *) entry->test_queue; - test_stats_queue =3D (struct l_queue *) user_data; - - if (l_queue_isempty(test_queue)) { - l_error("No Python IWD tests have been found in %s", - config_dir_path); - return; - } - - set_config_cycle_info(config_dir_path, test_stats_queue); - - hw_settings =3D read_hw_config(config_dir_path); - if (!hw_settings) - return; - - l_info("Configuring network..."); - - if (log) { - char *log_path; - - test_name =3D basename(config_dir_path); - log_path =3D l_strdup_printf("%s/%s", log_dir, test_name); - - mkdir(log_path, 0755); - if (chown(log_path, log_uid, log_gid) < 0) - l_error("chown failed"); - - l_free(log_path); - } - - if (chdir(config_dir_path) < 0) { - l_error("Failed to change to test directory: %s", - strerror(errno)); - goto free_hw_settings; - } - - tmpfs_extra_stuff =3D - l_settings_get_string_list(hw_settings, HW_CONFIG_GROUP_SETUP, - HW_CONFIG_SETUP_TMPFS_EXTRAS, - ':'); - - sim_keys =3D l_settings_get_value(hw_settings, HW_CONFIG_GROUP_SETUP, - "sim_keys"); - - if (sim_keys) { - if (!strcmp(sim_keys, "ofono")) { - bool ofono_found =3D false; - bool phonesim_found =3D false; - - if (!system("which ofonod > /dev/null 2>&1")) - ofono_found =3D true; - - if (!system("which phonesim > /dev/null 2>&1")) - phonesim_found =3D true; - - if (!ofono_found || !phonesim_found) { - l_info("ofono or phonesim not found, skipping"); - goto free_tmpfs_extra; - } - - ofono_req =3D true; - iwd_ext_options =3D "--plugin=3Dofono"; - } else { - setenv("IWD_SIM_KEYS", sim_keys, true); - iwd_ext_options =3D "--plugin=3Dsim_hardcoded"; - } - } - - /* turn on/off timeouts if GDB is being used */ - if (!strcmp(gdb_opt, "none")) - setenv("IWD_TEST_TIMEOUTS", "on", true); - else - setenv("IWD_TEST_TIMEOUTS", "off", true); - - if (!create_tmpfs_extra_stuff(tmpfs_extra_stuff)) - goto remove_abs_paths; - - l_settings_get_int(hw_settings, HW_CONFIG_GROUP_SETUP, - HW_CONFIG_SETUP_NUM_RADIOS, - &num_radios); - - if (!native_hw) { - reg_domain =3D l_settings_get_value(hw_settings, - HW_CONFIG_GROUP_SETUP, - HW_CONFIG_SETUP_REG_DOMAIN); - if (reg_domain) - set_reg_domain(reg_domain); - - wiphy_list =3D l_queue_new(); - - if (!configure_hw_radios(hw_settings, wiphy_list)) - goto remove_abs_paths; - - medium_pid =3D register_hwsim_as_trans_medium(); - if (medium_pid < 0) - goto remove_abs_paths; - - if (check_verbosity("hwsim")) { - list_hwsim_radios(); - list_interfaces(); - } - } else { - int len; - - l_settings_get_bool(hw_settings, HW_CONFIG_GROUP_SETUP, - HW_CONFIG_SETUP_NEEDS_HWSIM, &needs_hwsim); - - /* Skip test that require hwsim dbus APIs (hwsim not running) */ - if (needs_hwsim) { - l_error("test requires hwsim, skipping"); - goto remove_abs_paths; - } - - len =3D l_queue_length(wiphy_list); - - /* Skip tests that need more radios than we have */ - if (num_radios > len) { - l_error("test requires %d radios, only %d found", - num_radios, len); - goto remove_abs_paths; - } - - l_queue_foreach(wiphy_list, wiphy_up, NULL); - } - - if (check_verbosity("tls")) - setenv("IWD_TLS_DEBUG", "on", true); - - if (!configure_hostapd_instances(hw_settings, config_dir_path, - wiphy_list, hostapd_pids, - &phys_used)) - goto exit_hostapd; - - l_settings_get_bool(hw_settings, HW_CONFIG_GROUP_SETUP, - HW_CONFIG_SETUP_START_IWD, &start_iwd_daemon); - - if (start_iwd_daemon) { - /* - * In native mode we may have more radios than a test actually - * needs. This would result in IWD managing all phys that - * hostapd wasn't using, which could throw off test results. - * By passing the number of phys the test expects IWD to have - * we can leave the remaining (unneeded) phys unmanaged. - */ - int iwd_phys =3D num_radios - phys_used; - - iwd_config_dir =3D - l_settings_get_string(hw_settings, - HW_CONFIG_GROUP_SETUP, - HW_CONFIG_SETUP_IWD_CONF_DIR); - if (!iwd_config_dir) - iwd_config_dir =3D "/tmp"; - - iwd_pid =3D start_iwd(iwd_config_dir, wiphy_list, - iwd_ext_options, iwd_phys, test_name); - - if (iwd_pid =3D=3D -1) - goto exit_hostapd; - } else { - /* tells pytest to start iwd with valgrind */ - if (valgrind) - setenv("IWD_TEST_VALGRIND", "on", true); - } - - if (ofono_req) { - phonesim_pid =3D start_phonesim(test_name); - ofono_pid =3D start_ofono(test_name); - } - - set_wiphy_list(wiphy_list); - - if (!shell) - run_py_tests(hw_settings, test_queue, test_stats_queue, - test_name); - else { - if (system("/bin/sh")) - l_info("executing /bin/sh failed"); - } - - l_info("Destructing network..."); - - /* Script has responsibility to cleanup any iwd instances it started */ - if (iwd_pid > 0) - terminate_iwd(iwd_pid); - - /* /tmp/valgrind.log will only exist without logging turned on */ - if (valgrind && !log) { - if (system("cat /tmp/valgrind.log")) - l_info("cat /tmp/valgrind.log failed"); - - if (system("echo \"\" > /tmp/valgrind.log")) - l_info("Failed to reset /tmp/valgrind.log"); - } - - if (log) { - L_AUTO_FREE_VAR(char *, dmesg); - L_AUTO_FREE_VAR(char *, kernel_log); - - kernel_log =3D l_strdup_printf("%s/kernel.log", log_dir); - dmesg =3D l_strdup_printf("dmesg > %s", kernel_log); - - if (system(dmesg)) - l_error("dmesg failed"); - if (chown(kernel_log, log_uid, log_gid)) - l_error("chown failed"); - } - - if (ofono_req) { - loopback_started =3D false; - stop_ofono(ofono_pid); - stop_phonesim(phonesim_pid); - } - -exit_hostapd: - destroy_hostapd_instances(hostapd_pids); - - if (!native_hw) - terminate_medium(medium_pid); - -remove_abs_paths: - remove_absolute_path_dirs(tmpfs_extra_stuff); - - /* - * If running in hwsim mode, we want to completely free/destroy the - * wiphy list since it will be re-populated on the next test. For the - * native case we want to reset the list as if it was freshly - * discovered. This ensures that all the hostapd flags get reset. - */ - if (!native_hw) - l_queue_destroy(wiphy_list, wiphy_free); - else - l_queue_foreach(wiphy_list, wiphy_reset, NULL); - -free_tmpfs_extra: - l_strfreev(tmpfs_extra_stuff); -free_hw_settings: - l_settings_free(hw_settings); -} - -struct stat_totals { - unsigned int total_passed; - unsigned int total_failed; - unsigned int total_timedout; - double total_duration; -}; - -static void print_test_stat(void *data, void *user_data) -{ - struct test_stats *test_stats; - struct stat_totals *stat_totals; - char *str_runtime, *str_passed, *str_failed, *str_timedout; - - test_stats =3D (struct test_stats *) data; - stat_totals =3D (struct stat_totals *) user_data; - - stat_totals->total_duration +=3D test_stats->py_run_time; - stat_totals->total_passed +=3D test_stats->num_passed; - stat_totals->total_failed +=3D test_stats->num_failed; - stat_totals->total_timedout +=3D test_stats->num_timedout; - - if (test_stats->py_run_time) - str_runtime =3D l_strdup_printf("| %9.3f sec", - test_stats->py_run_time); - else - str_runtime =3D l_strdup_printf("| %9s", "Skipped"); - - if (test_stats->num_passed) - str_passed =3D l_strdup_printf(" %6d ", test_stats->num_passed); - else - str_passed =3D l_strdup_printf(" %6c ", '-'); - - if (test_stats->num_failed) - str_failed =3D l_strdup_printf(" %6d ", test_stats->num_failed); - else - str_failed =3D l_strdup_printf(" %6c ", '-'); - - if (test_stats->num_timedout) - str_timedout =3D l_strdup_printf(" %9d ", - test_stats->num_timedout); - else - str_timedout =3D l_strdup_printf(" %9c ", '-'); - - l_info(CONSOLE_LN_BOLD "%27s " - CONSOLE_LN_DEFAULT "|" CONSOLE_LN_GREEN "%s" - CONSOLE_LN_DEFAULT "|" CONSOLE_LN_RED "%s" - CONSOLE_LN_DEFAULT "|" CONSOLE_LN_YELLOW "%s" - CONSOLE_LN_RESET "%s", test_stats->config_cycle_name, - str_passed, str_failed, str_timedout, str_runtime); - - l_free(str_passed); - l_free(str_failed); - l_free(str_timedout); - l_free(str_runtime); -} - -static void print_results(struct l_queue *test_stat_queue) -{ - struct stat_totals stat_totals =3D { 0, 0, 0, 0 }; - char sep_line[80]; - - memset(sep_line, '_', sizeof(sep_line) - 1); - sep_line[sizeof(sep_line) - 1] =3D '\0'; - - l_info("%s\n" CONSOLE_LN_RESET, sep_line); - l_info("%27s " CONSOLE_LN_DEFAULT "|" CONSOLE_LN_GREEN " %s " - CONSOLE_LN_DEFAULT "|" CONSOLE_LN_RED " %5s " - CONSOLE_LN_DEFAULT "|" CONSOLE_LN_YELLOW " %9s " - CONSOLE_LN_RESET "| Duration", - "Configuration cycle", "PASSED", "FAILED", "TIMED OUT"); - - memset(sep_line, '-', sizeof(sep_line) - 1); - sep_line[sizeof(sep_line) - 1] =3D '\0'; - l_info("%s" CONSOLE_LN_RESET, sep_line); - - l_queue_foreach(test_stat_queue, print_test_stat, &stat_totals); - - l_info("%s" CONSOLE_LN_RESET, sep_line); - l_info("%27s " - CONSOLE_LN_DEFAULT "|" CONSOLE_LN_GREEN " %6d " - CONSOLE_LN_DEFAULT "|" CONSOLE_LN_RED " %6d " - CONSOLE_LN_DEFAULT "|" CONSOLE_LN_YELLOW " %9d " - CONSOLE_LN_RESET "| %9.3f sec", - "Total", stat_totals.total_passed, stat_totals.total_failed, - stat_totals.total_timedout, stat_totals.total_duration); - - memset(sep_line, '_', sizeof(sep_line) - 1); - sep_line[sizeof(sep_line) - 1] =3D '\0'; - l_info("%s" CONSOLE_LN_RESET, sep_line); -} - -static void test_stat_queue_entry_destroy(void *data) -{ - struct test_stats *ts; - - ts =3D (struct test_stats *) data; - - l_free(ts->config_cycle_name); - l_free(ts); -} - -static void free_test_entry(void *data) -{ - struct test_entry *entry =3D data; - - l_free(entry->path); - l_free(entry); -} - -static void run_auto_tests(void) -{ - L_AUTO_FREE_VAR(char*, test_home_path) =3D NULL; - L_AUTO_FREE_VAR(char*, env_path) =3D NULL; - int i; - struct l_queue *test_config_queue; - struct l_queue *test_stat_queue; - char **test_config_dirs; - - if (log) { - if (mount("logdir", log_dir, "9p", 0, - "trans=3Dvirtio,version=3D9p2000.L") < 0) { - l_error("Mounting %s failed", log_dir); - return; - } - } - - env_path =3D l_strdup_printf("%s/src:%s/tools:%s", top_level_path, - top_level_path, getenv("PATH")); - - setenv("PATH", env_path, true); - - test_home_path =3D l_strdup_printf("%s/%s", top_level_path, - TEST_TOP_DIR_DEFAULT_NAME); - - if (!path_exist(test_home_path)) { - l_error("Test directory %s does not exist", test_home_path); - return; - } - - test_config_queue =3D l_queue_new(); - if (!test_config_queue) - return; - - test_config_dirs =3D l_strsplit(test_action_params, ','); - - if (test_config_dirs[0]) { - i =3D 0; - - while (test_config_dirs[i]) { - if (strchr(test_config_dirs[i], '/')) { - if (!find_test_configuration( - test_config_dirs[i], 1, - test_config_queue)) - goto exit; - } else { - char *config_dir_path; - - config_dir_path =3D - l_strdup_printf("%s/%s", test_home_path, - test_config_dirs[i]); - - if (!find_test_configuration(config_dir_path, 1, - test_config_queue)) { - l_free(config_dir_path); - - goto exit; - } - - l_free(config_dir_path); - } - - i++; - } - } else { - /* - * --shell without any specific tests implies 'shell' test - */ - if (shell) { - char *config_dir_path; - config_dir_path =3D l_strdup_printf("%s/shell", - test_home_path); - - if (!find_test_configuration(config_dir_path, 1, - test_config_queue)) { - l_free(config_dir_path); - goto exit; - } - - l_free(config_dir_path); - } else { - l_info("Automatic test execution requested"); - l_info("Searching for the test configurations..."); - - if (!find_test_configuration(test_home_path, 0, - test_config_queue)) - goto exit; - } - } - - if (l_queue_isempty(test_config_queue)) { - l_error("No test configuration discovered"); - goto exit; - } - - create_dbus_system_conf(); - - if (!start_dbus_daemon()) - goto exit; - - if (!start_haveged()) { - l_error("Failed to start haveged"); - goto exit; - } - - test_stat_queue =3D l_queue_new(); - - l_queue_foreach(test_config_queue, create_network_and_run_tests, - test_stat_queue); - - print_results(test_stat_queue); - - l_queue_destroy(test_stat_queue, test_stat_queue_entry_destroy); - -exit: - l_strfreev(verbose_apps); - l_strfreev(test_config_dirs); - l_queue_destroy(test_config_queue, free_test_entry); -} - -static void run_unit_tests(void) -{ - DIR *d; - struct dirent *dirent; - char *argv[2]; - char *unit_test_abs_path =3D NULL; - char **unit_tests =3D NULL; - - if (strcmp(test_action_params, "")) { - unit_tests =3D l_strsplit(test_action_params, ','); - - if (!unit_tests || !unit_tests[0]) - goto exit; - } - - if (chdir(top_level_path) < 0) - goto exit; - - d =3D opendir("unit/"); - if (!d) - goto exit; - - while ((dirent =3D readdir(d)) !=3D NULL) { - struct stat st; - - if (dirent->d_type !=3D DT_REG) - continue; - - unit_test_abs_path =3D l_strdup_printf("%s%s%s", top_level_path, - "/unit/", dirent->d_name); - - if (stat(unit_test_abs_path, &st) < 0) - goto next; - - if (!(st.st_mode & S_IEXEC)) - goto next; - - if (unit_tests) { - if (!l_strv_contains(unit_tests, dirent->d_name)) - goto next; - } - - argv[0] =3D unit_test_abs_path; - argv[1] =3D NULL; - - l_info("\n---------- Unit %s ----------", dirent->d_name); - execute_program(argv, environ, true, NULL); - -next: - l_free(unit_test_abs_path); - } - - closedir(d); - -exit: - l_strfreev(unit_tests); -} - -static bool wiphy_match(const void *a, const void *b) -{ - const struct wiphy *wiphy =3D a; - int id =3D L_PTR_TO_INT(b); - - return (wiphy->id =3D=3D id); -} - -static struct wiphy *wiphy_find(int wiphy_id) -{ - return l_queue_find(wiphy_list, wiphy_match, L_INT_TO_PTR(wiphy_id)); -} - -static void parse_supported_iftypes(uint16_t *iftypes, - struct l_genl_attr *attr) -{ - uint16_t type, len; - const void *data; - - while (l_genl_attr_next(attr, &type, &len, &data)) { - /* - * NL80211_IFTYPE_UNSPECIFIED can be ignored, so we start - * at the first bit - */ - if (type > sizeof(uint16_t) * 8) { - l_warn("unsupported iftype: %u", type); - continue; - } - - *iftypes |=3D 1 << (type - 1); - } -} - -static void wiphy_dump_callback(struct l_genl_msg *msg, void *user_data) -{ - struct wiphy *wiphy; - struct l_genl_attr attr; - struct l_genl_attr nested; - uint32_t id =3D UINT32_MAX; - uint16_t type, len; - const void *data; - const char *name =3D NULL; - uint32_t name_len =3D 0; - uint16_t iftypes =3D 0; - - if (!l_genl_attr_init(&attr, msg)) - return; - - while (l_genl_attr_next(&attr, &type, &len, &data)) { - switch (type) { - case NL80211_ATTR_WIPHY: - if (len !=3D sizeof(uint32_t)) - return; - - id =3D *((uint32_t *) data); - - if (wiphy_find(id)) - return; - - break; - case NL80211_ATTR_WIPHY_NAME: - if (len > sizeof(((struct wiphy *) 0)->name)) - return; - - name =3D data; - name_len =3D len; - - break; - case NL80211_ATTR_SUPPORTED_IFTYPES: - if (l_genl_attr_recurse(&attr, &nested)) - parse_supported_iftypes(&iftypes, &nested); - - break; - } - } - - if (id =3D=3D UINT32_MAX || !name) - return; - - wiphy =3D l_new(struct wiphy, 1); - strncpy(wiphy->name, name, name_len); - wiphy->id =3D id; - wiphy->can_ap =3D iftypes & (1 << NL80211_IFTYPE_AP); - - l_queue_push_tail(wiphy_list, wiphy); -} - -static void iface_dump_callback(struct l_genl_msg *msg, void *user_data) -{ - struct l_genl_attr attr; - uint16_t type, len; - const void *data; - const char *ifname =3D NULL; - struct wiphy *wiphy =3D NULL; - - if (!l_genl_attr_init(&attr, msg)) - return; - - while (l_genl_attr_next(&attr, &type, &len, &data)) { - switch (type) { - - case NL80211_ATTR_IFNAME: - if (len > 16) { - l_warn("Invalid interface name attribute"); - return; - } - - ifname =3D data; - break; - - case NL80211_ATTR_WIPHY: - if (len !=3D sizeof(uint32_t)) { - l_warn("Invalid wiphy attribute"); - return; - } - - wiphy =3D wiphy_find(*((uint32_t *) data)); - break; - } - } - - if (!ifname || !wiphy) - return; - - wiphy->interface_name =3D l_strdup(ifname); - wiphy->interface_created =3D false; - - l_info("Discovered interface %s", wiphy->interface_name); -} - -struct nl_data { - struct l_genl *genl; - struct l_genl_family *nl80211; -}; - -static void iface_dump_done(void *user_data) -{ - struct nl_data *data =3D user_data; - - l_debug("Interface discovery complete, running tests"); - - list_interfaces(); - - run_auto_tests(); - - l_queue_destroy(wiphy_list, wiphy_free); - - l_genl_family_free(data->nl80211); - l_genl_unref(data->genl); - l_free(data); - - l_main_quit(); -} - -static void wiphy_dump_done(void *user_data) -{ - struct nl_data *data =3D user_data; - struct l_genl_msg *msg; - - l_debug("Wiphy discovery complete, discovering interfaces"); - - msg =3D l_genl_msg_new(NL80211_CMD_GET_INTERFACE); - if (!l_genl_family_dump(data->nl80211, msg, iface_dump_callback, - data, iface_dump_done)) - l_error("Getting all interface information failed"); -} - -static void nl80211_requested(const struct l_genl_family_info *info, - void *user_data) -{ - struct nl_data *data =3D user_data; - struct l_genl_msg *msg; - - if (info =3D=3D NULL) { - l_info("No nl80211 family found"); - goto done; - } - - l_debug("Found nl80211 interface"); - - data->nl80211 =3D l_genl_family_new(data->genl, NL80211_GENL_NAME); - wiphy_list =3D l_queue_new(); - - msg =3D l_genl_msg_new(NL80211_CMD_GET_WIPHY); - if (!l_genl_family_dump(data->nl80211, msg, wiphy_dump_callback, - data, wiphy_dump_done)) - l_error("Getting all wiphy devices failed"); - - return; -done: - l_main_quit(); -} - -static void start_hw_discovery(void) -{ - struct nl_data *data =3D l_new(struct nl_data, 1); - - data->genl =3D l_genl_new(); - l_genl_request_family(data->genl, NL80211_GENL_NAME, - nl80211_requested, data, NULL); - /* - * This is somewhat of a mystery, but it appears that - * calling lshw causes the OS to re-enumerate the USB - * bus. Without this no USB adapters are found when - * doing the wiphy/iface dump from nl80211. - * - * This also conveniently prints all the network - * adapters and their iface name, so its much easier - * to know which adapter are being used by iwd/hostapd - * after the test. - */ - if (system("lshw -C network")) - l_info("lshw failed"); - - l_main_run(); -} - -static void run_tests(void) -{ - char cmdline[CMDLINE_MAX], *ptr, *cmds; - char *test_action_str; - FILE *fp; - int i; - - fp =3D fopen("/proc/cmdline", "re"); - - if (!fp) { - l_error("Failed to open kernel command line"); - return; - } - - ptr =3D fgets(cmdline, sizeof(cmdline), fp); - fclose(fp); - - if (!ptr) { - l_error("Failed to read kernel command line"); - return; - } - - ptr =3D strstr(cmdline, "LOG_GID=3D"); - if (ptr) { - *ptr =3D '\0'; - test_action_str =3D ptr + 9; - ptr =3D strchr(test_action_str, '\''); - *ptr =3D '\0'; - log_gid =3D atoi(test_action_str); - } - - ptr =3D strstr(cmdline, "LOG_UID=3D"); - if (ptr) { - *ptr =3D '\0'; - test_action_str =3D ptr + 9; - ptr =3D strchr(test_action_str, '\''); - *ptr =3D '\0'; - log_uid =3D atoi(test_action_str); - } - - ptr =3D strstr(cmdline, "LOG_PATH=3D"); - if (ptr) { - *ptr =3D '\0'; - test_action_str =3D ptr + 10; - - ptr =3D strchr(test_action_str, '\''); - *ptr =3D '\0'; - - if (strcmp(test_action_str, "none")) { - log =3D true; - strcpy(log_dir, test_action_str); - } - } - - ptr =3D strstr(cmdline, "SHELL=3D"); - if (ptr) { - *ptr =3D '\0'; - test_action_str =3D ptr + 6; - - shell =3D atoi(test_action_str); - } - - ptr =3D strstr(cmdline, "HW=3D"); - if (ptr) { - *ptr =3D '\0'; - test_action_str =3D ptr + 4; - - ptr =3D strchr(test_action_str, '\''); - if (ptr) - *ptr =3D '\0'; - - if (!strcmp(test_action_str, "virtual")) - native_hw =3D false; - else - native_hw =3D true; - } - - ptr =3D strstr(cmdline, "GDB=3D"); - if (ptr) { - *ptr =3D '\0'; - test_action_str =3D ptr + 5; - - ptr =3D strchr(test_action_str, '\''); - *ptr =3D '\0'; - gdb_opt =3D l_strdup(test_action_str); - } - - ptr =3D strstr(cmdline, "VALGRIND=3D"); - if (ptr) { - char *end; - unsigned long v; - - *ptr =3D '\0'; - test_action_str =3D ptr + 9; - v =3D strtoul(test_action_str, &end, 10); - if ((v !=3D 0 && v !=3D 1) || end !=3D test_action_str + 1) { - l_error("malformed valgrind option"); - return; - } - - valgrind =3D (bool) v; - } - - ptr =3D strstr(cmdline, "PATH=3D"); - if (!ptr) { - l_error("No $PATH section found"); - return; - } - - *ptr =3D '\0'; - test_action_str =3D ptr + 6; - ptr =3D strchr(test_action_str, '\''); - *ptr =3D '\0'; - l_info("%s", test_action_str); - setenv("PATH", test_action_str, true); - - ptr =3D strstr(cmdline, "TESTARGS=3D"); - - if (!ptr) { - l_error("No test command section found"); - return; - } - - cmds =3D ptr + 10; - ptr =3D strchr(cmds, '\''); - - if (!ptr) { - l_error("Malformed test command section"); - return; - } - - *ptr =3D '\0'; - - ptr =3D strstr(cmdline, "TEST_ACTION_PARAMS=3D"); - - if (ptr) { - test_action_params =3D ptr + 20; - ptr =3D strchr(test_action_params, '\''); - - if (!ptr) { - l_error("Malformed test action parameters section"); - return; - } - - *ptr =3D '\0'; - } - - ptr =3D strstr(cmdline, "TEST_ACTION=3D"); - - if (ptr) { - test_action_str =3D ptr + 12; - ptr =3D strchr(test_action_str, ' '); - - if (!ptr) { - l_error("Malformed test action parameters section"); - return; - } - - *ptr =3D '\0'; - - test_action =3D (enum action) atoi(test_action_str); - } - - ptr =3D strstr(cmdline, "DEBUG_FILTER=3D"); - - if (ptr) { - debug_filter =3D ptr + 14; - - ptr =3D strchr(debug_filter, '\''); - - if (!ptr) { - l_error("Malformed debug filter section"); - return; - } - - *ptr =3D '\0'; - - if (debug_filter[0] !=3D '\0') { - enable_debug =3D true; - l_debug_enable(debug_filter); - setenv("HWSIM_DEBUG", "", true); - } - } - - ptr =3D strstr(cmdline, "TESTVERBOUT=3D"); - - if (ptr) { - verbose_opt =3D ptr + 13; - - ptr =3D strchr(verbose_opt, '\''); - if (!ptr) { - l_error("Malformed verbose parameter"); - return; - } - - *ptr =3D '\0'; - - l_info("Enable verbose output for %s", verbose_opt); - - verbose_apps =3D l_strsplit(verbose_opt, ','); - } - - ptr =3D strstr(cmdline, "TESTHOME=3D"); - - if (ptr) { - exec_home =3D ptr + 4; - ptr =3D strpbrk(exec_home + 9, " \r\n"); - - if (ptr) - *ptr =3D '\0'; - } - - ptr =3D strrchr(exec_home, '/'); - - if (!ptr) - exit(EXIT_FAILURE); - - i =3D ptr - exec_home; - - strncpy(top_level_path, exec_home + 5, i - 5); - top_level_path[i - 5] =3D '\0'; - - switch (test_action) { - case ACTION_AUTO_TEST: - if (native_hw) - start_hw_discovery(); - else - run_auto_tests(); - break; - case ACTION_UNIT_TEST: - run_unit_tests(); - break; - } -} - -static void usage(void) -{ - l_info("test-runner - Automated test execution utility\n" - "Usage:\n"); - l_info("\ttest-runner [options] [--] [args]\n"); - l_info("Options:\n" - "\t-q, --qemu QEMU binary\n" - "\t-k, --kernel Kernel image (bzImage)\n" - "\t-v, --verbose Comma separated list of " - "applications to enable\n" - "\t\t\t\tverbose output\n" - "\t-h, --help Show help options\n" - "\t-V, --valgrind Run valgrind on iwd. Note: \"-v" - " iwd\" is required\n" - "\t\t\t\tto see valgrind" - " output\n" - "\t-g, --gdb Run gdb on the specified" - " executable\n" - "\t-w, --hw Run using a physical hardware " - "configuration\n" - "\t-s, --shell Boot into shell. If -A is used the" - " environment\n" - "\t\t\t\twill be setup exactly as it is" - " in the test,\n" - "\t\t\t\tbut no test will be run. If no" - " test is specified\n" - "\t\t\t\tthe 'shell' test" - " will be used\n" - "\t-l, --log Directory used for log output. " - "This option sets \n" - "\t\t\t\t--verbose on all apps"); - l_info("Commands:\n" - "\t-A, --auto-tests Comma separated list of the " - "test configuration\n\t\t\t\t" - "directories to run\n" - "\t-U, --unit-tests Comma separated list of the " - "unit tests to run\n"); -} - -static const struct option main_options[] =3D { - { "auto-tests", required_argument, NULL, 'A' }, - { "unit-tests", optional_argument, NULL, 'U' }, - { "qemu", required_argument, NULL, 'q' }, - { "kernel", required_argument, NULL, 'k' }, - { "verbose", required_argument, NULL, 'v' }, - { "debug", optional_argument, NULL, 'd' }, - { "gdb", required_argument, NULL, 'g' }, - { "valgrind", no_argument, NULL, 'V' }, - { "hw", required_argument, NULL, 'w' }, - { "shell", optional_argument, NULL, 's' }, - { "log", required_argument, NULL, 'l' }, - { "help", no_argument, NULL, 'h' }, - { } -}; - -int main(int argc, char *argv[]) -{ - uint8_t actions =3D 0; - struct tm *timeinfo; - time_t t; - char *gid; - char *uid; - - l_log_set_stderr(); - - if (getpid() =3D=3D 1 && getppid() =3D=3D 0) { - if (!l_main_init()) - return EXIT_FAILURE; - - prepare_sandbox(); - - run_tests(); - - sync(); - l_info("Done running tests. Rebooting..."); - - reboot(RB_AUTOBOOT); - return EXIT_SUCCESS; - } - - for (;;) { - int opt; - - opt =3D getopt_long(argc, argv, "A:q:k:v:g:sl:UVdh", main_options, - NULL); - if (opt < 0) - break; - - switch (opt) { - case 'A': - test_action =3D ACTION_AUTO_TEST; - test_action_params =3D optarg; - actions++; - break; - case 'U': - test_action =3D ACTION_UNIT_TEST; - test_action_params =3D optarg; - actions++; - break; - case 'q': - qemu_binary =3D optarg; - break; - case 'k': - kernel_image =3D optarg; - break; - case 'd': - enable_debug =3D true; - - if (optarg) - debug_filter =3D optarg; - else - debug_filter =3D "*"; - - l_debug_enable(debug_filter); - break; - case 'v': - verbose_opt =3D optarg; - verbose_apps =3D l_strsplit(optarg, ','); - break; - case 'V': - valgrind =3D true; - break; - case 'g': - gdb_opt =3D optarg; - if (!gdb_opt || (strcmp(gdb_opt, "iwd") && - strcmp(gdb_opt, "hostapd") && - strcmp(gdb_opt, "hwsim"))) { - l_error("--gdb can only be used with iwd" - ", hwsim or hostapd"); - return EXIT_FAILURE; - } - break; - case 'w': - hw_config =3D l_settings_new(); - if (!l_settings_load_from_file(hw_config, optarg)) { - l_error("could not read hw config from %s", - optarg); - l_settings_free(hw_config); - return EXIT_FAILURE; - } - break; - case 's': - shell =3D true; - break; - case 'l': - /* - * Setup the log directory. This is created under the - * passed in log dir (--log) in the format: - * /run---- - * - * The created log dir is then chown'ed to the user - * who started test-runner, as are all files created - * under this directory. - */ - log =3D true; - - if (!optarg) - optarg =3D "."; - - time(&t); - timeinfo =3D localtime(&t); - - gid =3D getenv("SUDO_GID"); - uid =3D getenv("SUDO_UID"); - - if (!gid || !uid) { - log_gid =3D getgid(); - log_uid =3D getuid(); - } else { - log_gid =3D strtol(gid, NULL, 10); - log_uid =3D strtol(uid, NULL, 10); - } - - snprintf(log_dir, sizeof(log_dir), "%s/run-%d-%d-%d-%d", - optarg, timeinfo->tm_year + 1900, - timeinfo->tm_mon + 1, timeinfo->tm_mday, - getpid()); - mkdir(log_dir, 0755); - - if (chown(log_dir, log_uid, log_gid) < 0) - l_error("failed to fchown %s", log_dir); - - break; - case 'h': - usage(); - return EXIT_SUCCESS; - default: - return EXIT_FAILURE; - } - } - - if (argc - optind > 0) { - l_error("Invalid command line parameters"); - return EXIT_FAILURE; - } - - if (actions > 1) { - l_error("Only one action can be specified"); - return EXIT_FAILURE; - } - - if (!actions) - test_action =3D ACTION_AUTO_TEST; - - own_binary =3D argv[0]; - test_argv =3D argv + optind; - test_argc =3D argc - optind; - - if (!qemu_binary) { - qemu_binary =3D find_qemu(); - if (!qemu_binary) { - l_error("No default QEMU binary found"); - return EXIT_FAILURE; - } - } - - if (!kernel_image) { - kernel_image =3D find_kernel(); - if (!kernel_image) { - l_error("No default kernel image found"); - return EXIT_FAILURE; - } - } - - l_info("Using QEMU binary %s", qemu_binary); - l_info("Using kernel image %s", kernel_image); - - if (!start_qemu()) - return EXIT_FAILURE; - - return EXIT_SUCCESS; -} -- = 2.21.1 --===============3391692558395236834==--