// Copyright 2024 Google LLC

//! The dump_modem binary is used to capture kernel/userspace logs in bugreport

use std::fs;

const MODEM_STAT: &str = "/data/vendor/modem_stat/debug.txt";
const MODEM_UART: &str = "/data/vendor/radio/modem_uart_logs.txt";
const SSRDUMP_DIR: &str = "/data/vendor/ssrdump";
const RFSD_ERR_LOG_DIR: &str = "/data/vendor/log/rfsd";
const WAKEUP_EVENTS: &str = "/sys/devices/platform/cpif/wakeup_events";
const CPIF_LOGBUFFER: &str = "/dev/logbuffer_cpif";
const PCIE_EVENT_STATS: &str = "/sys/devices/platform/cpif/modem/pcie_event_stats";

fn handle_io_error(file: &str, err: std::io::Error) {
    match err.kind() {
        std::io::ErrorKind::NotFound => println!("{file} not found!"),
        std::io::ErrorKind::PermissionDenied => println!("Permission denied to access {file}"),
        _ => println!("I/O error accessing {file}: {err}"),
    }
}

fn print_file(file: &str) -> Result<(), std::io::Error> {
    fs::metadata(file)?;

    let data = fs::read_to_string(file)?;

    if data.is_empty() {
        println!("{file} is empty");
    } else {
        print!("{data}");
    }

    Ok(())
}

fn print_file_and_handle_error(file: &str) {
    if let Err(err) = print_file(file) {
        handle_io_error(file, err);
    }
}

fn print_matching_files_in_dir(dir: &str, filename: &str) {
    let Ok(entries) = fs::read_dir(dir) else {
        return println!("Cannot open directory {dir}");
    };

    for entry in entries {
        let Ok(entry) = entry else {
            continue;
        };
        if entry.path().is_file() && entry.file_name().to_string_lossy().starts_with(filename) {
            if let Some(path_str) = entry.path().to_str() {
                println!("{}", path_str);
                print_file_and_handle_error(path_str);
            }
        }
    }
}

// Capture modem stat log if it exists
fn modem_stat() {
    println!("------ Modem Stat ------");
    print_file_and_handle_error(MODEM_STAT);
    println!();
}

// Capture modem uart log if it exists
fn modem_uart() {
    println!("------ Modem UART ------");
    print_file_and_handle_error(MODEM_UART);
    println!();
}

// Capture crash signatures from all modem crashes
fn modem_ssr_history() {
    println!("------ Modem SSR history ------");
    print_matching_files_in_dir(SSRDUMP_DIR, "crashinfo_modem");
    println!();
}

// Capture rfsd error logs from all existing log files
fn rfsd_error_log() {
    println!("------ RFSD error log ------");
    print_matching_files_in_dir(RFSD_ERR_LOG_DIR, "rfslog");
    println!();
}

// Capture modem wakeup events if the sysfs attribute exists
fn wakeup_events() {
    println!("------ Wakeup event counts ------");
    print_file_and_handle_error(WAKEUP_EVENTS);
    println!();
}

// Capture kernel driver logbuffer if it exists
fn cpif_logbuffer() {
    println!("------ CPIF Logbuffer ------");
    print_file_and_handle_error(CPIF_LOGBUFFER);
    println!();
}

// Capture modem pcie stats if the sysfs attribute exists
fn pcie_event_stats() {
    println!("------ PCIe event stats ------");
    print_file_and_handle_error(PCIE_EVENT_STATS);
    println!();
}

fn main() {
    modem_stat();
    modem_uart();
    modem_ssr_history();
    rfsd_error_log();
    wakeup_events();
    cpif_logbuffer();
    pcie_event_stats();
}
