• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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