tools: Add initial code for btmon-logger 70/205070/1
authorSzymon Janc <szymon.janc@codecoup.pl>
Fri, 19 Jan 2018 15:23:20 +0000 (16:23 +0100)
committerAmit Purwar <amit.purwar@samsung.com>
Mon, 29 Apr 2019 04:20:13 +0000 (09:50 +0530)
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 <amit.purwar@samsung.com>
Makefile.tools
android/bluetoothd-snoop.c
bootstrap-configure
configure.ac
monitor/control.c
src/shared/btsnoop.c
src/shared/btsnoop.h
tools/bluetooth-logger.service.in [new file with mode: 0644]
tools/btmon-logger.c [new file with mode: 0644]

index cabede6..e07fd07 100755 (executable)
@@ -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 \
index 4b09663..8d9a2d0 100755 (executable)
@@ -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;
 
index 658eef2..b14b455 100755 (executable)
@@ -24,4 +24,5 @@ fi
                --enable-sixaxis \
                --enable-midi \
                --enable-mesh \
+               --enable-logger \
                --disable-datafiles $*
index 098d9d3..947c79d 100755 (executable)
@@ -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
index 8a9b250..ebdd336 100755 (executable)
@@ -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;
index d111e15..9ee5431 100755 (executable)
@@ -33,6 +33,8 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdio.h>
+#include <limits.h>
 #include <arpa/inet.h>
 #include <sys/stat.h>
 
@@ -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)
 {
index 25d1af1..f3a9301 100755 (executable)
@@ -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 (file)
index 0000000..210bf59
--- /dev/null
@@ -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 (file)
index 0000000..c3ba179
--- /dev/null
@@ -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 <marcel@holtmann.org>
+ *
+ *
+ *  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;
+}