| // SPDX-License-Identifier: LGPL-2.1-or-later |
| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2018 Intel Corporation. All rights reserved. |
| * |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #define _GNU_SOURCE |
| #include <stdio.h> |
| #include <errno.h> |
| #include <inttypes.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <sys/socket.h> |
| |
| #include "bluetooth/bluetooth.h" |
| #include "bluetooth/hci.h" |
| |
| #include "src/shared/util.h" |
| #include "src/shared/log.h" |
| |
| struct log_hdr { |
| uint16_t opcode; |
| uint16_t index; |
| uint16_t len; |
| uint8_t priority; |
| uint8_t ident_len; |
| } __attribute__((packed)); |
| |
| struct log_l2cap_hdr { |
| uint16_t cid; |
| uint16_t psm; |
| } __attribute__((packed)); |
| |
| static int log_fd = -1; |
| |
| int bt_log_sendmsg(uint16_t index, const char *label, int level, |
| struct iovec *io, size_t io_len) |
| { |
| struct log_hdr hdr; |
| struct msghdr msg; |
| struct iovec iov[5]; |
| size_t i; |
| int err; |
| |
| if (io_len > 3) |
| return -EMSGSIZE; |
| |
| log_fd = bt_log_open(); |
| if (log_fd < 0) |
| return log_fd; |
| |
| hdr.opcode = cpu_to_le16(0x0000); |
| hdr.index = cpu_to_le16(index); |
| hdr.ident_len = strlen(label) + 1; |
| hdr.len = cpu_to_le16(2 + hdr.ident_len); |
| hdr.priority = level; |
| |
| iov[0].iov_base = &hdr; |
| iov[0].iov_len = sizeof(hdr); |
| |
| iov[1].iov_base = (void *) label; |
| iov[1].iov_len = hdr.ident_len; |
| |
| memset(&msg, 0, sizeof(msg)); |
| msg.msg_iov = iov; |
| msg.msg_iovlen = 2; |
| |
| for (i = 0; i < io_len; i++) { |
| iov[i + 2] = io[i]; |
| hdr.len += io[i].iov_len; |
| msg.msg_iovlen++; |
| } |
| |
| err = sendmsg(log_fd, &msg, 0); |
| if (err < 0) { |
| err = -errno; |
| close(log_fd); |
| log_fd = -1; |
| } |
| |
| return err; |
| } |
| |
| int bt_log_open(void) |
| { |
| struct sockaddr_hci addr; |
| int fd; |
| static int err; |
| |
| if (err < 0) |
| return err; |
| |
| if (log_fd >= 0) |
| return log_fd; |
| |
| fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); |
| if (fd < 0) { |
| err = -errno; |
| return -errno; |
| } |
| |
| memset(&addr, 0, sizeof(addr)); |
| addr.hci_family = AF_BLUETOOTH; |
| addr.hci_dev = HCI_DEV_NONE; |
| addr.hci_channel = HCI_CHANNEL_LOGGING; |
| |
| err = bind(fd, (struct sockaddr *) &addr, sizeof(addr)); |
| if (err < 0) { |
| err = -errno; |
| close(fd); |
| return err; |
| } |
| |
| log_fd = fd; |
| |
| return fd; |
| } |
| |
| int bt_log_vprintf(uint16_t index, const char *label, int level, |
| const char *format, va_list ap) |
| { |
| struct iovec iov; |
| char *str; |
| int len; |
| |
| len = vasprintf(&str, format, ap); |
| if (len < 0 || !str) |
| return errno; |
| |
| len = strlen(str); |
| |
| /* Replace new line since btmon already adds it */ |
| if (len > 1 && str[len - 1] == '\n') { |
| str[len - 1] = '\0'; |
| len--; |
| } |
| |
| iov.iov_base = str; |
| iov.iov_len = len + 1; |
| |
| len = bt_log_sendmsg(index, label, level, &iov, 1); |
| |
| free(str); |
| |
| return len; |
| } |
| |
| int bt_log_printf(uint16_t index, const char *label, int level, |
| const char *format, ...) |
| { |
| va_list ap; |
| int err; |
| |
| va_start(ap, format); |
| err = bt_log_vprintf(index, label, level, format, ap); |
| va_end(ap); |
| |
| return err; |
| } |
| |
| void bt_log_close(void) |
| { |
| if (log_fd < 0) |
| return; |
| |
| close(log_fd); |
| log_fd = -1; |
| } |