1 use clap::{Arg, ArgAction, Command};
2 use std::io::Write;
3
4 mod engine;
5 mod groups;
6 mod parser;
7
8 use crate::engine::RuleEngine;
9 use crate::groups::{collisions, connections, controllers, informational};
10 use crate::parser::{LogParser, Packet, SnoopOpcodes};
11
main()12 fn main() {
13 let matches = Command::new("hcidoc")
14 .version("0.1")
15 .author("Abhishek Pandit-Subedi <abhishekpandit@google.com>")
16 .about("Analyzes a linux HCI snoop log for specific behaviors and errors.")
17 .arg(
18 Arg::new("filename")
19 .help("Path to the snoop log. If omitted, read from stdin instead."),
20 )
21 .arg(
22 Arg::new("ignore-unknown")
23 .long("ignore-unknown")
24 .action(ArgAction::SetTrue)
25 .help("Don't print warning for unknown opcodes"),
26 )
27 .arg(
28 Arg::new("signals")
29 .short('s')
30 .long("signals")
31 .action(ArgAction::SetTrue)
32 .help("Report signals from active rules."),
33 )
34 .arg(
35 Arg::new("signals-only")
36 .long("signals-only")
37 .action(ArgAction::SetTrue)
38 .help("Only print signals from active rules, don't print other events."),
39 )
40 .get_matches();
41
42 let filename = match matches.get_one::<String>("filename") {
43 Some(f) => f,
44 None => "",
45 };
46
47 let ignore_unknown_opcode = match matches.get_one::<bool>("ignore-unknown") {
48 Some(v) => *v,
49 None => false,
50 };
51
52 let mut report_signals = match matches.get_one::<bool>("signals") {
53 Some(v) => *v,
54 None => false,
55 };
56
57 let report_only_signals = match matches.get_one::<bool>("signals-only") {
58 Some(v) => *v,
59 None => false,
60 };
61
62 if report_only_signals {
63 report_signals = true;
64 }
65
66 let parser = match LogParser::new(filename) {
67 Ok(p) => p,
68 Err(e) => {
69 println!(
70 "Failed to load parser on {}: {}",
71 if filename.len() == 0 { "stdin" } else { filename },
72 e
73 );
74 return;
75 }
76 };
77
78 // Create engine with default rule groups.
79 let mut engine = RuleEngine::new();
80 engine.add_rule_group("Collisions".into(), collisions::get_collisions_group());
81 engine.add_rule_group("Connections".into(), connections::get_connections_group());
82 engine.add_rule_group("Controllers".into(), controllers::get_controllers_group());
83 engine.add_rule_group("Informational".into(), informational::get_informational_group());
84
85 // Decide where to write output.
86 let mut writer: Box<dyn Write> = Box::new(std::io::stdout());
87
88 for (pos, v) in parser.get_snoop_iterator().enumerate() {
89 match Packet::try_from((pos, &*v)) {
90 Ok(p) => engine.process(p),
91 Err(e) => {
92 if !ignore_unknown_opcode {
93 match v.opcode() {
94 SnoopOpcodes::Command | SnoopOpcodes::Event => {
95 eprintln!("#{}: {}", pos, e);
96 }
97 _ => (),
98 }
99 }
100 }
101 }
102 }
103
104 if !report_only_signals {
105 engine.report(&mut writer);
106 }
107 if report_signals {
108 let _ = writeln!(&mut writer, "### Signals ###");
109 engine.report_signals(&mut writer);
110 }
111 }
112