use clap::{Arg, ArgAction, Command}; use std::io::Write; mod engine; mod groups; mod parser; use crate::engine::RuleEngine; use crate::groups::{collisions, connections, controllers, informational}; use crate::parser::{LinuxSnoopOpcodes, LogParser, LogType, Packet}; fn main() { let matches = Command::new("hcidoc") .version("0.1") .author("Abhishek Pandit-Subedi ") .about("Analyzes a linux HCI snoop log for specific behaviors and errors.") .arg( Arg::new("filename") .help("Path to the snoop log. If omitted, read from stdin instead."), ) .arg( Arg::new("ignore-unknown") .long("ignore-unknown") .action(ArgAction::SetTrue) .help("Don't print warning for unknown opcodes"), ) .arg( Arg::new("signals") .short('s') .long("signals") .action(ArgAction::SetTrue) .help("Report signals from active rules."), ) .arg( Arg::new("signals-only") .long("signals-only") .action(ArgAction::SetTrue) .help("Only print signals from active rules, don't print other events."), ) .get_matches(); let filename = match matches.get_one::("filename") { Some(f) => f, None => "", }; let ignore_unknown_opcode = match matches.get_one::("ignore-unknown") { Some(v) => *v, None => false, }; let mut report_signals = match matches.get_one::("signals") { Some(v) => *v, None => false, }; let report_only_signals = match matches.get_one::("signals-only") { Some(v) => *v, None => false, }; if report_only_signals { report_signals = true; } let mut parser = match LogParser::new(filename) { Ok(p) => p, Err(e) => { println!( "Failed to load parser on {}: {}", if filename.len() == 0 { "stdin" } else { filename }, e ); return; } }; let log_type = match parser.read_log_type() { Ok(v) => v, Err(e) => { println!("Parsing {} failed: {}", filename, e); return; } }; // Create engine with default rule groups. let mut engine = RuleEngine::new(); engine.add_rule_group("Collisions".into(), collisions::get_collisions_group()); engine.add_rule_group("Connections".into(), connections::get_connections_group()); engine.add_rule_group("Controllers".into(), controllers::get_controllers_group()); engine.add_rule_group("Informational".into(), informational::get_informational_group()); // Decide where to write output. let mut writer: Box = Box::new(std::io::stdout()); if let LogType::LinuxSnoop(_header) = log_type { for (pos, v) in parser.get_snoop_iterator().expect("Not a linux snoop file").enumerate() { match Packet::try_from((pos, &v)) { Ok(p) => engine.process(p), Err(e) => { if !ignore_unknown_opcode { match v.opcode() { LinuxSnoopOpcodes::Command | LinuxSnoopOpcodes::Event => { eprintln!("#{}: {}", pos, e); } _ => (), } } } } } if !report_only_signals { engine.report(&mut writer); } if report_signals { let _ = writeln!(&mut writer, "### Signals ###"); engine.report_signals(&mut writer); } } }