From: Szymon Janc Date: Fri, 19 Jan 2018 15:23:20 +0000 (+0100) Subject: tools: Add initial code for btmon-logger X-Git-Tag: accepted/tizen/unified/20190522.085452~1^2~12 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F70%2F205070%2F1;p=platform%2Fupstream%2Fbluez.git 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. Change-Id: If61c91abab4b1ef40b276e76acc764ca28e40deb Signed-off-by: Amit Purwar --- diff --git a/Makefile.tools b/Makefile.tools index cabede6..e07fd07 100755 --- a/Makefile.tools +++ b/Makefile.tools @@ -68,6 +68,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 4b09663..8d9a2d0 100755 --- 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 658eef2..b14b455 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 098d9d3..947c79d 100755 --- a/configure.ac +++ b/configure.ac @@ -365,6 +365,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 8a9b250..ebdd336 100755 --- a/monitor/control.c +++ b/monitor/control.c @@ -1381,7 +1381,7 @@ bool control_writer(const char *path) btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR, rotate_count, file_size); #else - btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR); + btsnoop_file = btsnoop_create(path, 0, 0, BTSNOOP_FORMAT_MONITOR); #endif return !!btsnoop_file; diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c index d111e15..9ee5431 100755 --- a/src/shared/btsnoop.c +++ b/src/shared/btsnoop.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -67,21 +69,37 @@ struct pklg_pkt { } __attribute__ ((packed)); #define PKLG_PKT_SIZE (sizeof(struct pklg_pkt)) -struct btsnoop { - int ref_count; - int fd; - unsigned long flags; - uint32_t format; - uint16_t index; - bool aborted; - bool pklg_format; - bool pklg_v2; #ifdef TIZEN_FEATURE_BLUEZ_MODIFY - char *path; - int16_t rotate_count; - ssize_t file_size; -#endif +struct btsnoop { + int ref_count; + int fd; + unsigned long flags; + uint32_t format; + uint16_t index; + bool aborted; + bool pklg_format; + bool pklg_v2; + char *path; + int16_t rotate_count; + ssize_t file_size; }; +#else +struct btsnoop { + int ref_count; + int fd; + unsigned long flags; + uint32_t format; + uint16_t index; + 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; +}; +#endif struct btsnoop *btsnoop_open(const char *path, unsigned long flags) { @@ -142,9 +160,6 @@ failed: #ifdef TIZEN_FEATURE_BLUEZ_MODIFY struct btsnoop *btsnoop_create(const char *path, uint32_t format, int16_t rotate_count, ssize_t file_size) -#else -struct btsnoop *btsnoop_create(const char *path, uint32_t format) -#endif { struct btsnoop *btsnoop; struct btsnoop_hdr hdr; @@ -155,7 +170,7 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t format) return NULL; btsnoop->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (btsnoop->fd < 0) { free(btsnoop); return NULL; @@ -175,16 +190,68 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t format) return NULL; } -#ifdef TIZEN_FEATURE_BLUEZ_MODIFY if (rotate_count > 0 && file_size > 0) { btsnoop->path = strdup(path); btsnoop->rotate_count = rotate_count; btsnoop->file_size = file_size; } -#endif return btsnoop_ref(btsnoop); } +#else +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; + + /* 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); + return NULL; + } + + 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); + hdr.type = htobe32(btsnoop->format); + + written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE); + if (written < 0) { + close(btsnoop->fd); + free(btsnoop); + return NULL; + } + + btsnoop->cur_size = BTSNOOP_HDR_SIZE; + + return btsnoop_ref(btsnoop); +} +#endif #ifdef TIZEN_FEATURE_BLUEZ_MODIFY static int btsnoop_create_2(struct btsnoop *btsnoop) @@ -305,9 +372,48 @@ uint32_t btsnoop_get_format(struct btsnoop *btsnoop) return btsnoop->format; } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY +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; +} +#endif + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, - uint32_t flags, uint32_t drops, const void *data, - uint16_t size) + uint32_t flags, uint32_t drops, const void *data, + uint16_t size) { struct btsnoop_pkt pkt; uint64_t ts; @@ -324,7 +430,6 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, pkt.drops = htobe32(drops); pkt.ts = htobe64(ts + 0x00E03AB44A676000ll); -#ifdef TIZEN_FEATURE_BLUEZ_MODIFY if ((btsnoop->rotate_count > 0 && btsnoop->file_size > 0) && lseek(btsnoop->fd, 0x00, SEEK_CUR) + BTSNOOP_PKT_SIZE + size > btsnoop->file_size) { @@ -332,7 +437,6 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, if (btsnoop_create_2(btsnoop) < 0) return false; } -#endif written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE); if (written < 0) @@ -346,6 +450,48 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, return true; } +#else +bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, + uint32_t flags, uint32_t drops, const void *data, + uint16_t size) +{ + struct btsnoop_pkt pkt; + uint64_t ts; + ssize_t written; + + 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); + pkt.len = htobe32(size); + pkt.flags = htobe32(flags); + pkt.drops = htobe32(drops); + pkt.ts = htobe64(ts + 0x00E03AB44A676000ll); + + written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE); + 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; +} +#endif static uint32_t get_flags_from_opcode(uint16_t opcode) { diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h index 25d1af1..f3a9301 100755 --- a/src/shared/btsnoop.h +++ b/src/shared/btsnoop.h @@ -103,7 +103,8 @@ struct btsnoop *btsnoop_open(const char *path, unsigned long flags); struct btsnoop *btsnoop_create(const char *path, uint32_t format, int16_t rotate_count, ssize_t file_size); #else -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); #endif struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop); diff --git a/tools/bluetooth-logger.service.in b/tools/bluetooth-logger.service.in new file mode 100644 index 0000000..210bf59 --- /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 0000000..c3ba179 --- /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 + * + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 Save traces in specified path\n" + "\t-p, --parents Create basename parent directories\n" + "\t-l, --limit Limit traces file size (rotate)\n" + "\t-c, --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; +}