3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
40 #include <sys/types.h>
41 #include <sys/mount.h>
42 #include <sys/param.h>
43 #include <sys/reboot.h>
45 #include "lib/bluetooth.h"
47 #include "lib/hci_lib.h"
48 #include "tools/hciattach.h"
54 #define CMDLINE_MAX 2048
56 static const char *own_binary;
57 static char **test_argv;
60 static bool run_auto = false;
61 static bool start_dbus = false;
62 static int num_devs = 0;
63 static const char *qemu_binary = NULL;
64 static const char *kernel_image = NULL;
66 static const char *qemu_table[] = {
69 "/usr/bin/qemu-system-x86_64",
70 "/usr/bin/qemu-system-i386",
74 static const char *find_qemu(void)
78 for (i = 0; qemu_table[i]; i++) {
81 if (!stat(qemu_table[i], &st))
88 static const char *kernel_table[] = {
90 "arch/x86/boot/bzImage",
92 "arch/x86/boot/vmlinux",
96 static const char *find_kernel(void)
100 for (i = 0; kernel_table[i]; i++) {
103 if (!stat(kernel_table[i], &st))
104 return kernel_table[i];
110 static const struct {
112 const char *linkpath;
114 { "/proc/self/fd", "/dev/fd" },
115 { "/proc/self/fd/0", "/dev/stdin" },
116 { "/proc/self/fd/1", "/dev/stdout" },
117 { "/proc/self/fd/2", "/dev/stderr" },
121 static const struct {
127 { "sysfs", "/sys", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV },
128 { "proc", "/proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV },
129 { "devtmpfs", "/dev", "mode=0755", MS_NOSUID|MS_STRICTATIME },
130 { "devpts", "/dev/pts", "mode=0620", MS_NOSUID|MS_NOEXEC },
131 { "tmpfs", "/dev/shm", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME },
132 { "tmpfs", "/run", "mode=0755", MS_NOSUID|MS_NODEV|MS_STRICTATIME },
133 { "tmpfs", "/tmp", NULL, 0 },
134 { "debugfs", "/sys/kernel/debug", NULL, 0 },
138 static const char *config_table[] = {
139 "/var/lib/bluetooth",
146 static void prepare_sandbox(void)
150 for (i = 0; mount_table[i].fstype; i++) {
153 if (lstat(mount_table[i].target, &st) < 0) {
154 printf("Creating %s\n", mount_table[i].target);
155 mkdir(mount_table[i].target, 0755);
158 printf("Mounting %s to %s\n", mount_table[i].fstype,
159 mount_table[i].target);
161 if (mount(mount_table[i].fstype,
162 mount_table[i].target,
163 mount_table[i].fstype,
164 mount_table[i].flags,
165 mount_table[i].options) < 0)
166 perror("Failed to mount filesystem");
169 for (i = 0; dev_table[i].target; i++) {
170 printf("Linking %s to %s\n", dev_table[i].linkpath,
171 dev_table[i].target);
173 if (symlink(dev_table[i].target, dev_table[i].linkpath) < 0)
174 perror("Failed to create device symlink");
177 printf("Creating new session group leader\n");
180 printf("Setting controlling terminal\n");
181 ioctl(STDIN_FILENO, TIOCSCTTY, 1);
183 for (i = 0; config_table[i]; i++) {
184 printf("Creating %s\n", config_table[i]);
186 if (mount("tmpfs", config_table[i], "tmpfs",
187 MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
189 perror("Failed to create filesystem");
193 static char *const qemu_argv[] = {
200 "-machine", "type=q35,accel=kvm:tcg",
209 "-fsdev", "local,id=fsdev-root,path=/,readonly,security_model=none",
210 "-device", "virtio-9p-pci,fsdev=fsdev-root,mount_tag=/dev/root",
211 "-chardev", "stdio,id=chardev-serial0,signal=off",
212 "-device", "pci-serial,chardev=chardev-serial0",
216 static char *const qemu_envp[] = {
221 static void check_virtualization(void)
223 #if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__))
226 __asm__ __volatile__("cpuid" : "=c" (ecx) : "a" (1) : "memory");
228 if (!!(ecx & (1 << 5)))
229 printf("Found support for Virtual Machine eXtensions\n");
233 static void start_qemu(void)
235 char cwd[PATH_MAX], initcmd[PATH_MAX], testargs[PATH_MAX];
236 char cmdline[CMDLINE_MAX];
240 check_virtualization();
242 if (!getcwd(cwd, sizeof(cwd)))
245 if (own_binary[0] == '/')
246 snprintf(initcmd, sizeof(initcmd), "%s", own_binary);
248 snprintf(initcmd, sizeof(initcmd), "%s/%s", cwd, own_binary);
250 pos = snprintf(testargs, sizeof(testargs), "%s", test_argv[0]);
252 for (i = 1; i < test_argc; i++) {
253 int len = sizeof(testargs) - pos;
254 pos += snprintf(testargs + pos, len, " %s", test_argv[i]);
257 snprintf(cmdline, sizeof(cmdline),
258 "console=ttyS0,115200n8 earlyprintk=serial "
260 "rootflags=trans=virtio,version=9p2000.L "
261 "acpi=off pci=noacpi noapic quiet ro init=%s "
262 "TESTHOME=%s TESTDBUS=%u TESTDEVS=%d "
263 "TESTAUTO=%u TESTARGS=\'%s\'", initcmd, cwd,
264 start_dbus, num_devs, run_auto, testargs);
266 argv = alloca(sizeof(qemu_argv) +
267 (sizeof(char *) * (4 + (num_devs * 4))));
268 memcpy(argv, qemu_argv, sizeof(qemu_argv));
270 pos = (sizeof(qemu_argv) / sizeof(char *)) - 1;
272 argv[0] = (char *) qemu_binary;
274 argv[pos++] = "-kernel";
275 argv[pos++] = (char *) kernel_image;
276 argv[pos++] = "-append";
277 argv[pos++] = (char *) cmdline;
279 for (i = 0; i < num_devs; i++) {
280 const char *path = "/tmp/bt-server-bredr";
281 char *chrdev, *serdev;
283 chrdev = alloca(32 + strlen(path));
284 sprintf(chrdev, "socket,path=%s,id=bt%d", path, i);
287 sprintf(serdev, "pci-serial,chardev=bt%d", i);
289 argv[pos++] = "-chardev";
290 argv[pos++] = chrdev;
291 argv[pos++] = "-device";
292 argv[pos++] = serdev;
297 execve(argv[0], argv, qemu_envp);
300 static int open_serial(const char *path)
303 int fd, saved_ldisc, ldisc = N_HCI;
305 fd = open(path, O_RDWR | O_NOCTTY);
307 perror("Failed to open serial port");
311 if (tcflush(fd, TCIOFLUSH) < 0) {
312 perror("Failed to flush serial port");
317 if (ioctl(fd, TIOCGETD, &saved_ldisc) < 0) {
318 perror("Failed get serial line discipline");
323 /* Switch TTY to raw mode */
324 memset(&ti, 0, sizeof(ti));
327 ti.c_cflag |= (B115200 | CLOCAL | CREAD);
329 /* Set flow control */
330 ti.c_cflag |= CRTSCTS;
332 if (tcsetattr(fd, TCSANOW, &ti) < 0) {
333 perror("Failed to set serial port settings");
338 if (ioctl(fd, TIOCSETD, &ldisc) < 0) {
339 perror("Failed set serial line discipline");
344 printf("Switched line discipline from %d to %d\n", saved_ldisc, ldisc);
349 static int attach_proto(const char *path, unsigned int proto,
350 unsigned int mandatory_flags,
351 unsigned int optional_flags)
353 unsigned int flags = mandatory_flags | optional_flags;
356 fd = open_serial(path);
360 if (ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
361 if (errno == EINVAL) {
362 if (ioctl(fd, HCIUARTSETFLAGS, mandatory_flags) < 0) {
363 perror("Failed to set mandatory flags");
368 perror("Failed to set flags");
374 if (ioctl(fd, HCIUARTSETPROTO, proto) < 0) {
375 perror("Failed to set protocol");
380 dev_id = ioctl(fd, HCIUARTGETDEVICE);
382 perror("Failed to get device id");
387 printf("Device index %d attached\n", dev_id);
392 static void create_dbus_system_conf(void)
396 fp = fopen("/etc/dbus-1/system.conf", "we");
400 fputs("<!DOCTYPE busconfig PUBLIC "
401 "\"-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN\" "
402 "\"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd\">\n", fp);
403 fputs("<busconfig>\n", fp);
404 fputs("<type>system</type>\n", fp);
405 fputs("<listen>unix:path=/run/dbus/system_bus_socket</listen>\n", fp);
406 fputs("<policy context=\"default\">\n", fp);
407 fputs("<allow user=\"*\"/>\n", fp);
408 fputs("<allow own=\"*\"/>\n", fp);
409 fputs("<allow send_type=\"method_call\"/>\n",fp);
410 fputs("<allow send_type=\"signal\"/>\n", fp);
411 fputs("<allow send_type=\"method_return\"/>\n", fp);
412 fputs("<allow send_type=\"error\"/>\n", fp);
413 fputs("<allow receive_type=\"method_call\"/>\n",fp);
414 fputs("<allow receive_type=\"signal\"/>\n", fp);
415 fputs("<allow receive_type=\"method_return\"/>\n", fp);
416 fputs("<allow receive_type=\"error\"/>\n", fp);
417 fputs("</policy>\n", fp);
418 fputs("</busconfig>\n", fp);
422 mkdir("/run/dbus", 0755);
425 static pid_t start_dbus_daemon(void)
427 char *argv[3], *envp[1];
431 argv[0] = "/usr/bin/dbus-daemon";
432 argv[1] = "--system";
437 printf("Starting D-Bus daemon\n");
441 perror("Failed to fork new process");
446 execve(argv[0], argv, envp);
450 printf("D-Bus daemon process %d created\n", pid);
452 for (i = 0; i < 20; i++) {
455 if (!stat("/run/dbus/system_bus_socket", &st)) {
456 printf("Found D-Bus daemon socket\n");
466 static const char *daemon_table[] = {
469 "/usr/sbin/bluetoothd",
470 "/usr/libexec/bluetooth/bluetoothd",
474 static pid_t start_bluetooth_daemon(const char *home)
476 const char *daemon = NULL;
477 char *argv[3], *envp[2];
481 if (chdir(home + 5) < 0) {
482 perror("Failed to change home directory for daemon");
486 for (i = 0; daemon_table[i]; i++) {
489 if (!stat(daemon_table[i], &st)) {
490 daemon = daemon_table[i];
496 fprintf(stderr, "Failed to locate Bluetooth daemon binary\n");
500 printf("Using Bluetooth daemon %s\n", daemon);
502 argv[0] = (char *) daemon;
503 argv[1] = "--nodetach";
506 envp[0] = "DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket";
509 printf("Starting Bluetooth daemon\n");
513 perror("Failed to fork new process");
518 execve(argv[0], argv, envp);
522 printf("Bluetooth daemon process %d created\n", pid);
527 static const char *test_table[] = {
537 "tools/l2cap-tester",
538 "tools/rfcomm-tester",
541 "tools/check-selftest",
545 static void run_command(char *cmdname, char *home)
547 char *argv[9], *envp[3];
548 int pos = 0, idx = 0;
550 pid_t pid, dbus_pid, daemon_pid;
553 const char *node = "/dev/ttyS1";
554 unsigned int basic_flags, extra_flags;
556 printf("Attaching BR/EDR controller to %s\n", node);
558 basic_flags = (1 << HCI_UART_RESET_ON_INIT);
559 extra_flags = (1 << HCI_UART_VND_DETECT);
561 serial_fd = attach_proto(node, HCI_UART_H4, basic_flags,
567 create_dbus_system_conf();
568 dbus_pid = start_dbus_daemon();
569 daemon_pid = start_bluetooth_daemon(home);
577 if (chdir(home + 5) < 0) {
578 perror("Failed to change home test directory");
585 if (!test_table[idx])
588 if (!stat(test_table[idx], &st))
594 argv[0] = (char *) test_table[idx];
601 ptr = strchr(cmdname, ' ');
603 argv[pos++] = cmdname;
608 argv[pos++] = cmdname;
619 envp[pos++] = "TERM=linux";
624 printf("Running command %s\n", argv[0]);
628 perror("Failed to fork new process");
634 printf("Changing into directory %s\n", home + 5);
635 if (chdir(home + 5) < 0)
636 perror("Failed to change directory");
639 execve(argv[0], argv, envp);
643 printf("New process %d created\n", pid);
649 corpse = waitpid(WAIT_ANY, &status, 0);
650 if (corpse < 0 || corpse == 0)
653 if (WIFEXITED(status))
654 printf("Process %d exited with status %d\n",
655 corpse, WEXITSTATUS(status));
656 else if (WIFSIGNALED(status))
657 printf("Process %d terminated with signal %d\n",
658 corpse, WTERMSIG(status));
659 else if (WIFSTOPPED(status))
660 printf("Process %d stopped with signal %d\n",
661 corpse, WSTOPSIG(status));
662 else if (WIFCONTINUED(status))
663 printf("Process %d continued\n", corpse);
665 if (corpse == dbus_pid) {
666 printf("D-Bus daemon terminated\n");
670 if (corpse == daemon_pid) {
671 printf("Bluetooth daemon terminated\n");
678 kill(daemon_pid, SIGTERM);
680 kill(dbus_pid, SIGTERM);
691 if (serial_fd >= 0) {
697 static void run_tests(void)
699 char cmdline[CMDLINE_MAX], *ptr, *cmds, *home = NULL;
702 fp = fopen("/proc/cmdline", "re");
704 fprintf(stderr, "Failed to open kernel command line\n");
708 ptr = fgets(cmdline, sizeof(cmdline), fp);
712 fprintf(stderr, "Failed to read kernel command line\n");
716 ptr = strstr(cmdline, "TESTARGS=");
718 fprintf(stderr, "No test command section found\n");
723 ptr = strchr(cmds, '\'');
725 fprintf(stderr, "Malformed test command section\n");
731 ptr = strstr(cmdline, "TESTAUTO=1");
733 printf("Automatic test execution requested\n");
737 ptr = strstr(cmdline, "TESTDEVS=1");
739 printf("Attachment of devices requested\n");
743 ptr = strstr(cmdline, "TESTDBUS=1");
745 printf("D-Bus daemon requested\n");
749 ptr = strstr(cmdline, "TESTHOME=");
752 ptr = strpbrk(home + 9, " \r\n");
757 run_command(cmds, home);
760 static void usage(void)
762 printf("test-runner - Automated test execution utility\n"
764 printf("\ttest-runner [options] [--] <command> [args]\n");
766 "\t-a, --auto Find tests and run them\n"
767 "\t-d, --dbus Start D-Bus daemon\n"
768 "\t-u, --unix [path] Provide serial device\n"
769 "\t-q, --qemu <path> QEMU binary\n"
770 "\t-k, --kernel <image> Kernel image (bzImage)\n"
771 "\t-h, --help Show help options\n");
774 static const struct option main_options[] = {
775 { "all", no_argument, NULL, 'a' },
776 { "auto", no_argument, NULL, 'a' },
777 { "unix", no_argument, NULL, 'u' },
778 { "dbus", no_argument, NULL, 'd' },
779 { "qemu", required_argument, NULL, 'q' },
780 { "kernel", required_argument, NULL, 'k' },
781 { "version", no_argument, NULL, 'v' },
782 { "help", no_argument, NULL, 'h' },
786 int main(int argc, char *argv[])
788 if (getpid() == 1 && getppid() == 0) {
800 opt = getopt_long(argc, argv, "audq:k:vh", main_options, NULL);
815 qemu_binary = optarg;
818 kernel_image = optarg;
821 printf("%s\n", VERSION);
832 if (argc - optind > 0) {
833 fprintf(stderr, "Invalid command line parameters\n");
837 if (argc - optind < 1) {
838 fprintf(stderr, "Failed to specify test command\n");
843 own_binary = argv[0];
844 test_argv = argv + optind;
845 test_argc = argc - optind;
848 qemu_binary = find_qemu();
850 fprintf(stderr, "No default QEMU binary found\n");
856 kernel_image = find_kernel();
858 fprintf(stderr, "No default kernel image found\n");
863 printf("Using QEMU binary %s\n", qemu_binary);
864 printf("Using kernel image %s\n", kernel_image);