Diff between 29eda6468530b7555a573402a629e29993c27d9f and e622e623de136222356272ef89d9d620990b546f

Changed Files

File Additions Deletions Status
.gitignore +1 -0 modified
Makefile.tools +2 -1 modified
tools/test-runner.c +431 -0 added

Full Patch

diff --git a/.gitignore b/.gitignore
index 45f84ff..7767062 100644
--- a/.gitignore
+++ b/.gitignore
@@ -81,6 +81,7 @@ tools/obexctl
 tools/gatt-service
 tools/btgatt-client
 tools/btgatt-server
+tools/test-runner
 tools/mcaptest
 tools/bneptest
 test/sap_client.pyc
diff --git a/Makefile.tools b/Makefile.tools
index 833f67a..8faf2f4 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -225,7 +225,8 @@ noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \
 			tools/btsnoop tools/btproxy \
 			tools/btiotest tools/bneptest tools/mcaptest \
 			tools/cltest tools/oobtest tools/seq2bseq \
-			tools/ibeacon tools/btgatt-client tools/btgatt-server
+			tools/ibeacon tools/btgatt-client tools/btgatt-server \
+			tools/test-runner
 
 tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
 tools_bdaddr_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
diff --git a/tools/test-runner.c b/tools/test-runner.c
new file mode 100644
index 0000000..3687b0d
--- /dev/null
+++ b/tools/test-runner.c
@@ -0,0 +1,431 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif
+
+#define CMDLINE_MAX 2048
+
+static const char *own_binary;
+static char **test_argv;
+static int test_argc;
+
+static const char *qemu_binary = NULL;
+static const char *kernel_image = NULL;
+
+static const char *qemu_table[] = {
+	"/usr/bin/qemu-system-x86_64",
+	"/usr/bin/qemu-system-i386",
+	NULL
+};
+
+static const char *find_qemu(void)
+{
+	int i;
+
+	for (i = 0; qemu_table[i]; i++) {
+		struct stat st;
+
+		if (!stat(qemu_table[i], &st))
+			return qemu_table[i];
+	}
+
+	return NULL;
+}
+
+static const char *kernel_table[] = {
+	"bzImage",
+	"arch/x86/boot/bzImage",
+	NULL
+};
+
+static const char *find_kernel(void)
+{
+	int i;
+
+	for (i = 0; kernel_table[i]; i++) {
+		struct stat st;
+
+		if (!stat(kernel_table[i], &st))
+			return kernel_table[i];
+	}
+
+	return NULL;
+}
+
+static const struct {
+	const char *target;
+	const char *linkpath;
+} dev_table[] = {
+	{ "/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[] = {
+	{ "sysfs",    "/sys",     NULL,        MS_NOSUID|MS_NOEXEC|MS_NODEV },
+	{ "proc",     "/proc",    NULL,        MS_NOSUID|MS_NOEXEC|MS_NODEV },
+	{ "devtmpfs", "/dev",     "mode=755",  MS_NOSUID|MS_STRICTATIME },
+	{ "devpts",   "/dev/pts", "mode=620",  MS_NOSUID|MS_NOEXEC },
+	{ "tmpfs",    "/dev/shm", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME },
+	{ "tmpfs",    "/run",     "mode=755",  MS_NOSUID|MS_NODEV|MS_STRICTATIME },
+	{ }
+};
+
+static void prepare_sandbox(void)
+{
+	int i;
+
+	for (i = 0; mount_table[i].fstype; i++) {
+		struct stat st;
+
+		if (lstat(mount_table[i].target, &st) < 0) {
+			printf("Creating %s\n", mount_table[i].target);
+			mkdir(mount_table[i].target, 0755);
+		}
+
+		printf("Mounting %s to %s\n", mount_table[i].fstype,
+						mount_table[i].target);
+
+		if (mount(mount_table[i].fstype,
+				mount_table[i].target,
+				mount_table[i].fstype,
+				mount_table[i].flags, NULL) < 0)
+			perror("Failed to mount filesystem");
+	}
+
+	for (i = 0; dev_table[i].target; i++) {
+		printf("Linking %s to %s\n", dev_table[i].linkpath,
+						dev_table[i].target);
+
+		if (symlink(dev_table[i].target, dev_table[i].linkpath) < 0)
+			perror("Failed to create device symlink");
+	}
+
+	printf("Creating new session group leader\n");
+	setsid();
+
+	printf("Setting controlling terminal\n");
+	ioctl(STDIN_FILENO, TIOCSCTTY, 1);
+}
+
+static char *const qemu_argv[] = {
+	"",
+	"-nodefaults",
+	"-nodefconfig",
+	"-no-user-config",
+	"-monitor", "none",
+	"-display", "none",
+	"-machine", "type=q35,accel=kvm",
+	"-smbios", "type=0,uefi=on",
+	"-m", "192M",
+	"-nographic",
+	"-vga", "none",
+	"-net", "none",
+	"-balloon", "none",
+	"-no-acpi",
+	"-no-hpet",
+	"-no-reboot",
+	"-fsdev", "local,id=fsdev-root,path=/,readonly,security_model=none",
+	"-device", "virtio-9p-pci,fsdev=fsdev-root,mount_tag=/dev/root",
+	"-chardev", "stdio,id=chardev-serial0",
+	"-device", "pci-serial,chardev=chardev-serial0",
+	"-kernel", "",
+	"-append", "",
+	NULL
+};
+
+static char *const qemu_envp[] = {
+	NULL
+};
+
+static void start_qemu(void)
+{
+	char cwd[PATH_MAX], initcmd[PATH_MAX], testargs[PATH_MAX];
+	char cmdline[CMDLINE_MAX];
+	char **argv;
+	int i, pos;
+
+	getcwd(cwd, sizeof(cwd));
+
+	if (own_binary[0] == '/')
+		snprintf(initcmd, sizeof(initcmd), "%s", own_binary);
+	else
+		snprintf(initcmd, sizeof(initcmd), "%s/%s", cwd, own_binary);
+
+	if (test_argv[0][0] == '/')
+		pos = snprintf(testargs, sizeof(testargs), "%s", test_argv[0]);
+	else
+		pos = snprintf(testargs, sizeof(testargs), "%s/%s",
+							cwd, test_argv[0]);
+
+	for (i = 1; i < test_argc; i++) {
+		int len = sizeof(testargs) - pos;
+		pos += snprintf(testargs + pos, len, " %s", test_argv[i]);
+	}
+
+	snprintf(cmdline, sizeof(cmdline),
+				"console=ttyS0,115200n8 earlyprintk=serial "
+				"rootfstype=9p "
+				"rootflags=trans=virtio,version=9p2000.L "
+				"acpi=off pci=noacpi noapic quiet ro init=%s "
+				"TESTHOME=%s TESTARGS=\'%s\'",
+						initcmd, cwd, testargs);
+
+	argv = alloca(sizeof(qemu_argv));
+	memcpy(argv, qemu_argv, sizeof(qemu_argv));
+
+	argv[0] = (char *) qemu_binary;
+
+	for (i = 1; argv[i]; i++) {
+		if (!strcmp(argv[i], "-kernel"))
+			argv[i + 1] = (char *) kernel_image;
+		else if (!strcmp(argv[i], "-append"))
+			argv[i + 1] = (char *) cmdline;
+	}
+
+	execve(argv[0], argv, qemu_envp);
+}
+
+static void run_command(char *cmdname, char *home)
+{
+	char *argv[9], *envp[3];
+	int pos = 0;
+	pid_t pid;
+
+	while (1) {
+		char *ptr;
+
+		ptr = strchr(cmdname, ' ');
+		if (!ptr) {
+			argv[pos++] = cmdname;
+			break;
+		}
+
+		*ptr = '\0';
+		argv[pos++] = cmdname;
+		if (pos > 8)
+			break;
+
+		cmdname = ptr + 1;
+	}
+
+	argv[pos] = NULL;
+
+	pos = 0;
+	envp[pos++] = "TERM=linux";
+	if (home)
+		envp[pos++] = home;
+	envp[pos] = NULL;
+
+	printf("Running command %s\n", argv[0]);
+
+	pid = fork();
+	if (pid < 0) {
+		perror("Failed to fork new process");
+		return;
+	}
+
+	if (pid == 0) {
+		execve(argv[0], argv, envp);
+		exit(EXIT_SUCCESS);
+	}
+
+	printf("New process %d created\n", pid);
+
+	while (1)  {
+		pid_t corpse;
+		int status;
+
+		corpse = waitpid(WAIT_ANY, &status, 0);
+		if (corpse < 0 || corpse == 0)
+			continue;
+
+		printf("Process %d terminated with status=%d\n",
+							corpse, status);
+
+		if (corpse == pid)
+			break;
+	}
+}
+
+static void run_tests(void)
+{
+	char cmdline[CMDLINE_MAX], *ptr, *cmds, *home = NULL;
+	FILE *fp;
+
+	fp = fopen("/proc/cmdline", "re");
+	if (!fp) {
+		fprintf(stderr, "Failed to open kernel command line\n");
+		return;
+	}
+
+	ptr = fgets(cmdline, sizeof(cmdline), fp);
+	fclose(fp);
+
+	if (!ptr) {
+		fprintf(stderr, "Failed to read kernel command line\n");
+		return;
+	}
+
+	ptr = strstr(cmdline, "TESTARGS=");
+	if (!ptr) {
+		fprintf(stderr, "No test command section found\n");
+		return;
+	}
+
+	cmds = ptr + 10;
+	ptr = strchr(cmds, '\'');
+	if (!ptr) {
+		fprintf(stderr, "Malformed test command section\n");
+		return;
+	}
+
+	*ptr = '\0';
+
+	ptr = strstr(cmdline, "TESTHOME=");
+	if (ptr) {
+		home = ptr + 4;
+		ptr = strpbrk(home + 9, " \r\n");
+		if (ptr)
+			*ptr = '\0';
+	}
+
+	run_command(cmds, home);
+}
+
+static void usage(void)
+{
+	printf("test-runner - Automated test execution utility\n"
+		"Usage:\n");
+	printf("\ttest-runner [options] [--] <command> [args]\n");
+	printf("Options:\n"
+		"\t-q, --qemu             QEMU binary\n"
+		"\t-k, --kernel           Kernel image (bzImage)\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "qemu",    required_argument, NULL, 'q' },
+	{ "kernel",  required_argument, NULL, 'k' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	if (getpid() == 1 && getppid() == 0) {
+		prepare_sandbox();
+		run_tests();
+
+		sync();
+		reboot(RB_AUTOBOOT);
+		return EXIT_SUCCESS;
+	}
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "q:k:vh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'q':
+			qemu_binary = optarg;
+			break;
+		case 'k':
+			kernel_image = optarg;
+			break;
+		case 'v':
+			printf("%s\n", VERSION);
+			return EXIT_SUCCESS;
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		default:
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (argc - optind < 1) {
+		fprintf(stderr, "Failed to specify test command\n");
+		return EXIT_FAILURE;
+	}
+
+	own_binary = argv[0];
+	test_argv = argv + optind;
+	test_argc = argc - optind;
+
+	if (!qemu_binary) {
+		qemu_binary = find_qemu();
+		if (!qemu_binary) {
+			fprintf(stderr, "No default QEMU binary found\n");
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (!kernel_image) {
+		kernel_image = find_kernel();
+		if (!kernel_image) {
+			fprintf(stderr, "No default kernel image found\n");
+			return EXIT_FAILURE;
+		}
+	}
+
+	printf("Using QEMU binary %s\n", qemu_binary);
+	printf("Using kernel image %s\n", kernel_image);
+
+	start_qemu();
+
+	return EXIT_SUCCESS;
+}