[PATCH v2 01/14] test-runner: Move environment setup into own module
by James Prestwood
This (as well as subsequent commits) will separate test-runner into two
parts:
1. Environment setup
2. Running tests
Spurred by interest in adding UML/host support, test-runner was in need
of a refactor to separate out the environment setup and actually running
the tests.
The environment (currently only Qemu) requires quite a bit of special
handling (ctypes mounting/reboot, 9p mounts, tons of kernel options etc)
which nobody writing tests should need to see or care about. This has all
been moved into 'runner.py'.
Running the tests (inside test-runner) won't change much.
The new 'runner.py' module adds an abstraction class which allows different
Runner's to be implemented, and setup their own environment as they see
fit. This is in preparation for UML and Host runners.
---
tools/runner.py | 485 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 485 insertions(+)
create mode 100644 tools/runner.py
diff --git a/tools/runner.py b/tools/runner.py
new file mode 100644
index 00000000..743d74b1
--- /dev/null
+++ b/tools/runner.py
@@ -0,0 +1,485 @@
+#!/usr/bin/python3
+
+from argparse import ArgumentParser
+from argparse import Namespace
+from configparser import ConfigParser
+from collections import namedtuple
+from shutil import copy, copytree, which, rmtree
+from glob import glob
+
+import os
+import ctypes
+import fcntl
+import sys
+
+libc = ctypes.cdll['libc.so.6']
+libc.mount.argtypes = (ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, \
+ ctypes.c_ulong, ctypes.c_char_p)
+
+# Using ctypes to load the libc library is somewhat low level. Because of this
+# we need to define our own flags/options for use with mounting.
+MS_NOSUID = 2
+MS_NODEV = 4
+MS_NOEXEC = 8
+MS_STRICTATIME = 1 << 24
+STDIN_FILENO = 0
+TIOCSTTY = 0x540E
+
+MountInfo = namedtuple('MountInfo', 'fstype target options flags')
+DevInfo = namedtuple('DevInfo', 'target linkpath')
+
+mounts_common = [
+ MountInfo('sysfs', '/sys', '', MS_NOSUID|MS_NOEXEC|MS_NODEV),
+ MountInfo('proc', '/proc', '', MS_NOSUID|MS_NOEXEC|MS_NODEV),
+ MountInfo('devpts', '/dev/pts', 'mode=0620', MS_NOSUID|MS_NOEXEC),
+ MountInfo('tmpfs', '/dev/shm', 'mode=1777',
+ MS_NOSUID|MS_NODEV|MS_STRICTATIME),
+ MountInfo('tmpfs', '/run', 'mode=0755',
+ MS_NOSUID|MS_NODEV|MS_STRICTATIME),
+ MountInfo('tmpfs', '/tmp', '', 0),
+ MountInfo('tmpfs', '/usr/share/dbus-1', 'mode=0755',
+ MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME),
+]
+
+dev_table = [
+ DevInfo('/proc/self/fd', '/dev/fd'),
+ DevInfo('/proc/self/fd/0', '/dev/stdin'),
+ DevInfo('/proc/self/fd/1', '/dev/stdout'),
+ DevInfo('/proc/self/fd/2', '/dev/stderr')
+]
+
+def mount(source, target, fs, flags, options=''):
+ '''
+ Python wrapper for libc mount()
+ '''
+ ret = libc.mount(source.encode(), target.encode(), fs.encode(), flags,
+ options.encode())
+ if ret < 0:
+ errno = ctypes.get_errno()
+ raise Exception("Could not mount %s (%d)" % (target, errno))
+
+#
+# Custom argparse.Namespace class to stringify arguments in a way that can be
+# directly passed to the test environment as kernel arguments. This also removes
+# any None, False, or [] arguments.
+#
+class RunnerNamespace(Namespace):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def to_cmd(self):
+ ret = ''
+ for k, v in self.__dict__.items():
+ if v in [None, False, [], '']:
+ continue
+
+ ret += '%s=%s ' % (k, str(v))
+
+ return ret.strip()
+
+#
+# The core arguments needed both inside and outside the test environment
+#
+class RunnerCoreArgParse(ArgumentParser):
+ def __init__(self, *args, **kwargs):
+ super().__init__(self, *args, **kwargs)
+
+ self.add_argument('--start', '-s',
+ help='Custom init process in virtual environment',
+ dest='start',
+ default=None)
+ self.add_argument('--verbose', '-v', metavar='<list>',
+ type=str,
+ help='Comma separated list of applications',
+ dest='verbose',
+ default=[])
+ self.add_argument('--debug', '--dbg', '-d',
+ action='store_true',
+ help='Enable test-runner debugging',
+ dest='dbg')
+ self.add_argument('--log', '-l',
+ type=str,
+ help='Directory for log files')
+ self.add_argument('--monitor', '-m',
+ type=str,
+ help='Enables iwmon output to file')
+ self.add_argument('--sub-tests', '-S',
+ metavar='<subtests>',
+ type=str, nargs=1, help='List of subtests to run',
+ default=None, dest='sub_tests')
+ self.add_argument('--result', '-e', type=str,
+ help='Writes PASS/FAIL to results file')
+ self.add_argument('--hw', '-w',
+ type=str,
+ help='Use physical adapters for tests (passthrough)')
+ self.add_argument('--testhome')
+
+ # Prevent --autotest/--unittest from being used together
+ auto_unit_group = self.add_mutually_exclusive_group()
+ auto_unit_group.add_argument('--autotests', '-A',
+ metavar='<tests>',
+ type=str,
+ help='List of tests to run',
+ default=None,
+ dest='autotests')
+ auto_unit_group.add_argument('--unit-tests', '-U',
+ metavar='<tests>',
+ type=str,
+ nargs='?',
+ const='*',
+ help='List of unit tests to run',
+ dest='unit_tests')
+
+ # Prevent --valgrind/--gdb from being used together
+ valgrind_gdb_group = self.add_mutually_exclusive_group()
+ valgrind_gdb_group.add_argument('--gdb', '-g',
+ metavar='<exec>',
+ type=str,
+ nargs=1,
+ help='Run gdb on specified executable',
+ dest='gdb')
+ valgrind_gdb_group.add_argument('--valgrind', '-V',
+ action='store_true',
+ help='Run valgrind on IWD',
+ dest='valgrind')
+
+ # Overwrite to use a custom namespace class and parse from env
+ def parse_args(self, *args, namespace=RunnerNamespace()):
+ if len(sys.argv) > 1:
+ return super().parse_args(*args, namespace=namespace)
+
+ options = []
+ for k, v in os.environ.items():
+ options.append('--' + k)
+ options.append(v)
+
+ return self.parse_known_args(args=options, namespace=namespace)[0]
+
+#
+# Arguments only needed outside the test environment
+#
+class RunnerArgParse(RunnerCoreArgParse):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.add_argument('--runner', '-r',
+ metavar='<runner type>',
+ type=str,
+ help='Type of runner to use (qemu, uml, host)',
+ dest='runner',
+ default=None)
+ self.add_argument('--kernel', '-k',
+ metavar='<kernel>',
+ type=str,
+ help='Path to kernel/uml image',
+ dest='kernel',
+ default=None)
+
+#
+# Class to sort out what type of runner this is, returns the RunnerAbstract
+# implementation.
+#
+class Runner:
+ def __new__(self):
+ parser = RunnerArgParse(description='IWD Test Runner')
+
+ args = parser.parse_args()
+
+ # Common options
+ args.PATH = os.environ['PATH']
+
+ if 'testhome' not in args.to_cmd():
+ if os.getcwd().endswith('tools'):
+ args.testhome = '%s/../' % os.getcwd()
+ else:
+ args.testhome = os.getcwd()
+
+ if args.start is None:
+ if os.path.exists('run-tests'):
+ args.start = os.path.abspath('run-tests')
+ elif os.path.exists('tools/run-tests'):
+ args.start = os.path.abspath('tools/run-tests')
+ else:
+ raise Exception("Cannot locate run-tests binary")
+
+ # If no runner is specified but we have a kernel image assume
+ # if the kernel is executable its UML, otherwise qemu
+ if not args.runner:
+ if not args.kernel:
+ raise Exception("Please specify --runner/--kernel")
+
+ if os.access(args.kernel, os.X_OK):
+ args.runner = 'uml'
+ else:
+ args.runner = 'qemu'
+
+ if args.runner == 'qemu':
+ return QemuRunner(args)
+ else:
+ raise Exception("Unknown runner %s" % args.runner)
+
+class RunnerAbstract:
+ cmdline = []
+ env = None
+ name = "unnamed"
+
+ def __init__(self, args):
+ self.args = args
+
+ if self.args.log:
+ self.args.log = os.path.abspath(self.args.log)
+
+ def start(self):
+ print("Starting %s" % self.name)
+ os.execlpe(self.cmdline[0], *self.cmdline, self.env)
+
+ def prepare_environment(self):
+ path = os.environ['PATH']
+ os.environ['PATH'] = '%s/src' % self.args.testhome
+ os.environ['PATH'] += ':%s/tools' % self.args.testhome
+ os.environ['PATH'] += ':%s/client' % self.args.testhome
+ os.environ['PATH'] += ':%s/monitor' % self.args.testhome
+ os.environ['PATH'] += ':%s/wired' % self.args.testhome
+ os.environ['PATH'] += ':' + path
+
+ sys.path.append(self.args.testhome + '/autotests/util')
+
+ if not os.path.exists('/tmp/iwd'):
+ os.mkdir('/tmp/iwd')
+
+ #
+ # This prevents any print() calls in this script from printing unless
+ # --debug is passed. For an 'always print' option use dbg()
+ #
+ if not self.args.dbg:
+ sys.stdout = open(os.devnull, 'w')
+
+ # Copy autotests/misc/{certs,secrets,phonesim} so any test can refer to them
+ if os.path.exists('/tmp/certs'):
+ rmtree('/tmp/certs')
+
+ if os.path.exists('/tmp/secrets'):
+ rmtree('/tmp/secrets')
+
+ copytree(self.args.testhome + '/autotests/misc/certs', '/tmp/certs')
+ copytree(self.args.testhome + '/autotests/misc/secrets', '/tmp/secrets')
+ copy(self.args.testhome + '/autotests/misc/phonesim/phonesim.conf', '/tmp')
+
+ def cleanup_environment(self):
+ rmtree('/tmp/iwd')
+ rmtree('/tmp/certs')
+ rmtree('/tmp/secrets')
+ os.remove('/tmp/phonesim.conf')
+
+ os.sync()
+
+ # For QEMU/UML runners
+ def _prepare_mounts(self):
+ for entry in mounts_common:
+ try:
+ os.lstat(entry.target)
+ except:
+ os.mkdir(entry.target, 755)
+
+ mount(entry.fstype, entry.target, entry.fstype, entry.flags,
+ entry.options)
+
+ for entry in dev_table:
+ os.symlink(entry.target, entry.linkpath)
+
+ os.setsid()
+
+ def stop(self):
+ exit()
+
+class QemuRunner(RunnerAbstract):
+ name = "Qemu Runner"
+
+ def __init__(self, args):
+ def mount_options(id):
+ return 'mount_tag=%s,security_model=passthrough,id=%s' % (id, id)
+
+ usb_adapters = None
+ pci_adapters = None
+ gid = None
+ append_gid_uid = False
+
+ super().__init__(args)
+
+ if len(sys.argv) <= 1:
+ return
+
+ if not which('qemu-system-x86_64'):
+ raise Exception('Cannot locate qemu binary')
+
+ if not args.kernel or not os.path.exists(args.kernel):
+ raise Exception('Cannot locate kernel image %s' % args.kernel)
+
+ if args.hw:
+ hw_conf = ConfigParser()
+ hw_conf.read(args.hw)
+
+ if hw_conf.has_section('USBAdapters'):
+ # The actual key name of the adapter
+ # doesn't matter since all we need is the
+ # bus/address. This gets named by the kernel
+ # anyways once in the VM.
+ usb_adapters = [v for v in hw_conf['USBAdapters'].values()]
+
+ if hw_conf.has_section('PCIAdapters'):
+ pci_adapters = [v for v in hw_conf['PCIAdapters'].values()]
+
+ if os.environ.get('SUDO_GID', None):
+ uid = int(os.environ['SUDO_UID'])
+ gid = int(os.environ['SUDO_GID'])
+
+ if args.log:
+ if os.getuid() != 0:
+ print("--log can only be used as root user")
+ quit()
+
+ append_gid_uid = True
+
+ args.log = os.path.abspath(args.log)
+ if not os.path.exists(self.args.log):
+ os.mkdir(self.args.log)
+
+ if gid:
+ os.chown(self.args.log, uid, gid)
+
+ if args.monitor:
+ if os.getuid() != 0:
+ print("--monitor can only be used as root user")
+ quit()
+
+ append_gid_uid = True
+
+ args.monitor = os.path.abspath(args.monitor)
+ monitor_parent_dir = os.path.abspath(os.path.join(self.args.monitor,
+ os.pardir))
+
+ if args.result:
+ if os.getuid() != 0:
+ print("--result can only be used as root user")
+ quit()
+
+ append_gid_uid = True
+
+ args.result = os.path.abspath(args.result)
+ result_parent_dir = os.path.abspath(os.path.join(self.args.result,
+ os.pardir))
+
+ if append_gid_uid:
+ args.SUDO_UID = uid
+ args.SUDO_GID = gid
+
+ kern_log = "ignore_loglevel" if "kernel" in args.verbose else "quiet"
+
+ qemu_cmdline = [
+ 'qemu-system-x86_64',
+ '-machine', 'type=q35,accel=kvm:tcg',
+ '-nodefaults', '-no-user-config', '-monitor', 'none',
+ '-display', 'none', '-m', '256M', '-nographic', '-vga',
+ 'none', '-no-acpi', '-no-hpet',
+ '-no-reboot', '-fsdev',
+ 'local,id=fsdev-root,path=/,readonly=on,security_model=none,multidevs=remap',
+ '-device',
+ 'virtio-9p-pci,fsdev=fsdev-root,mount_tag=/dev/root',
+ '-chardev', 'stdio,id=chardev-serial0,signal=off',
+ '-device', 'pci-serial,chardev=chardev-serial0',
+ '-device', 'virtio-rng-pci',
+ '-kernel', args.kernel,
+ '-smp', '2',
+ '-append',
+ 'console=ttyS0,115200n8 earlyprintk=serial \
+ rootfstype=9p root=/dev/root \
+ rootflags=trans=virtio \
+ acpi=off pci=noacpi %s ro \
+ mac80211_hwsim.radios=0 init=%s %s' %
+ (kern_log, args.start, args.to_cmd()),
+ ]
+
+ # Add two ethernet devices for testing EAD
+ qemu_cmdline.extend([
+ '-net', 'nic,model=virtio',
+ '-net', 'nic,model=virtio',
+ '-net', 'user'
+ ])
+
+ if usb_adapters:
+ for bus, addr in [s.split(',') for s in usb_adapters]:
+ qemu_cmdline.extend(['-usb',
+ '-device',
+ 'usb-host,hostbus=%s,hostaddr=%s' % \
+ (bus, addr)])
+ if pci_adapters:
+ qemu_cmdline.extend(['-enable-kvm'])
+ for addr in pci_adapters:
+ qemu_cmdline.extend(['-device', 'vfio-pci,host=%s' % addr])
+
+ if args.log:
+ #
+ # Creates a virtfs device that can be mounted. This mount
+ # will point back to the provided log directory and is
+ # writable unlike the rest of the mounted file system.
+ #
+ qemu_cmdline.extend([
+ '-virtfs',
+ 'local,path=%s,%s' % (args.log, mount_options('logdir'))
+ ])
+
+ if args.monitor:
+ qemu_cmdline.extend([
+ '-virtfs',
+ 'local,path=%s,%s' % (monitor_parent_dir, mount_options('mondir'))
+ ])
+
+ if args.result:
+ qemu_cmdline.extend([
+ '-virtfs',
+ 'local,path=%s,%s' % (result_parent_dir, mount_options('resultdir'))
+ ])
+
+ self.cmdline = qemu_cmdline
+
+ def prepare_environment(self):
+ mounts_common.extend([
+ MountInfo('debugfs', '/sys/kernel/debug', '', 0)
+ ])
+
+ self._prepare_mounts()
+
+ super().prepare_environment()
+
+ fcntl.ioctl(STDIN_FILENO, TIOCSTTY, 1)
+
+ if self.args.log:
+ mount('logdir', self.args.log, '9p', 0,
+ 'trans=virtio,version=9p2000.L,msize=10240')
+ # Clear out any log files from other test runs
+ for f in glob('%s/*' % self.args.log):
+ print("removing %s" % f)
+
+ if os.path.isdir(f):
+ rmtree(f)
+ else:
+ os.remove(f)
+ elif self.args.monitor:
+ parent = os.path.abspath(os.path.join(self.args.monitor, os.pardir))
+ mount('mondir', parent, '9p', 0,
+ 'trans=virtio,version=9p2000.L,msize=10240')
+
+ if self.args.result:
+ parent = os.path.abspath(os.path.join(self.args.result, os.pardir))
+ mount('resultdir', parent, '9p', 0,
+ 'trans=virtio,version=9p2000.L,msize=10240')
+
+ def stop(self):
+ RB_AUTOBOOT = 0x01234567
+ #
+ # Calling 'reboot' or 'shutdown' from a shell (e.g. os.system('reboot'))
+ # is not the same the POSIX reboot() and will cause a kernel panic since
+ # we are the init process. The libc.reboot() allows the VM to exit
+ # gracefully.
+ #
+ libc.reboot(RB_AUTOBOOT)
--
2.34.1
2 months, 3 weeks
[PATCH 1/7] test-runner: Move environment setup into own module
by James Prestwood
This (as well as subsequent commits) will separate test-runner into two
parts:
1. Environment setup
2. Running tests
Spurred by interest in adding UML/host support, test-runner was in need
of a refactor to separate out the environment setup and actually running
the tests.
The environment (currently only Qemu) requires quite a bit of special
handling (ctypes mounting/reboot, 9p mounts, tons of kernel options etc)
which nobody writing tests should need to see or care about. This has all
been moved into 'runner.py'.
Running the tests (inside test-runner) won't change much.
The new 'runner.py' module adds an abstraction class which allows different
Runner's to be implemented, and setup their own environment as they see
fit. This is in preparation for UML and Host runners.
---
tools/runner.py | 485 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 485 insertions(+)
create mode 100644 tools/runner.py
diff --git a/tools/runner.py b/tools/runner.py
new file mode 100644
index 00000000..743d74b1
--- /dev/null
+++ b/tools/runner.py
@@ -0,0 +1,485 @@
+#!/usr/bin/python3
+
+from argparse import ArgumentParser
+from argparse import Namespace
+from configparser import ConfigParser
+from collections import namedtuple
+from shutil import copy, copytree, which, rmtree
+from glob import glob
+
+import os
+import ctypes
+import fcntl
+import sys
+
+libc = ctypes.cdll['libc.so.6']
+libc.mount.argtypes = (ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, \
+ ctypes.c_ulong, ctypes.c_char_p)
+
+# Using ctypes to load the libc library is somewhat low level. Because of this
+# we need to define our own flags/options for use with mounting.
+MS_NOSUID = 2
+MS_NODEV = 4
+MS_NOEXEC = 8
+MS_STRICTATIME = 1 << 24
+STDIN_FILENO = 0
+TIOCSTTY = 0x540E
+
+MountInfo = namedtuple('MountInfo', 'fstype target options flags')
+DevInfo = namedtuple('DevInfo', 'target linkpath')
+
+mounts_common = [
+ MountInfo('sysfs', '/sys', '', MS_NOSUID|MS_NOEXEC|MS_NODEV),
+ MountInfo('proc', '/proc', '', MS_NOSUID|MS_NOEXEC|MS_NODEV),
+ MountInfo('devpts', '/dev/pts', 'mode=0620', MS_NOSUID|MS_NOEXEC),
+ MountInfo('tmpfs', '/dev/shm', 'mode=1777',
+ MS_NOSUID|MS_NODEV|MS_STRICTATIME),
+ MountInfo('tmpfs', '/run', 'mode=0755',
+ MS_NOSUID|MS_NODEV|MS_STRICTATIME),
+ MountInfo('tmpfs', '/tmp', '', 0),
+ MountInfo('tmpfs', '/usr/share/dbus-1', 'mode=0755',
+ MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME),
+]
+
+dev_table = [
+ DevInfo('/proc/self/fd', '/dev/fd'),
+ DevInfo('/proc/self/fd/0', '/dev/stdin'),
+ DevInfo('/proc/self/fd/1', '/dev/stdout'),
+ DevInfo('/proc/self/fd/2', '/dev/stderr')
+]
+
+def mount(source, target, fs, flags, options=''):
+ '''
+ Python wrapper for libc mount()
+ '''
+ ret = libc.mount(source.encode(), target.encode(), fs.encode(), flags,
+ options.encode())
+ if ret < 0:
+ errno = ctypes.get_errno()
+ raise Exception("Could not mount %s (%d)" % (target, errno))
+
+#
+# Custom argparse.Namespace class to stringify arguments in a way that can be
+# directly passed to the test environment as kernel arguments. This also removes
+# any None, False, or [] arguments.
+#
+class RunnerNamespace(Namespace):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def to_cmd(self):
+ ret = ''
+ for k, v in self.__dict__.items():
+ if v in [None, False, [], '']:
+ continue
+
+ ret += '%s=%s ' % (k, str(v))
+
+ return ret.strip()
+
+#
+# The core arguments needed both inside and outside the test environment
+#
+class RunnerCoreArgParse(ArgumentParser):
+ def __init__(self, *args, **kwargs):
+ super().__init__(self, *args, **kwargs)
+
+ self.add_argument('--start', '-s',
+ help='Custom init process in virtual environment',
+ dest='start',
+ default=None)
+ self.add_argument('--verbose', '-v', metavar='<list>',
+ type=str,
+ help='Comma separated list of applications',
+ dest='verbose',
+ default=[])
+ self.add_argument('--debug', '--dbg', '-d',
+ action='store_true',
+ help='Enable test-runner debugging',
+ dest='dbg')
+ self.add_argument('--log', '-l',
+ type=str,
+ help='Directory for log files')
+ self.add_argument('--monitor', '-m',
+ type=str,
+ help='Enables iwmon output to file')
+ self.add_argument('--sub-tests', '-S',
+ metavar='<subtests>',
+ type=str, nargs=1, help='List of subtests to run',
+ default=None, dest='sub_tests')
+ self.add_argument('--result', '-e', type=str,
+ help='Writes PASS/FAIL to results file')
+ self.add_argument('--hw', '-w',
+ type=str,
+ help='Use physical adapters for tests (passthrough)')
+ self.add_argument('--testhome')
+
+ # Prevent --autotest/--unittest from being used together
+ auto_unit_group = self.add_mutually_exclusive_group()
+ auto_unit_group.add_argument('--autotests', '-A',
+ metavar='<tests>',
+ type=str,
+ help='List of tests to run',
+ default=None,
+ dest='autotests')
+ auto_unit_group.add_argument('--unit-tests', '-U',
+ metavar='<tests>',
+ type=str,
+ nargs='?',
+ const='*',
+ help='List of unit tests to run',
+ dest='unit_tests')
+
+ # Prevent --valgrind/--gdb from being used together
+ valgrind_gdb_group = self.add_mutually_exclusive_group()
+ valgrind_gdb_group.add_argument('--gdb', '-g',
+ metavar='<exec>',
+ type=str,
+ nargs=1,
+ help='Run gdb on specified executable',
+ dest='gdb')
+ valgrind_gdb_group.add_argument('--valgrind', '-V',
+ action='store_true',
+ help='Run valgrind on IWD',
+ dest='valgrind')
+
+ # Overwrite to use a custom namespace class and parse from env
+ def parse_args(self, *args, namespace=RunnerNamespace()):
+ if len(sys.argv) > 1:
+ return super().parse_args(*args, namespace=namespace)
+
+ options = []
+ for k, v in os.environ.items():
+ options.append('--' + k)
+ options.append(v)
+
+ return self.parse_known_args(args=options, namespace=namespace)[0]
+
+#
+# Arguments only needed outside the test environment
+#
+class RunnerArgParse(RunnerCoreArgParse):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.add_argument('--runner', '-r',
+ metavar='<runner type>',
+ type=str,
+ help='Type of runner to use (qemu, uml, host)',
+ dest='runner',
+ default=None)
+ self.add_argument('--kernel', '-k',
+ metavar='<kernel>',
+ type=str,
+ help='Path to kernel/uml image',
+ dest='kernel',
+ default=None)
+
+#
+# Class to sort out what type of runner this is, returns the RunnerAbstract
+# implementation.
+#
+class Runner:
+ def __new__(self):
+ parser = RunnerArgParse(description='IWD Test Runner')
+
+ args = parser.parse_args()
+
+ # Common options
+ args.PATH = os.environ['PATH']
+
+ if 'testhome' not in args.to_cmd():
+ if os.getcwd().endswith('tools'):
+ args.testhome = '%s/../' % os.getcwd()
+ else:
+ args.testhome = os.getcwd()
+
+ if args.start is None:
+ if os.path.exists('run-tests'):
+ args.start = os.path.abspath('run-tests')
+ elif os.path.exists('tools/run-tests'):
+ args.start = os.path.abspath('tools/run-tests')
+ else:
+ raise Exception("Cannot locate run-tests binary")
+
+ # If no runner is specified but we have a kernel image assume
+ # if the kernel is executable its UML, otherwise qemu
+ if not args.runner:
+ if not args.kernel:
+ raise Exception("Please specify --runner/--kernel")
+
+ if os.access(args.kernel, os.X_OK):
+ args.runner = 'uml'
+ else:
+ args.runner = 'qemu'
+
+ if args.runner == 'qemu':
+ return QemuRunner(args)
+ else:
+ raise Exception("Unknown runner %s" % args.runner)
+
+class RunnerAbstract:
+ cmdline = []
+ env = None
+ name = "unnamed"
+
+ def __init__(self, args):
+ self.args = args
+
+ if self.args.log:
+ self.args.log = os.path.abspath(self.args.log)
+
+ def start(self):
+ print("Starting %s" % self.name)
+ os.execlpe(self.cmdline[0], *self.cmdline, self.env)
+
+ def prepare_environment(self):
+ path = os.environ['PATH']
+ os.environ['PATH'] = '%s/src' % self.args.testhome
+ os.environ['PATH'] += ':%s/tools' % self.args.testhome
+ os.environ['PATH'] += ':%s/client' % self.args.testhome
+ os.environ['PATH'] += ':%s/monitor' % self.args.testhome
+ os.environ['PATH'] += ':%s/wired' % self.args.testhome
+ os.environ['PATH'] += ':' + path
+
+ sys.path.append(self.args.testhome + '/autotests/util')
+
+ if not os.path.exists('/tmp/iwd'):
+ os.mkdir('/tmp/iwd')
+
+ #
+ # This prevents any print() calls in this script from printing unless
+ # --debug is passed. For an 'always print' option use dbg()
+ #
+ if not self.args.dbg:
+ sys.stdout = open(os.devnull, 'w')
+
+ # Copy autotests/misc/{certs,secrets,phonesim} so any test can refer to them
+ if os.path.exists('/tmp/certs'):
+ rmtree('/tmp/certs')
+
+ if os.path.exists('/tmp/secrets'):
+ rmtree('/tmp/secrets')
+
+ copytree(self.args.testhome + '/autotests/misc/certs', '/tmp/certs')
+ copytree(self.args.testhome + '/autotests/misc/secrets', '/tmp/secrets')
+ copy(self.args.testhome + '/autotests/misc/phonesim/phonesim.conf', '/tmp')
+
+ def cleanup_environment(self):
+ rmtree('/tmp/iwd')
+ rmtree('/tmp/certs')
+ rmtree('/tmp/secrets')
+ os.remove('/tmp/phonesim.conf')
+
+ os.sync()
+
+ # For QEMU/UML runners
+ def _prepare_mounts(self):
+ for entry in mounts_common:
+ try:
+ os.lstat(entry.target)
+ except:
+ os.mkdir(entry.target, 755)
+
+ mount(entry.fstype, entry.target, entry.fstype, entry.flags,
+ entry.options)
+
+ for entry in dev_table:
+ os.symlink(entry.target, entry.linkpath)
+
+ os.setsid()
+
+ def stop(self):
+ exit()
+
+class QemuRunner(RunnerAbstract):
+ name = "Qemu Runner"
+
+ def __init__(self, args):
+ def mount_options(id):
+ return 'mount_tag=%s,security_model=passthrough,id=%s' % (id, id)
+
+ usb_adapters = None
+ pci_adapters = None
+ gid = None
+ append_gid_uid = False
+
+ super().__init__(args)
+
+ if len(sys.argv) <= 1:
+ return
+
+ if not which('qemu-system-x86_64'):
+ raise Exception('Cannot locate qemu binary')
+
+ if not args.kernel or not os.path.exists(args.kernel):
+ raise Exception('Cannot locate kernel image %s' % args.kernel)
+
+ if args.hw:
+ hw_conf = ConfigParser()
+ hw_conf.read(args.hw)
+
+ if hw_conf.has_section('USBAdapters'):
+ # The actual key name of the adapter
+ # doesn't matter since all we need is the
+ # bus/address. This gets named by the kernel
+ # anyways once in the VM.
+ usb_adapters = [v for v in hw_conf['USBAdapters'].values()]
+
+ if hw_conf.has_section('PCIAdapters'):
+ pci_adapters = [v for v in hw_conf['PCIAdapters'].values()]
+
+ if os.environ.get('SUDO_GID', None):
+ uid = int(os.environ['SUDO_UID'])
+ gid = int(os.environ['SUDO_GID'])
+
+ if args.log:
+ if os.getuid() != 0:
+ print("--log can only be used as root user")
+ quit()
+
+ append_gid_uid = True
+
+ args.log = os.path.abspath(args.log)
+ if not os.path.exists(self.args.log):
+ os.mkdir(self.args.log)
+
+ if gid:
+ os.chown(self.args.log, uid, gid)
+
+ if args.monitor:
+ if os.getuid() != 0:
+ print("--monitor can only be used as root user")
+ quit()
+
+ append_gid_uid = True
+
+ args.monitor = os.path.abspath(args.monitor)
+ monitor_parent_dir = os.path.abspath(os.path.join(self.args.monitor,
+ os.pardir))
+
+ if args.result:
+ if os.getuid() != 0:
+ print("--result can only be used as root user")
+ quit()
+
+ append_gid_uid = True
+
+ args.result = os.path.abspath(args.result)
+ result_parent_dir = os.path.abspath(os.path.join(self.args.result,
+ os.pardir))
+
+ if append_gid_uid:
+ args.SUDO_UID = uid
+ args.SUDO_GID = gid
+
+ kern_log = "ignore_loglevel" if "kernel" in args.verbose else "quiet"
+
+ qemu_cmdline = [
+ 'qemu-system-x86_64',
+ '-machine', 'type=q35,accel=kvm:tcg',
+ '-nodefaults', '-no-user-config', '-monitor', 'none',
+ '-display', 'none', '-m', '256M', '-nographic', '-vga',
+ 'none', '-no-acpi', '-no-hpet',
+ '-no-reboot', '-fsdev',
+ 'local,id=fsdev-root,path=/,readonly=on,security_model=none,multidevs=remap',
+ '-device',
+ 'virtio-9p-pci,fsdev=fsdev-root,mount_tag=/dev/root',
+ '-chardev', 'stdio,id=chardev-serial0,signal=off',
+ '-device', 'pci-serial,chardev=chardev-serial0',
+ '-device', 'virtio-rng-pci',
+ '-kernel', args.kernel,
+ '-smp', '2',
+ '-append',
+ 'console=ttyS0,115200n8 earlyprintk=serial \
+ rootfstype=9p root=/dev/root \
+ rootflags=trans=virtio \
+ acpi=off pci=noacpi %s ro \
+ mac80211_hwsim.radios=0 init=%s %s' %
+ (kern_log, args.start, args.to_cmd()),
+ ]
+
+ # Add two ethernet devices for testing EAD
+ qemu_cmdline.extend([
+ '-net', 'nic,model=virtio',
+ '-net', 'nic,model=virtio',
+ '-net', 'user'
+ ])
+
+ if usb_adapters:
+ for bus, addr in [s.split(',') for s in usb_adapters]:
+ qemu_cmdline.extend(['-usb',
+ '-device',
+ 'usb-host,hostbus=%s,hostaddr=%s' % \
+ (bus, addr)])
+ if pci_adapters:
+ qemu_cmdline.extend(['-enable-kvm'])
+ for addr in pci_adapters:
+ qemu_cmdline.extend(['-device', 'vfio-pci,host=%s' % addr])
+
+ if args.log:
+ #
+ # Creates a virtfs device that can be mounted. This mount
+ # will point back to the provided log directory and is
+ # writable unlike the rest of the mounted file system.
+ #
+ qemu_cmdline.extend([
+ '-virtfs',
+ 'local,path=%s,%s' % (args.log, mount_options('logdir'))
+ ])
+
+ if args.monitor:
+ qemu_cmdline.extend([
+ '-virtfs',
+ 'local,path=%s,%s' % (monitor_parent_dir, mount_options('mondir'))
+ ])
+
+ if args.result:
+ qemu_cmdline.extend([
+ '-virtfs',
+ 'local,path=%s,%s' % (result_parent_dir, mount_options('resultdir'))
+ ])
+
+ self.cmdline = qemu_cmdline
+
+ def prepare_environment(self):
+ mounts_common.extend([
+ MountInfo('debugfs', '/sys/kernel/debug', '', 0)
+ ])
+
+ self._prepare_mounts()
+
+ super().prepare_environment()
+
+ fcntl.ioctl(STDIN_FILENO, TIOCSTTY, 1)
+
+ if self.args.log:
+ mount('logdir', self.args.log, '9p', 0,
+ 'trans=virtio,version=9p2000.L,msize=10240')
+ # Clear out any log files from other test runs
+ for f in glob('%s/*' % self.args.log):
+ print("removing %s" % f)
+
+ if os.path.isdir(f):
+ rmtree(f)
+ else:
+ os.remove(f)
+ elif self.args.monitor:
+ parent = os.path.abspath(os.path.join(self.args.monitor, os.pardir))
+ mount('mondir', parent, '9p', 0,
+ 'trans=virtio,version=9p2000.L,msize=10240')
+
+ if self.args.result:
+ parent = os.path.abspath(os.path.join(self.args.result, os.pardir))
+ mount('resultdir', parent, '9p', 0,
+ 'trans=virtio,version=9p2000.L,msize=10240')
+
+ def stop(self):
+ RB_AUTOBOOT = 0x01234567
+ #
+ # Calling 'reboot' or 'shutdown' from a shell (e.g. os.system('reboot'))
+ # is not the same the POSIX reboot() and will cause a kernel panic since
+ # we are the init process. The libc.reboot() allows the VM to exit
+ # gracefully.
+ #
+ libc.reboot(RB_AUTOBOOT)
--
2.34.1
2 months, 3 weeks
[PATCH] auto-t: use full_scan=True on testBSSBlacklist
by James Prestwood
This test was missed during the test-wide change.
---
autotests/testBSSBlacklist/connection_test.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/autotests/testBSSBlacklist/connection_test.py b/autotests/testBSSBlacklist/connection_test.py
index 1bc9e604..8e239f44 100644
--- a/autotests/testBSSBlacklist/connection_test.py
+++ b/autotests/testBSSBlacklist/connection_test.py
@@ -101,7 +101,7 @@ class Test(unittest.TestCase):
devices = wd.list_devices(1)
device = devices[0]
- ordered_network = device.get_ordered_network("TestBlacklist")
+ ordered_network = device.get_ordered_network("TestBlacklist", full_scan=True)
self.assertEqual(ordered_network.type, NetworkType.psk)
@@ -148,7 +148,7 @@ class Test(unittest.TestCase):
devices = wd.list_devices(1)
device = devices[0]
- ordered_network = device.get_ordered_network("TestBlacklist")
+ ordered_network = device.get_ordered_network("TestBlacklist", full_scan=True)
self.assertEqual(ordered_network.type, NetworkType.psk)
@@ -188,7 +188,7 @@ class Test(unittest.TestCase):
devices[1].disconnect()
- ordered_network = device.get_ordered_network("TestBlacklist")
+ ordered_network = device.get_ordered_network("TestBlacklist", full_scan=True)
self.assertEqual(ordered_network.type, NetworkType.psk)
--
2.34.1
2 months, 3 weeks
[PATCH 1/7] tools: add some required options to kernel config
by James Prestwood
It looks like some architectures defconfig were adding these in
automatically, but not others. Explicitly add these to make sure
the kernel is built correctly.
---
tools/test_runner_kernel_config | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/tools/test_runner_kernel_config b/tools/test_runner_kernel_config
index 64674683..c3e65d34 100755
--- a/tools/test_runner_kernel_config
+++ b/tools/test_runner_kernel_config
@@ -1,8 +1,12 @@
#! /bin/sh
+scripts/config --enable CONFIG_CFG80211
+scripts/config --enable CONFIG_MAC80211
scripts/config --enable CONFIG_MAC80211_HWSIM
scripts/config --enable CONFIG_NLMON
scripts/config --enable CONFIG_VLAN_8021Q
+scripts/config --enable CONFIG_RFKILL
+scripts/config --enable CONFIG_IPV6
scripts/config --enable CONFIG_EXPERT
scripts/config --enable CONFIG_CFG80211_CERTIFICATION_ONUS
@@ -16,14 +20,21 @@ scripts/config --enable CONFIG_CRYPTO_RSA
scripts/config --enable CONFIG_CRYPTO_AES_X86_64
scripts/config --enable CONFIG_CRYPTO_AES_NI_INTEL
scripts/config --enable CONFIG_CRYPTO_MD4
+scripts/config --enable CONFIG_CRYPTO_MD5
+scripts/config --enable CONFIG_CRYPTO_SHA1
+scripts/config --enable CONFIG_CRYPTO_SHA256
+scripts/config --enable CONFIG_CRYPTO_SHA512
scripts/config --enable CONFIG_CRYPTO_SHA1_SSSE3
scripts/config --enable CONFIG_CRYPTO_SHA256_SSSE3
scripts/config --enable CONFIG_CRYPTO_SHA512_SSSE3
scripts/config --enable CONFIG_CRYPTO_ECB
+scripts/config --enable CONFIG_CRYPTO_CBC
scripts/config --enable CONFIG_CRYPTO_CMAC
scripts/config --enable CONFIG_CRYPTO_DES
scripts/config --enable CONFIG_CRYPTO_DES3_EDE_X86_64
scripts/config --enable CONFIG_CRYPTO_ARC4
+scripts/config --enable CONFIG_CRYPTO_HMAC
+scripts/config --enable CONFIG_KEYS
scripts/config --enable CONFIG_KEY_DH_OPERATIONS
scripts/config --enable CONFIG_ASYMMETRIC_KEY_TYPE
scripts/config --enable CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE
--
2.34.1
2 months, 3 weeks
[PATCH 1/4] p2p: Fix Device Address updates from Probe Requests
by Andrew Zaborowski
p2p_peer_update_existing may be called with a scan_bss struct built from
a Probe Request frame so it can't access bss->p2p_probe_resp_info even
if peer->bss was built from a Probe Response. Check the source frame
type of the scan_bss struct before updating the Device Address.
This fixes one timing issue that would make the autotest fail often.
---
src/p2p.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/p2p.c b/src/p2p.c
index 4c90efb6..ebd0f62b 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -3655,11 +3655,15 @@ static bool p2p_peer_update_existing(struct scan_bss *bss,
* Some property changes may need to be notified here.
*/
- if (peer->device_addr == peer->bss->addr)
- peer->device_addr = bss->addr;
- else
+ if (bss->source_frame == SCAN_BSS_PROBE_RESP)
peer->device_addr =
bss->p2p_probe_resp_info->device_info.device_addr;
+ else if (bss->source_frame == SCAN_BSS_PROBE_REQ && !l_memeqzero(
+ bss->p2p_probe_req_info->device_info.device_addr, 6))
+ peer->device_addr =
+ bss->p2p_probe_req_info->device_info.device_addr;
+ else
+ peer->device_addr = bss->addr;
scan_bss_free(peer->bss);
peer->bss = bss;
--
2.32.0
2 months, 3 weeks
[PATCH] eapol: zero entire buffer when creating frame
by James Prestwood
Since l_malloc is used the frame contents are not zero'ed automatically
which could result in random bytes being present in the frame which were
expected to be zero. This poses a problem when calculating the MIC as the
crypto operations are done on the entire frame with the expectation of
the MIC being zero.
---
src/eapol.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/eapol.c b/src/eapol.c
index 9f397d1d..e2c99991 100644
--- a/src/eapol.c
+++ b/src/eapol.c
@@ -706,7 +706,7 @@ static struct eapol_key *eapol_create_common(
struct eapol_key *out_frame = l_malloc(to_alloc + extra_len +
extra_key_len);
- memset(out_frame, 0, to_alloc + extra_len);
+ memset(out_frame, 0, to_alloc + extra_len + extra_key_len);
out_frame->header.protocol_version = protocol;
out_frame->header.packet_type = 0x3;
--
2.34.1
2 months, 4 weeks
[PATCH] src/storage.c: fix build with uclibc
by Fabrice Fontaine
explicit_bzero is used in src/storage.c since commit
01cd8587606bf2da1af245163150589834126c1c but src/missing.h is not
included, as a result build with uclibc fails on:
/home/buildroot/autobuild/instance-0/output-1/host/lib/gcc/powerpc-buildroot-linux-uclibc/10.3.0/../../../../powerpc-buildroot-linux-uclibc/bin/ld: src/storage.o: in function `storage_init':
storage.c:(.text+0x13a4): undefined reference to `explicit_bzero'
Fixes:
- http://autobuild.buildroot.org/results/2aff8d3d7c33c95e2c57f7c8a71e69939f...
Signed-off-by: Fabrice Fontaine <fontaine.fabrice(a)gmail.com>
---
src/storage.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/storage.c b/src/storage.c
index 82a72443..aa8066b1 100644
--- a/src/storage.c
+++ b/src/storage.c
@@ -44,6 +44,7 @@
#include <ell/ell.h>
#include "ell/useful.h"
+#include "src/missing.h"
#include "src/common.h"
#include "src/storage.h"
#include "src/crypto.h"
--
2.35.1
2 months, 4 weeks