2018-04-24 06:49:00

by Szymon Janc

[permalink] [raw]
Subject: [PATCH v7] tools: Add initial code for btmon-logger

This is intended for use for automated logging or unatrended systems.
It doesn't contain any packet decoding functionality which results
in much smaller binary.
---
.gitignore | 2 +
Makefile.tools | 17 ++
android/bluetoothd-snoop.c | 2 +-
bootstrap-configure | 1 +
configure.ac | 4 +
monitor/control.c | 2 +-
src/shared/btsnoop.c | 75 +++++++-
src/shared/btsnoop.h | 3 +-
tools/bluetooth-logger.service.in | 18 ++
tools/btmon-logger.c | 388 ++++++++++++++++++++++++++++++++++++++
10 files changed, 507 insertions(+), 5 deletions(-)
create mode 100644 tools/bluetooth-logger.service.in
create mode 100644 tools/btmon-logger.c

diff --git a/.gitignore b/.gitignore
index 47808059b..2b652dcfc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -118,6 +118,8 @@ tools/btconfig
tools/btmgmt
tools/btsnoop
tools/btpclient
+tools/btmon-logger
+tools/bluetooth-logger.service
peripheral/btsensor
monitor/btmon
emulator/btvirt
diff --git a/Makefile.tools b/Makefile.tools
index f7ab77de1..59f307624 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -66,6 +66,23 @@ monitor_btmon_LDADD = lib/libbluetooth-internal.la \
src/libshared-mainloop.la @UDEV_LIBS@
endif

+if LOGGER
+libexec_PROGRAMS += tools/btmon-logger
+
+tools_btmon_logger_SOURCES = tools/btmon-logger.c src/systemd.c src/systemd.h \
+ lib/monitor.h
+tools_btmon_logger_LDADD = src/libshared-mainloop.la
+tools_btmon_logger_DEPENDENCIES = src/libshared-mainloop.la \
+ tools/bluetooth-logger.service
+
+if SYSTEMD
+systemdsystemunit_DATA += tools/bluetooth-logger.service
+endif
+endif
+
+CLEANFILES += tools/bluetooth-logger.service
+EXTRA_DIST += tools/bluetooth-logger.service.in
+
if TESTING
noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp \
peripheral/btsensor tools/3dsp \
diff --git a/android/bluetoothd-snoop.c b/android/bluetoothd-snoop.c
index 4b096632a..8d9a2d087 100644
--- a/android/bluetoothd-snoop.c
+++ b/android/bluetoothd-snoop.c
@@ -148,7 +148,7 @@ static int open_monitor(const char *path)
struct sockaddr_hci addr;
int opt = 1;

- snoop = btsnoop_create(path, BTSNOOP_FORMAT_HCI);
+ snoop = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_HCI);
if (!snoop)
return -1;

diff --git a/bootstrap-configure b/bootstrap-configure
index 658eef296..b14b4553b 100755
--- a/bootstrap-configure
+++ b/bootstrap-configure
@@ -24,4 +24,5 @@ fi
--enable-sixaxis \
--enable-midi \
--enable-mesh \
+ --enable-logger \
--disable-datafiles $*
diff --git a/configure.ac b/configure.ac
index db46d6f07..5132131f2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -327,6 +327,10 @@ AC_ARG_ENABLE(sixaxis, AC_HELP_STRING([--enable-sixaxis],
AM_CONDITIONAL(SIXAXIS, test "${enable_sixaxis}" = "yes" &&
test "${enable_udev}" != "no")

+AC_ARG_ENABLE(logger, AC_HELP_STRING([--enable-logger],
+ [enable HCI logger service]), [enable_logger=${enableval}])
+AM_CONDITIONAL(LOGGER, test "${enable_logger}" = "yes")
+
if (test "${prefix}" = "NONE"); then
dnl no prefix and no localstatedir, so default to /var
if (test "$localstatedir" = '${prefix}/var'); then
diff --git a/monitor/control.c b/monitor/control.c
index 1cd79ca5d..6330fff96 100644
--- a/monitor/control.c
+++ b/monitor/control.c
@@ -1373,7 +1373,7 @@ int control_tty(const char *path, unsigned int speed)

bool control_writer(const char *path)
{
- btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR);
+ btsnoop_file = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_MONITOR);

return !!btsnoop_file;
}
diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
index e20d1b382..a3803e184 100644
--- a/src/shared/btsnoop.c
+++ b/src/shared/btsnoop.c
@@ -30,6 +30,8 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
+#include <limits.h>
#include <arpa/inet.h>
#include <sys/stat.h>

@@ -73,6 +75,11 @@ struct btsnoop {
bool aborted;
bool pklg_format;
bool pklg_v2;
+ const char *path;
+ size_t max_size;
+ size_t cur_size;
+ unsigned int max_count;
+ unsigned int cur_count;
};

struct btsnoop *btsnoop_open(const char *path, unsigned long flags)
@@ -131,17 +138,31 @@ failed:
return NULL;
}

-struct btsnoop *btsnoop_create(const char *path, uint32_t format)
+struct btsnoop *btsnoop_create(const char *path, size_t max_size,
+ unsigned int max_count, uint32_t format)
{
struct btsnoop *btsnoop;
struct btsnoop_hdr hdr;
+ const char *real_path;
+ char tmp[PATH_MAX];
ssize_t written;

+ if (!max_size && max_count)
+ return NULL;
+
btsnoop = calloc(1, sizeof(*btsnoop));
if (!btsnoop)
return NULL;

- btsnoop->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+ /* If max file size is specified, always add counter to file path */
+ if (max_size) {
+ snprintf(tmp, PATH_MAX, "%s.0", path);
+ real_path = tmp;
+ } else {
+ real_path = path;
+ }
+
+ btsnoop->fd = open(real_path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (btsnoop->fd < 0) {
free(btsnoop);
@@ -150,6 +171,9 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t format)

btsnoop->format = format;
btsnoop->index = 0xffff;
+ btsnoop->path = path;
+ btsnoop->max_count = max_count;
+ btsnoop->max_size = max_size;

memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
hdr.version = htobe32(btsnoop_version);
@@ -162,6 +186,8 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t format)
return NULL;
}

+ btsnoop->cur_size = BTSNOOP_HDR_SIZE;
+
return btsnoop_ref(btsnoop);
}

@@ -197,6 +223,42 @@ uint32_t btsnoop_get_format(struct btsnoop *btsnoop)
return btsnoop->format;
}

+static bool btsnoop_rotate(struct btsnoop *btsnoop)
+{
+ struct btsnoop_hdr hdr;
+ char path[PATH_MAX];
+ ssize_t written;
+
+ close(btsnoop->fd);
+
+ /* Check if max number of log files has been reached */
+ if (btsnoop->max_count && btsnoop->cur_count >= btsnoop->max_count) {
+ snprintf(path, PATH_MAX, "%s.%u", btsnoop->path,
+ btsnoop->cur_count - btsnoop->max_count);
+ unlink(path);
+ }
+
+ snprintf(path, PATH_MAX,"%s.%u", btsnoop->path, btsnoop->cur_count);
+ btsnoop->cur_count++;
+
+ btsnoop->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (btsnoop->fd < 0)
+ return false;
+
+ memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+ hdr.version = htobe32(btsnoop_version);
+ hdr.type = htobe32(btsnoop->format);
+
+ written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
+ if (written < 0)
+ return false;
+
+ btsnoop->cur_size = BTSNOOP_HDR_SIZE;
+
+ return true;
+}
+
bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
uint32_t flags, uint32_t drops, const void *data,
uint16_t size)
@@ -208,6 +270,11 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
if (!btsnoop || !tv)
return false;

+ if (btsnoop->max_size && btsnoop->max_size <=
+ btsnoop->cur_size + size + BTSNOOP_PKT_SIZE)
+ if (!btsnoop_rotate(btsnoop))
+ return false;
+
ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec;

pkt.size = htobe32(size);
@@ -220,12 +287,16 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
if (written < 0)
return false;

+ btsnoop->cur_size += BTSNOOP_PKT_SIZE;
+
if (data && size > 0) {
written = write(btsnoop->fd, data, size);
if (written < 0)
return false;
}

+ btsnoop->cur_size += size;
+
return true;
}

diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index 3df8998a3..3043d33e2 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -99,7 +99,8 @@ struct btsnoop_opcode_user_logging {
struct btsnoop;

struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
-struct btsnoop *btsnoop_create(const char *path, uint32_t format);
+struct btsnoop *btsnoop_create(const char *path, size_t max_size,
+ unsigned int max_count, uint32_t format);

struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop);
void btsnoop_unref(struct btsnoop *btsnoop);
diff --git a/tools/bluetooth-logger.service.in b/tools/bluetooth-logger.service.in
new file mode 100644
index 000000000..210bf59ac
--- /dev/null
+++ b/tools/bluetooth-logger.service.in
@@ -0,0 +1,18 @@
+[Unit]
+Description=Bluetooth monitor logger
+ConditionPathIsDirectory=/sys/class/bluetooth
+
+[Service]
+Type=simple
+ExecStart=@libexecdir@/btmon-logger -p -b /var/log/bluetooth/hci.log
+NotifyAccess=main
+CapabilityBoundingSet=CAP_NET_RAW
+LimitNPROC=1
+ProtectHome=true
+ProtectSystem=full
+PrivateTmp=true
+PrivateDevices=true
+PrivateNetwork=true
+
+[Install]
+WantedBy=bluetooth.target
diff --git a/tools/btmon-logger.c b/tools/btmon-logger.c
new file mode 100644
index 000000000..c3ba17939
--- /dev/null
+++ b/tools/btmon-logger.c
@@ -0,0 +1,388 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2017-2018 Codecoup
+ * Copyright (C) 2011-2014 Intel Corporation
+ * Copyright (C) 2002-2010 Marcel Holtmann <[email protected]>
+ *
+ *
+ * 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 <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <time.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <libgen.h>
+#include <errno.h>
+
+#include <linux/capability.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/util.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/btsnoop.h"
+
+#include "src/systemd.h"
+
+#define MONITOR_INDEX_NONE 0xffff
+
+struct monitor_hdr {
+ uint16_t opcode;
+ uint16_t index;
+ uint16_t len;
+} __attribute__ ((packed));
+
+static struct btsnoop *btsnoop_file = NULL;
+
+static void data_callback(int fd, uint32_t events, void *user_data)
+{
+ uint8_t buf[BTSNOOP_MAX_PACKET_SIZE];
+ unsigned char control[64];
+ struct monitor_hdr hdr;
+ struct msghdr msg;
+ struct iovec iov[2];
+
+ if (events & (EPOLLERR | EPOLLHUP)) {
+ mainloop_exit_failure();
+ return;
+ }
+
+ iov[0].iov_base = &hdr;
+ iov[0].iov_len = sizeof(hdr);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = sizeof(buf);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ while (1) {
+ struct cmsghdr *cmsg;
+ struct timeval *tv = NULL;
+ struct timeval ctv;
+ uint16_t opcode, index, pktlen;
+ ssize_t len;
+
+ len = recvmsg(fd, &msg, MSG_DONTWAIT);
+ if (len < 0)
+ break;
+
+ if (len < (ssize_t) sizeof(hdr))
+ break;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET)
+ continue;
+
+ if (cmsg->cmsg_type == SCM_TIMESTAMP) {
+ memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv));
+ tv = &ctv;
+ }
+ }
+
+ opcode = le16_to_cpu(hdr.opcode);
+ index = le16_to_cpu(hdr.index);
+ pktlen = le16_to_cpu(hdr.len);
+
+ btsnoop_write_hci(btsnoop_file, tv, index, opcode, 0, buf,
+ pktlen);
+ }
+}
+
+static bool open_monitor_channel(void)
+{
+ struct sockaddr_hci addr;
+ int fd, opt = 1;
+
+ fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+ if (fd < 0) {
+ perror("Failed to open monitor channel");
+ return false;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_family = AF_BLUETOOTH;
+ addr.hci_dev = HCI_DEV_NONE;
+ addr.hci_channel = HCI_CHANNEL_MONITOR;
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Failed to bind monitor channel");
+ close(fd);
+ return false;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) {
+ perror("Failed to enable timestamps");
+ close(fd);
+ return false;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)) < 0) {
+ perror("Failed to enable credentials");
+ close(fd);
+ return false;
+ }
+
+ mainloop_add_fd(fd, EPOLLIN, data_callback, NULL, NULL);
+
+ return true;
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ mainloop_quit();
+ break;
+ }
+}
+
+extern int capget(struct __user_cap_header_struct *header,
+ struct __user_cap_data_struct *data);
+extern int capset(struct __user_cap_header_struct *header,
+ const struct __user_cap_data_struct *data);
+
+static void drop_capabilities(void)
+{
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+ unsigned int mask;
+ int err;
+
+ header.version = _LINUX_CAPABILITY_VERSION_3;
+ header.pid = 0;
+
+ err = capget(&header, &cap);
+ if (err) {
+ perror("Unable to get current capabilities");
+ return;
+ }
+
+ /* not needed anymore since monitor socket is already open */
+ mask = ~CAP_TO_MASK(CAP_NET_RAW);
+
+ cap.effective &= mask;
+ cap.permitted &= mask;
+ cap.inheritable &= mask;
+
+ err = capset(&header, &cap);
+ if (err)
+ perror("Failed to set capabilities");
+}
+
+static void usage(void)
+{
+ printf("btmon-logger - Bluetooth monitor\n"
+ "Usage:\n");
+ printf("\tbtmon-logger [options]\n");
+ printf("options:\n"
+ "\t-b, --basename <path> Save traces in specified path\n"
+ "\t-p, --parents Create basename parent directories\n"
+ "\t-l, --limit <limit> Limit traces file size (rotate)\n"
+ "\t-c, --count <count> Limit number of rotated files\n"
+ "\t-v, --version Show version\n"
+ "\t-h, --help Show help options\n");
+}
+
+static const struct option main_options[] = {
+ { "basename", required_argument, NULL, 'b' },
+ { "parents", no_argument, NULL, 'p' },
+ { "limit", required_argument, NULL, 'l' },
+ { "count", required_argument, NULL, 'c' },
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { }
+};
+
+static int create_dir(const char *filename)
+{
+ char *dirc;
+ char *dir;
+ char *p;
+ int err = 0;
+
+ /* get base directory */
+ dirc = strdup(filename);
+ dir = dirname(dirc);
+
+ p = dir;
+
+ /* preserve leading / if present */
+ if (*p == '/')
+ p++;
+
+ /* create any intermediate directories */
+ p = strchrnul(p, '/');
+ while (*p) {
+ /* cut directory path */
+ *p = '\0';
+
+ if (mkdir(dir, 0700) < 0 && errno != EEXIST) {
+ err = errno;
+ goto done;
+ }
+
+ /* restore directory path */
+ *p = '/';
+ p = strchrnul(p + 1, '/');
+ }
+
+ /* create leaf directory */
+ if (mkdir(dir, 0700) < 0 && errno != EEXIST)
+ err = errno;
+
+done:
+ free(dirc);
+
+ if (err)
+ printf("Failed to create parent directories for %s\n",
+ filename);
+
+ return err;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *path = "hci.log";
+ unsigned long max_count = 0;
+ size_t size_limit = 0;
+ bool parents = false;
+ int exit_status;
+ sigset_t mask;
+ char *endptr;
+
+ mainloop_init();
+
+ sd_notify(0, "STATUS=Starting up");
+
+ while (true) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "b:l:c:vhp", main_options,
+ NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'b':
+ path = optarg;
+ if (strlen(path) > PATH_MAX) {
+ fprintf(stderr, "Too long path\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'l':
+ size_limit = strtoul(optarg, &endptr, 10);
+
+ if (size_limit == ULONG_MAX) {
+ fprintf(stderr, "Invalid limit\n");
+ return EXIT_FAILURE;
+ }
+
+ if (*endptr != '\0') {
+ if (*endptr == 'K' || *endptr == 'k') {
+ size_limit *= 1024;
+ } else if (*endptr == 'M' || *endptr == 'm') {
+ size_limit *= 1024 * 1024;
+ } else {
+ fprintf(stderr, "Invalid limit\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* limit this to reasonable size */
+ if (size_limit < 4096) {
+ fprintf(stderr, "Too small limit value\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'c':
+ max_count = strtoul(optarg, &endptr, 10);
+ break;
+ case 'p':
+ if (getppid() != 1) {
+ fprintf(stderr, "Parents option allowed only "
+ "when running as a service\n");
+ return EXIT_FAILURE;
+ }
+
+ parents = true;
+ break;
+ case 'v':
+ printf("%s\n", VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (argc - optind > 0) {
+ fprintf(stderr, "Invalid command line parameters\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!open_monitor_channel())
+ return EXIT_FAILURE;
+
+ if (parents && create_dir(path) < 0)
+ return EXIT_FAILURE;
+
+ btsnoop_file = btsnoop_create(path, size_limit, max_count,
+ BTSNOOP_FORMAT_MONITOR);
+ if (!btsnoop_file)
+ return EXIT_FAILURE;
+
+ drop_capabilities();
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+ printf("Bluetooth monitor logger ver %s\n", VERSION);
+
+ sd_notify(0, "STATUS=Running");
+ sd_notify(0, "READY=1");
+
+ exit_status = mainloop_run();
+
+ sd_notify(0, "STATUS=Quitting");
+
+ btsnoop_unref(btsnoop_file);
+
+ return exit_status;
+}
--
2.14.3



2018-04-24 08:43:08

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH v7] tools: Add initial code for btmon-logger

On Tuesday, 24 April 2018 08:49:00 CEST Szymon Janc wrote:
> This is intended for use for automated logging or unatrended systems.
> It doesn't contain any packet decoding functionality which results
> in much smaller binary.
> ---
> .gitignore | 2 +
> Makefile.tools | 17 ++
> android/bluetoothd-snoop.c | 2 +-
> bootstrap-configure | 1 +
> configure.ac | 4 +
> monitor/control.c | 2 +-
> src/shared/btsnoop.c | 75 +++++++-
> src/shared/btsnoop.h | 3 +-
> tools/bluetooth-logger.service.in | 18 ++
> tools/btmon-logger.c | 388
> ++++++++++++++++++++++++++++++++++++++ 10 files changed, 507 insertions(+),
> 5 deletions(-)
> create mode 100644 tools/bluetooth-logger.service.in
> create mode 100644 tools/btmon-logger.c
>
> diff --git a/.gitignore b/.gitignore
> index 47808059b..2b652dcfc 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -118,6 +118,8 @@ tools/btconfig
> tools/btmgmt
> tools/btsnoop
> tools/btpclient
> +tools/btmon-logger
> +tools/bluetooth-logger.service
> peripheral/btsensor
> monitor/btmon
> emulator/btvirt
> diff --git a/Makefile.tools b/Makefile.tools
> index f7ab77de1..59f307624 100644
> --- a/Makefile.tools
> +++ b/Makefile.tools
> @@ -66,6 +66,23 @@ monitor_btmon_LDADD = lib/libbluetooth-internal.la \
> src/libshared-mainloop.la @UDEV_LIBS@
> endif
>
> +if LOGGER
> +libexec_PROGRAMS += tools/btmon-logger
> +
> +tools_btmon_logger_SOURCES = tools/btmon-logger.c src/systemd.c
> src/systemd.h \ + lib/monitor.h
> +tools_btmon_logger_LDADD = src/libshared-mainloop.la
> +tools_btmon_logger_DEPENDENCIES = src/libshared-mainloop.la \
> + tools/bluetooth-logger.service
> +
> +if SYSTEMD
> +systemdsystemunit_DATA += tools/bluetooth-logger.service
> +endif
> +endif
> +
> +CLEANFILES += tools/bluetooth-logger.service
> +EXTRA_DIST += tools/bluetooth-logger.service.in
> +
> if TESTING
> noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp \
> peripheral/btsensor tools/3dsp \
> diff --git a/android/bluetoothd-snoop.c b/android/bluetoothd-snoop.c
> index 4b096632a..8d9a2d087 100644
> --- a/android/bluetoothd-snoop.c
> +++ b/android/bluetoothd-snoop.c
> @@ -148,7 +148,7 @@ static int open_monitor(const char *path)
> struct sockaddr_hci addr;
> int opt = 1;
>
> - snoop = btsnoop_create(path, BTSNOOP_FORMAT_HCI);
> + snoop = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_HCI);
> if (!snoop)
> return -1;
>
> diff --git a/bootstrap-configure b/bootstrap-configure
> index 658eef296..b14b4553b 100755
> --- a/bootstrap-configure
> +++ b/bootstrap-configure
> @@ -24,4 +24,5 @@ fi
> --enable-sixaxis \
> --enable-midi \
> --enable-mesh \
> + --enable-logger \
> --disable-datafiles $*
> diff --git a/configure.ac b/configure.ac
> index db46d6f07..5132131f2 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -327,6 +327,10 @@ AC_ARG_ENABLE(sixaxis,
> AC_HELP_STRING([--enable-sixaxis], AM_CONDITIONAL(SIXAXIS, test
> "${enable_sixaxis}" = "yes" &&
> test "${enable_udev}" != "no")
>
> +AC_ARG_ENABLE(logger, AC_HELP_STRING([--enable-logger],
> + [enable HCI logger service]), [enable_logger=${enableval}])
> +AM_CONDITIONAL(LOGGER, test "${enable_logger}" = "yes")
> +
> if (test "${prefix}" = "NONE"); then
> dnl no prefix and no localstatedir, so default to /var
> if (test "$localstatedir" = '${prefix}/var'); then
> diff --git a/monitor/control.c b/monitor/control.c
> index 1cd79ca5d..6330fff96 100644
> --- a/monitor/control.c
> +++ b/monitor/control.c
> @@ -1373,7 +1373,7 @@ int control_tty(const char *path, unsigned int speed)
>
> bool control_writer(const char *path)
> {
> - btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR);
> + btsnoop_file = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_MONITOR);
>
> return !!btsnoop_file;
> }
> diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
> index e20d1b382..a3803e184 100644
> --- a/src/shared/btsnoop.c
> +++ b/src/shared/btsnoop.c
> @@ -30,6 +30,8 @@
> #include <unistd.h>
> #include <stdlib.h>
> #include <string.h>
> +#include <stdio.h>
> +#include <limits.h>
> #include <arpa/inet.h>
> #include <sys/stat.h>
>
> @@ -73,6 +75,11 @@ struct btsnoop {
> bool aborted;
> bool pklg_format;
> bool pklg_v2;
> + const char *path;
> + size_t max_size;
> + size_t cur_size;
> + unsigned int max_count;
> + unsigned int cur_count;
> };
>
> struct btsnoop *btsnoop_open(const char *path, unsigned long flags)
> @@ -131,17 +138,31 @@ failed:
> return NULL;
> }
>
> -struct btsnoop *btsnoop_create(const char *path, uint32_t format)
> +struct btsnoop *btsnoop_create(const char *path, size_t max_size,
> + unsigned int max_count, uint32_t format)
> {
> struct btsnoop *btsnoop;
> struct btsnoop_hdr hdr;
> + const char *real_path;
> + char tmp[PATH_MAX];
> ssize_t written;
>
> + if (!max_size && max_count)
> + return NULL;
> +
> btsnoop = calloc(1, sizeof(*btsnoop));
> if (!btsnoop)
> return NULL;
>
> - btsnoop->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
> + /* If max file size is specified, always add counter to file path */
> + if (max_size) {
> + snprintf(tmp, PATH_MAX, "%s.0", path);
> + real_path = tmp;
> + } else {
> + real_path = path;
> + }
> +
> + btsnoop->fd = open(real_path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
> S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
> if (btsnoop->fd < 0) {
> free(btsnoop);
> @@ -150,6 +171,9 @@ struct btsnoop *btsnoop_create(const char *path,
> uint32_t format)
>
> btsnoop->format = format;
> btsnoop->index = 0xffff;
> + btsnoop->path = path;
> + btsnoop->max_count = max_count;
> + btsnoop->max_size = max_size;
>
> memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
> hdr.version = htobe32(btsnoop_version);
> @@ -162,6 +186,8 @@ struct btsnoop *btsnoop_create(const char *path,
> uint32_t format) return NULL;
> }
>
> + btsnoop->cur_size = BTSNOOP_HDR_SIZE;
> +
> return btsnoop_ref(btsnoop);
> }
>
> @@ -197,6 +223,42 @@ uint32_t btsnoop_get_format(struct btsnoop *btsnoop)
> return btsnoop->format;
> }
>
> +static bool btsnoop_rotate(struct btsnoop *btsnoop)
> +{
> + struct btsnoop_hdr hdr;
> + char path[PATH_MAX];
> + ssize_t written;
> +
> + close(btsnoop->fd);
> +
> + /* Check if max number of log files has been reached */
> + if (btsnoop->max_count && btsnoop->cur_count >= btsnoop->max_count) {
> + snprintf(path, PATH_MAX, "%s.%u", btsnoop->path,
> + btsnoop->cur_count - btsnoop->max_count);
> + unlink(path);
> + }
> +
> + snprintf(path, PATH_MAX,"%s.%u", btsnoop->path, btsnoop->cur_count);
> + btsnoop->cur_count++;
> +
> + btsnoop->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
> + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
> + if (btsnoop->fd < 0)
> + return false;
> +
> + memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
> + hdr.version = htobe32(btsnoop_version);
> + hdr.type = htobe32(btsnoop->format);
> +
> + written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
> + if (written < 0)
> + return false;
> +
> + btsnoop->cur_size = BTSNOOP_HDR_SIZE;
> +
> + return true;
> +}
> +
> bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
> uint32_t flags, uint32_t drops, const void *data,
> uint16_t size)
> @@ -208,6 +270,11 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct
> timeval *tv, if (!btsnoop || !tv)
> return false;
>
> + if (btsnoop->max_size && btsnoop->max_size <=
> + btsnoop->cur_size + size + BTSNOOP_PKT_SIZE)
> + if (!btsnoop_rotate(btsnoop))
> + return false;
> +
> ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec;
>
> pkt.size = htobe32(size);
> @@ -220,12 +287,16 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct
> timeval *tv, if (written < 0)
> return false;
>
> + btsnoop->cur_size += BTSNOOP_PKT_SIZE;
> +
> if (data && size > 0) {
> written = write(btsnoop->fd, data, size);
> if (written < 0)
> return false;
> }
>
> + btsnoop->cur_size += size;
> +
> return true;
> }
>
> diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
> index 3df8998a3..3043d33e2 100644
> --- a/src/shared/btsnoop.h
> +++ b/src/shared/btsnoop.h
> @@ -99,7 +99,8 @@ struct btsnoop_opcode_user_logging {
> struct btsnoop;
>
> struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
> -struct btsnoop *btsnoop_create(const char *path, uint32_t format);
> +struct btsnoop *btsnoop_create(const char *path, size_t max_size,
> + unsigned int max_count, uint32_t format);
>
> struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop);
> void btsnoop_unref(struct btsnoop *btsnoop);
> diff --git a/tools/bluetooth-logger.service.in
> b/tools/bluetooth-logger.service.in new file mode 100644
> index 000000000..210bf59ac
> --- /dev/null
> +++ b/tools/bluetooth-logger.service.in
> @@ -0,0 +1,18 @@
> +[Unit]
> +Description=Bluetooth monitor logger
> +ConditionPathIsDirectory=/sys/class/bluetooth
> +
> +[Service]
> +Type=simple
> +ExecStart=@libexecdir@/btmon-logger -p -b /var/log/bluetooth/hci.log
> +NotifyAccess=main
> +CapabilityBoundingSet=CAP_NET_RAW
> +LimitNPROC=1
> +ProtectHome=true
> +ProtectSystem=full
> +PrivateTmp=true
> +PrivateDevices=true
> +PrivateNetwork=true
> +
> +[Install]
> +WantedBy=bluetooth.target
> diff --git a/tools/btmon-logger.c b/tools/btmon-logger.c
> new file mode 100644
> index 000000000..c3ba17939
> --- /dev/null
> +++ b/tools/btmon-logger.c
> @@ -0,0 +1,388 @@
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2017-2018 Codecoup
> + * Copyright (C) 2011-2014 Intel Corporation
> + * Copyright (C) 2002-2010 Marcel Holtmann <[email protected]>
> + *
> + *
> + * 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 <stdlib.h>
> +#include <limits.h>
> +#include <string.h>
> +#include <time.h>
> +#include <getopt.h>
> +#include <unistd.h>
> +#include <sys/socket.h>
> +#include <sys/stat.h>
> +#include <libgen.h>
> +#include <errno.h>
> +
> +#include <linux/capability.h>
> +
> +#include "lib/bluetooth.h"
> +#include "lib/hci.h"
> +
> +#include "src/shared/util.h"
> +#include "src/shared/mainloop.h"
> +#include "src/shared/btsnoop.h"
> +
> +#include "src/systemd.h"
> +
> +#define MONITOR_INDEX_NONE 0xffff
> +
> +struct monitor_hdr {
> + uint16_t opcode;
> + uint16_t index;
> + uint16_t len;
> +} __attribute__ ((packed));
> +
> +static struct btsnoop *btsnoop_file = NULL;
> +
> +static void data_callback(int fd, uint32_t events, void *user_data)
> +{
> + uint8_t buf[BTSNOOP_MAX_PACKET_SIZE];
> + unsigned char control[64];
> + struct monitor_hdr hdr;
> + struct msghdr msg;
> + struct iovec iov[2];
> +
> + if (events & (EPOLLERR | EPOLLHUP)) {
> + mainloop_exit_failure();
> + return;
> + }
> +
> + iov[0].iov_base = &hdr;
> + iov[0].iov_len = sizeof(hdr);
> + iov[1].iov_base = buf;
> + iov[1].iov_len = sizeof(buf);
> +
> + memset(&msg, 0, sizeof(msg));
> + msg.msg_iov = iov;
> + msg.msg_iovlen = 2;
> + msg.msg_control = control;
> + msg.msg_controllen = sizeof(control);
> +
> + while (1) {
> + struct cmsghdr *cmsg;
> + struct timeval *tv = NULL;
> + struct timeval ctv;
> + uint16_t opcode, index, pktlen;
> + ssize_t len;
> +
> + len = recvmsg(fd, &msg, MSG_DONTWAIT);
> + if (len < 0)
> + break;
> +
> + if (len < (ssize_t) sizeof(hdr))
> + break;
> +
> + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
> + cmsg = CMSG_NXTHDR(&msg, cmsg)) {
> + if (cmsg->cmsg_level != SOL_SOCKET)
> + continue;
> +
> + if (cmsg->cmsg_type == SCM_TIMESTAMP) {
> + memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv));
> + tv = &ctv;
> + }
> + }
> +
> + opcode = le16_to_cpu(hdr.opcode);
> + index = le16_to_cpu(hdr.index);
> + pktlen = le16_to_cpu(hdr.len);
> +
> + btsnoop_write_hci(btsnoop_file, tv, index, opcode, 0, buf,
> + pktlen);
> + }
> +}
> +
> +static bool open_monitor_channel(void)
> +{
> + struct sockaddr_hci addr;
> + int fd, opt = 1;
> +
> + fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
> + if (fd < 0) {
> + perror("Failed to open monitor channel");
> + return false;
> + }
> +
> + memset(&addr, 0, sizeof(addr));
> + addr.hci_family = AF_BLUETOOTH;
> + addr.hci_dev = HCI_DEV_NONE;
> + addr.hci_channel = HCI_CHANNEL_MONITOR;
> +
> + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
> + perror("Failed to bind monitor channel");
> + close(fd);
> + return false;
> + }
> +
> + if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) {
> + perror("Failed to enable timestamps");
> + close(fd);
> + return false;
> + }
> +
> + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)) < 0) {
> + perror("Failed to enable credentials");
> + close(fd);
> + return false;
> + }
> +
> + mainloop_add_fd(fd, EPOLLIN, data_callback, NULL, NULL);
> +
> + return true;
> +}
> +
> +static void signal_callback(int signum, void *user_data)
> +{
> + switch (signum) {
> + case SIGINT:
> + case SIGTERM:
> + mainloop_quit();
> + break;
> + }
> +}
> +
> +extern int capget(struct __user_cap_header_struct *header,
> + struct __user_cap_data_struct *data);
> +extern int capset(struct __user_cap_header_struct *header,
> + const struct __user_cap_data_struct *data);
> +
> +static void drop_capabilities(void)
> +{
> + struct __user_cap_header_struct header;
> + struct __user_cap_data_struct cap;
> + unsigned int mask;
> + int err;
> +
> + header.version = _LINUX_CAPABILITY_VERSION_3;
> + header.pid = 0;
> +
> + err = capget(&header, &cap);
> + if (err) {
> + perror("Unable to get current capabilities");
> + return;
> + }
> +
> + /* not needed anymore since monitor socket is already open */
> + mask = ~CAP_TO_MASK(CAP_NET_RAW);
> +
> + cap.effective &= mask;
> + cap.permitted &= mask;
> + cap.inheritable &= mask;
> +
> + err = capset(&header, &cap);
> + if (err)
> + perror("Failed to set capabilities");
> +}
> +
> +static void usage(void)
> +{
> + printf("btmon-logger - Bluetooth monitor\n"
> + "Usage:\n");
> + printf("\tbtmon-logger [options]\n");
> + printf("options:\n"
> + "\t-b, --basename <path> Save traces in specified path\n"
> + "\t-p, --parents Create basename parent directories\n"
> + "\t-l, --limit <limit> Limit traces file size (rotate)\n"
> + "\t-c, --count <count> Limit number of rotated files\n"
> + "\t-v, --version Show version\n"
> + "\t-h, --help Show help options\n");
> +}
> +
> +static const struct option main_options[] = {
> + { "basename", required_argument, NULL, 'b' },
> + { "parents", no_argument, NULL, 'p' },
> + { "limit", required_argument, NULL, 'l' },
> + { "count", required_argument, NULL, 'c' },
> + { "version", no_argument, NULL, 'v' },
> + { "help", no_argument, NULL, 'h' },
> + { }
> +};
> +
> +static int create_dir(const char *filename)
> +{
> + char *dirc;
> + char *dir;
> + char *p;
> + int err = 0;
> +
> + /* get base directory */
> + dirc = strdup(filename);
> + dir = dirname(dirc);
> +
> + p = dir;
> +
> + /* preserve leading / if present */
> + if (*p == '/')
> + p++;
> +
> + /* create any intermediate directories */
> + p = strchrnul(p, '/');
> + while (*p) {
> + /* cut directory path */
> + *p = '\0';
> +
> + if (mkdir(dir, 0700) < 0 && errno != EEXIST) {
> + err = errno;
> + goto done;
> + }
> +
> + /* restore directory path */
> + *p = '/';
> + p = strchrnul(p + 1, '/');
> + }
> +
> + /* create leaf directory */
> + if (mkdir(dir, 0700) < 0 && errno != EEXIST)
> + err = errno;
> +
> +done:
> + free(dirc);
> +
> + if (err)
> + printf("Failed to create parent directories for %s\n",
> + filename);
> +
> + return err;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + const char *path = "hci.log";
> + unsigned long max_count = 0;
> + size_t size_limit = 0;
> + bool parents = false;
> + int exit_status;
> + sigset_t mask;
> + char *endptr;
> +
> + mainloop_init();
> +
> + sd_notify(0, "STATUS=Starting up");
> +
> + while (true) {
> + int opt;
> +
> + opt = getopt_long(argc, argv, "b:l:c:vhp", main_options,
> + NULL);
> + if (opt < 0)
> + break;
> +
> + switch (opt) {
> + case 'b':
> + path = optarg;
> + if (strlen(path) > PATH_MAX) {
> + fprintf(stderr, "Too long path\n");
> + return EXIT_FAILURE;
> + }
> + break;
> + case 'l':
> + size_limit = strtoul(optarg, &endptr, 10);
> +
> + if (size_limit == ULONG_MAX) {
> + fprintf(stderr, "Invalid limit\n");
> + return EXIT_FAILURE;
> + }
> +
> + if (*endptr != '\0') {
> + if (*endptr == 'K' || *endptr == 'k') {
> + size_limit *= 1024;
> + } else if (*endptr == 'M' || *endptr == 'm') {
> + size_limit *= 1024 * 1024;
> + } else {
> + fprintf(stderr, "Invalid limit\n");
> + return EXIT_FAILURE;
> + }
> + }
> +
> + /* limit this to reasonable size */
> + if (size_limit < 4096) {
> + fprintf(stderr, "Too small limit value\n");
> + return EXIT_FAILURE;
> + }
> + break;
> + case 'c':
> + max_count = strtoul(optarg, &endptr, 10);
> + break;
> + case 'p':
> + if (getppid() != 1) {
> + fprintf(stderr, "Parents option allowed only "
> + "when running as a service\n");
> + return EXIT_FAILURE;
> + }
> +
> + parents = true;
> + break;
> + case 'v':
> + printf("%s\n", VERSION);
> + return EXIT_SUCCESS;
> + case 'h':
> + usage();
> + return EXIT_SUCCESS;
> + default:
> + return EXIT_FAILURE;
> + }
> + }
> +
> + if (argc - optind > 0) {
> + fprintf(stderr, "Invalid command line parameters\n");
> + return EXIT_FAILURE;
> + }
> +
> + if (!open_monitor_channel())
> + return EXIT_FAILURE;
> +
> + if (parents && create_dir(path) < 0)
> + return EXIT_FAILURE;
> +
> + btsnoop_file = btsnoop_create(path, size_limit, max_count,
> + BTSNOOP_FORMAT_MONITOR);
> + if (!btsnoop_file)
> + return EXIT_FAILURE;
> +
> + drop_capabilities();
> +
> + sigemptyset(&mask);
> + sigaddset(&mask, SIGINT);
> + sigaddset(&mask, SIGTERM);
> +
> + mainloop_set_signal(&mask, signal_callback, NULL, NULL);
> +
> + printf("Bluetooth monitor logger ver %s\n", VERSION);
> +
> + sd_notify(0, "STATUS=Running");
> + sd_notify(0, "READY=1");
> +
> + exit_status = mainloop_run();
> +
> + sd_notify(0, "STATUS=Quitting");
> +
> + btsnoop_unref(btsnoop_file);
> +
> + return exit_status;
> +}

Applied.

--
pozdrawiam
Szymon Janc