Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb4cca25 authored by Zach Johnson's avatar Zach Johnson Committed by Gerrit Code Review
Browse files

Merge "rusty-gd: implement btsnoop"

parents addd3f7f cc66ba1f
Loading
Loading
Loading
Loading
+127 −17
Original line number Diff line number Diff line
@@ -3,8 +3,15 @@
use crate::internal::RawHalExports;
use crate::HalExports;
use bt_common::sys_prop;
use bt_packet::{HciCommand, HciEvent, RawPacket};
use bytes::{BufMut, Bytes, BytesMut};
use gddi::{module, provides, Stoppable};
use log::error;
use std::convert::TryFrom;
use std::sync::Arc;
use std::time::SystemTime;
use tokio::fs::{remove_file, rename, File};
use tokio::io::AsyncWriteExt;
use tokio::runtime::Runtime;
use tokio::select;
use tokio::sync::mpsc::{channel, UnboundedReceiver};
@@ -96,30 +103,30 @@ async fn provide_snooped_hal(
    hal_exports: RawHalExports,
    rt: Arc<Runtime>,
) -> HalExports {
    let (cmd_down_tx, mut cmd_down_rx) = channel(10);
    let (evt_up_tx, evt_up_rx) = channel(10);
    let (acl_down_tx, mut acl_down_rx) = channel(10);
    let (acl_up_tx, acl_up_rx) = channel(10);
    let (cmd_down_tx, mut cmd_down_rx) = channel::<HciCommand>(10);
    let (evt_up_tx, evt_up_rx) = channel::<HciEvent>(10);
    let (acl_down_tx, mut acl_down_rx) = channel::<RawPacket>(10);
    let (acl_up_tx, acl_up_rx) = channel::<RawPacket>(10);

    rt.spawn(async move {
        let logger = SnoopLogger::new(config);
        let mut logger = SnoopLogger::new(config).await;
        loop {
            select! {
                Some(evt) = consume(&hal_exports.evt_rx) => {
                    logger.log(Type::Evt, Direction::Up, &evt);
                    evt_up_tx.send(evt).await.unwrap();
                    evt_up_tx.send(evt.clone()).await.unwrap();
                    logger.log(Type::Evt, Direction::Up, evt).await;
                },
                Some(cmd) = cmd_down_rx.recv() => {
                    logger.log(Type::Cmd, Direction::Down, &cmd);
                    hal_exports.cmd_tx.send(cmd).unwrap();
                    hal_exports.cmd_tx.send(cmd.clone()).unwrap();
                    logger.log(Type::Cmd, Direction::Down, cmd).await;
                },
                Some(acl) = acl_down_rx.recv() => {
                    logger.log(Type::Acl, Direction::Down, &acl);
                    hal_exports.acl_tx.send(acl).unwrap();
                    hal_exports.acl_tx.send(acl.clone()).unwrap();
                    logger.log(Type::Acl, Direction::Down, acl).await;
                },
                Some(acl) = consume(&hal_exports.acl_rx) => {
                    logger.log(Type::Acl, Direction::Up, &acl);
                    acl_up_tx.send(acl).await.unwrap();
                    acl_up_tx.send(acl.clone()).await.unwrap();
                    logger.log(Type::Acl, Direction::Up, acl).await;
                }
            }
        }
@@ -151,12 +158,115 @@ enum Direction {
    Down,
}

struct SnoopLogger;
struct SnoopLogger {
    config: SnoopConfig,
    file: Option<File>,
    packets: u32,
}

// micros since 0000-01-01
const SNOOP_EPOCH_DELTA: u64 = 0x00dcddb30f2f8000;

impl SnoopLogger {
    fn new(_config: SnoopConfig) -> Self {
        Self {}
    async fn new(mut config: SnoopConfig) -> Self {
        // filtered snoop is not available at this time
        if let SnoopMode::Filtered = config.mode {
            config.mode = SnoopMode::Disabled;
        }

        remove_file(&config.path).await.ok();
        remove_file(config.path.clone() + ".last").await.ok();
        if let SnoopMode::Disabled = config.mode {
            remove_file(config.path.clone() + ".filtered").await.ok();
            remove_file(config.path.clone() + ".filtered.last")
                .await
                .ok();
        }

        let mut ret = Self {
            config,
            file: None,
            packets: 0,
        };
        ret.open_next_file().await;

        ret
    }

    async fn log(&mut self, t: Type, dir: Direction, bytes: Bytes) {
        if let SnoopMode::Disabled = self.config.mode {
            return;
        }

        let mut flags = 0;
        if let Direction::Up = dir {
            flags |= 0b01;
        }
        if let Type::Cmd | Type::Evt = t {
            flags |= 0b10;
        }

        let timestamp: u64 = u64::try_from(
            SystemTime::now()
                .duration_since(SystemTime::UNIX_EPOCH)
                .unwrap()
                .as_micros(),
        )
        .unwrap()
            + SNOOP_EPOCH_DELTA;

        // Add one for the type byte
        let length = u32::try_from(bytes.len()).unwrap() + 1;

        let mut buffer = BytesMut::new();
        buffer.put_u32(length); // original length
        buffer.put_u32(length); // captured length
        buffer.put_u32(flags); // flags
        buffer.put_u32(0); // dropped packets
        buffer.put_u64(timestamp); // timestamp
        buffer.put_u8(t as u8); // type
        buffer.put(bytes);

        self.packets += 1;
        if self.packets > self.config.max_packets_per_file {
            self.open_next_file().await;
        }

    fn log(&self, _t: Type, _dir: Direction, _bytes: &bytes::Bytes) {}
        if let Some(file) = &mut self.file {
            if file.write_all(&buffer).await.is_err() {
                error!("Failed to write");
            }
            if file.flush().await.is_err() {
                error!("Failed to flush");
            }
        } else {
            panic!("Logging without a backing file");
        }
    }

    async fn close_file(&mut self) {
        if let Some(file) = &mut self.file {
            file.flush().await.ok();
            self.file = None;
        }
        self.packets = 0;
    }

    async fn open_next_file(&mut self) {
        self.close_file().await;

        rename(&self.config.path, self.config.path.clone() + ".last")
            .await
            .ok();
        let mut file = File::create(&self.config.path)
            .await
            .expect("could not open snoop log");
        file.write_all(b"btsnoop\x00\x00\x00\x00\x01\x00\x00\x03\xea")
            .await
            .expect("could not write snoop header");
        if file.flush().await.is_err() {
            error!("Failed to flush");
        }
        self.file = Some(file);
    }
}