WIP
This commit is contained in:
parent
d030beb00b
commit
4f1f1ba88e
35
Cargo.lock
generated
35
Cargo.lock
generated
@ -1,3 +1,5 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.9"
|
||||
@ -15,6 +17,11 @@ dependencies = [
|
||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.28"
|
||||
@ -216,16 +223,20 @@ dependencies = [
|
||||
"getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"globset 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_yaml 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
@ -248,6 +259,16 @@ dependencies = [
|
||||
"yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simplelog"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.26"
|
||||
@ -258,6 +279,15 @@ dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.6"
|
||||
@ -326,6 +356,7 @@ dependencies = [
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
|
||||
"checksum blurz 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1bc2df4d0f1d373b324672d9966aca3f5ba1f03d69edad6240144774539ea59"
|
||||
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
|
||||
"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749"
|
||||
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
|
||||
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
|
||||
@ -355,7 +386,9 @@ dependencies = [
|
||||
"checksum serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)" = "534b8b91a95e0f71bca3ed5824752d558da048d4248c91af873b63bd60519752"
|
||||
"checksum serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)" = "a915306b0f1ac5607797697148c223bedeaa36bcc2e28a01441cd638cc6567b4"
|
||||
"checksum serde_yaml 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0887a8e097a69559b56aa2526bf7aff7c3048cf627dff781f0b56a6001534593"
|
||||
"checksum simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e95345f185d5adeb8ec93459d2dc99654e294cc6ccf5b75414d8ea262de9a13"
|
||||
"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9"
|
||||
"checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
|
||||
|
@ -6,11 +6,12 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libloading = "0.5.0"
|
||||
serde = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_yaml = "0.8"
|
||||
serde_derive = "1.0"
|
||||
getopts = "0.2"
|
||||
blurz = "0.4.0"
|
||||
regex = "1"
|
||||
chrono = "0.4"
|
||||
globset = "0.3"
|
||||
log = "0.4"
|
||||
simplelog = "0.5"
|
@ -1,8 +0,0 @@
|
||||
use serde_derive::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub monitors: Vec<HashMap<String, serde_yaml::Value>>,
|
||||
pub outputs: Vec<HashMap<String, serde_yaml::Value>>,
|
||||
}
|
83
src/filter.rs
Normal file
83
src/filter.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use globset::{Glob, GlobMatcher};
|
||||
|
||||
pub use std::collections::HashMap;
|
||||
|
||||
use log::{info, trace, warn};
|
||||
|
||||
use crate::message::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Filter {
|
||||
level: Level,
|
||||
include_types: Vec<GlobMatcher>,
|
||||
exclude_types: Vec<GlobMatcher>,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn new(config: &HashMap<String, serde_yaml::Value>) -> Self {
|
||||
let filter_node = config
|
||||
.get("filter")
|
||||
.unwrap_or(&serde_yaml::Mapping::new().into())
|
||||
.clone();
|
||||
|
||||
let include_types = match filter_node.get("include_types") {
|
||||
Some(v) => v
|
||||
.as_sequence()
|
||||
.expect("include_types must be a list of strings")
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
Glob::new(x.as_str().expect("filter must be a list of strings"))
|
||||
.expect("Invalid glob pattern")
|
||||
.compile_matcher()
|
||||
})
|
||||
.collect(),
|
||||
None => vec![],
|
||||
};
|
||||
let exclude_types = match filter_node.get("exclude_types") {
|
||||
Some(v) => v
|
||||
.as_sequence()
|
||||
.expect("exclude_types must be a list of strings")
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
Glob::new(x.as_str().expect("filter must be a list of strings"))
|
||||
.expect("Invalid glob pattern")
|
||||
.compile_matcher()
|
||||
})
|
||||
.collect(),
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
let level = match filter_node.get("level") {
|
||||
Some(l) => Level::try_from(l.as_str().expect("Level must be a string"))
|
||||
.expect("Unknown level name"),
|
||||
None => Level::Notice,
|
||||
};
|
||||
|
||||
Filter {
|
||||
level: level,
|
||||
include_types: include_types,
|
||||
exclude_types: exclude_types,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_message_allowed(&self, message: &Message) -> bool {
|
||||
if message.level < self.level {
|
||||
return false;
|
||||
}
|
||||
for exc in &self.exclude_types {
|
||||
if exc.is_match(&message.msg_type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if self.include_types.len() > 0 {
|
||||
for exc in &self.include_types {
|
||||
if exc.is_match(&message.msg_type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
107
src/main.rs
107
src/main.rs
@ -1,25 +1,37 @@
|
||||
mod config;
|
||||
#![feature(unrestricted_attribute_tokens)]
|
||||
#![feature(custom_attribute)]
|
||||
#![feature(try_from)]
|
||||
|
||||
mod filter;
|
||||
mod message;
|
||||
mod monitor;
|
||||
mod monitors;
|
||||
mod output;
|
||||
mod plugins;
|
||||
mod outputs;
|
||||
|
||||
extern crate getopts;
|
||||
extern crate serde_derive;
|
||||
extern crate globset;
|
||||
extern crate log;
|
||||
extern crate serde_yaml;
|
||||
extern crate simplelog;
|
||||
|
||||
use getopts::Options;
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
use getopts::Options;
|
||||
|
||||
use crate::config::*;
|
||||
use crate::filter::*;
|
||||
use crate::message::*;
|
||||
use crate::monitor::*;
|
||||
use crate::output::*;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct AppConfig {
|
||||
pub monitors: Vec<HashMap<String, serde_yaml::Value>>,
|
||||
pub outputs: Vec<HashMap<String, serde_yaml::Value>>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
@ -33,6 +45,8 @@ fn main() {
|
||||
"config.yaml",
|
||||
);
|
||||
opts.optflag("h", "help", "print this help menu");
|
||||
opts.optflag("v", "verbose", "Switch to DEBUG output");
|
||||
opts.optflag("q", "quiet", "Switch to WARNING output");
|
||||
|
||||
let matches = match opts.parse(&args[1..]) {
|
||||
Ok(m) => m,
|
||||
@ -44,6 +58,30 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
let level;
|
||||
if matches.opt_present("v") && matches.opt_present("q") {
|
||||
panic!("-v and -q are mutually exclusive");
|
||||
}
|
||||
if matches.opt_present("v") {
|
||||
level = simplelog::LevelFilter::Debug;
|
||||
} else if matches.opt_present("q") {
|
||||
level = simplelog::LevelFilter::Warn;
|
||||
} else {
|
||||
level = simplelog::LevelFilter::Info;
|
||||
}
|
||||
|
||||
simplelog::TermLogger::init(
|
||||
level,
|
||||
simplelog::Config {
|
||||
time: Some(simplelog::Level::Debug),
|
||||
level: Some(simplelog::Level::Error),
|
||||
target: Some(simplelog::Level::Info),
|
||||
location: Some(simplelog::Level::Debug),
|
||||
time_format: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Parse config
|
||||
let config_path = matches.opt_str("c").unwrap_or("./config.yaml".to_owned());
|
||||
|
||||
@ -52,7 +90,7 @@ fn main() {
|
||||
config_file
|
||||
.read_to_string(&mut config_content)
|
||||
.expect("Could not read configuration file");
|
||||
let config: Config =
|
||||
let config: AppConfig =
|
||||
serde_yaml::from_str(&config_content).expect("Invalid config file content");
|
||||
|
||||
// Monitors -> dispatcher channel
|
||||
@ -60,40 +98,53 @@ fn main() {
|
||||
|
||||
// Instantiate monitor threads and structs
|
||||
for mon_config in config.monitors {
|
||||
let name = mon_config
|
||||
.get("name")
|
||||
.expect("Missing `name` key for monitor")
|
||||
let mon_type = mon_config
|
||||
.get("type")
|
||||
.expect("Missing `type` key for monitor")
|
||||
.as_str()
|
||||
.expect("Key `name` for monitor is not a string");
|
||||
|
||||
println!("Loading monitor: {}", name);
|
||||
.expect("Key `type` for monitor is not a string")
|
||||
.to_owned();
|
||||
|
||||
let snd = mon_sender.clone();
|
||||
let mut mon = plugins::monitor::factory(name, &mon_config);
|
||||
thread::spawn(move || loop {
|
||||
mon.run(&snd);
|
||||
|
||||
thread::spawn(move || {
|
||||
log::info!("+> monitor: {}", mon_type);
|
||||
match monitors::factory(&mon_type, &mon_config) {
|
||||
Ok(mut mon) => loop {
|
||||
mon.run(&snd);
|
||||
},
|
||||
Err(e) => log::error!("Cannot instantiate monitor {}: {}", mon_type, e),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Instantiate output threads and structs
|
||||
// let mut outputs: Vec<(Box<Output>, Filter)> = vec![];
|
||||
let mut output_senders: Vec<mpsc::Sender<Message>> = vec![];
|
||||
for out_config in config.outputs {
|
||||
let name = out_config
|
||||
.get("name")
|
||||
.expect("Missing `name` key for monitor")
|
||||
let out_type = out_config
|
||||
.get("type")
|
||||
.expect("Missing `type` key for output")
|
||||
.as_str()
|
||||
.expect("Key `name` for monitor is not a string");
|
||||
|
||||
println!("Loading output: {}", name);
|
||||
.expect("Key `type` for output is not a string")
|
||||
.to_owned();
|
||||
|
||||
let (out_sender, out_receiver) = mpsc::channel();
|
||||
output_senders.push(out_sender);
|
||||
|
||||
let mut output = plugins::output::factory(name, &out_config);
|
||||
let filter = Filter::new(&out_config);
|
||||
|
||||
thread::spawn(move || loop {
|
||||
let message = out_receiver.recv().unwrap();
|
||||
output.process_message(message);
|
||||
thread::spawn(move || {
|
||||
log::info!("+> output: {}", out_type);
|
||||
match outputs::factory(&out_type, &out_config) {
|
||||
Ok(mut output) => loop {
|
||||
let message = out_receiver.recv().unwrap();
|
||||
if filter.is_message_allowed(&message) {
|
||||
output.process_message(message);
|
||||
}
|
||||
},
|
||||
Err(e) => log::error!("Cannot instantiate output {}: {}", out_type, e),
|
||||
}
|
||||
});
|
||||
}
|
||||
let output_senders = output_senders;
|
||||
|
@ -1,10 +1,33 @@
|
||||
#[derive(Debug, Clone)]
|
||||
use serde::Deserialize;
|
||||
pub use std::convert::TryFrom;
|
||||
|
||||
// pub type Level = u8;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Hash, Deserialize)]
|
||||
pub enum Level {
|
||||
Debug = 0,
|
||||
Info = 10,
|
||||
Warning = 20,
|
||||
Error = 30,
|
||||
Critical = 40,
|
||||
Notice = 1,
|
||||
Anomaly = 2,
|
||||
Issue = 3,
|
||||
Critical = 4,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Level {
|
||||
type Error = ();
|
||||
fn try_from(level: &str) -> Result<Self, ()> {
|
||||
match serde_yaml::from_str(level) {
|
||||
Ok(v) => v,
|
||||
_ => Err(()),
|
||||
}
|
||||
// match level {
|
||||
// "Debug" => Ok(Level::Debug),
|
||||
// "Notice" => Ok(Level::Notice),
|
||||
// "Anomaly" => Ok(Level::Anomaly),
|
||||
// "Issue" => Ok(Level::Issue),
|
||||
// "Critical" => Ok(Level::Critical),
|
||||
// _ => Err(()),
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -4,7 +4,7 @@ pub use std::collections::HashMap;
|
||||
pub use std::sync::mpsc;
|
||||
|
||||
pub trait Monitor {
|
||||
fn new(config: &HashMap<String, serde_yaml::Value>) -> Self
|
||||
fn new(config: &HashMap<String, serde_yaml::Value>) -> Result<Self, Box<dyn std::error::Error>>
|
||||
where
|
||||
Self: Sized;
|
||||
fn run(&mut self, sender: &mpsc::Sender<Message>);
|
||||
|
@ -1,6 +1,6 @@
|
||||
pub use crate::message::*;
|
||||
pub use crate::monitor::*;
|
||||
use serde_derive::*;
|
||||
use serde::Deserialize;
|
||||
|
||||
extern crate regex;
|
||||
use regex::Regex;
|
||||
@ -15,39 +15,49 @@ pub struct DHCPLeases {
|
||||
rgx_date_start: Regex,
|
||||
rgx_date_ends: Regex,
|
||||
}
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
struct DHCPLeasesConfig {
|
||||
#[serde(default)]
|
||||
path: String,
|
||||
mac_rules: HashMap<String, Vec<String>>,
|
||||
#[serde(default)]
|
||||
mac_rules: HashMap<String, Option<Vec<String>>>,
|
||||
}
|
||||
|
||||
impl Monitor for DHCPLeases {
|
||||
fn new(config: &HashMap<String, serde_yaml::Value>) -> Self {
|
||||
let config_node = config.get("config").expect("Missing `config` key").clone();
|
||||
let config =
|
||||
serde_yaml::from_value(config_node).expect("Invalid config for wifi_availability");
|
||||
fn new(
|
||||
config: &HashMap<String, serde_yaml::Value>,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let config_node = config
|
||||
.get("config")
|
||||
.unwrap_or(&serde_yaml::Mapping::new().into())
|
||||
.clone();
|
||||
let config = serde_yaml::from_value(config_node).expect("Invalid config for dhcp_leases");
|
||||
|
||||
// Regex compilation
|
||||
let rgx_lease = Regex::new(r"(?s)lease\s+(\d+(?:\.\d+){3})\s*\{\n?(.*?)\}").unwrap();
|
||||
let rgx_mac =
|
||||
Regex::new(r"^\s*hardware\s+ethernet\s([a-f0-9]{2}(?:\:[a-f0-9]{2}){5})\s*;").unwrap();
|
||||
Regex::new(r"^\s*hardware\s+ethernet\s([a-f0-9]{2}(?::[a-f0-9]{2}){5})\s*;").unwrap();
|
||||
let rgx_date_start = Regex::new(r"^\s*starts\s+(.*?)\s*;").unwrap();
|
||||
let rgx_date_ends = Regex::new(r"^\s*ends\s+(.*?)\s*;").unwrap();
|
||||
|
||||
DHCPLeases {
|
||||
Ok(DHCPLeases {
|
||||
config: config,
|
||||
rgx_lease: rgx_lease,
|
||||
rgx_mac: rgx_mac,
|
||||
rgx_date_start: rgx_date_start,
|
||||
rgx_date_ends: rgx_date_ends,
|
||||
}
|
||||
})
|
||||
}
|
||||
fn run(&mut self, sender: &mpsc::Sender<Message>) {
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
|
||||
let config_file_path = match self.config.path.len() {
|
||||
0 => "/var/lib/dhcp/dhcpd.leases",
|
||||
_ => &self.config.path,
|
||||
};
|
||||
let mut config_file =
|
||||
File::open(&self.config.path).expect("Could not open DHCP leases file");
|
||||
File::open(config_file_path).expect("Could not open DHCP leases file");
|
||||
let mut config_content = String::new();
|
||||
config_file
|
||||
.read_to_string(&mut config_content)
|
||||
@ -91,19 +101,26 @@ impl Monitor for DHCPLeases {
|
||||
if starts <= now && now < ends {
|
||||
// Lease is active
|
||||
if let Some(rules) = self.config.mac_rules.get(mac) {
|
||||
for rule in rules {
|
||||
if content.find(rule).is_none() {
|
||||
unauthorized_macs.push(mac.to_owned());
|
||||
// Found rules
|
||||
if let Some(rules_list) = rules {
|
||||
// Rules contains one or more entries
|
||||
for rule in rules_list {
|
||||
if content.find(rule).is_none() {
|
||||
unauthorized_macs.push(mac.to_owned());
|
||||
|
||||
sender
|
||||
.send(Message {
|
||||
emitter: "dhcp_leases".to_owned(),
|
||||
level: Level::Error,
|
||||
msg_type: "dhcp_leases.unauthorized_mac.rule".to_owned(),
|
||||
text: format!("Mismatching rule '{}' for device {}", rule, mac),
|
||||
})
|
||||
.unwrap();
|
||||
break;
|
||||
sender
|
||||
.send(Message {
|
||||
emitter: "dhcp_leases".to_owned(),
|
||||
level: Level::Issue,
|
||||
msg_type: "dhcp_leases.unauthorized_mac.rule".to_owned(),
|
||||
text: format!(
|
||||
"Mismatching rule '{}' for device {}",
|
||||
rule, mac
|
||||
),
|
||||
})
|
||||
.unwrap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -112,7 +129,7 @@ impl Monitor for DHCPLeases {
|
||||
sender
|
||||
.send(Message {
|
||||
emitter: "dhcp_leases".to_owned(),
|
||||
level: Level::Error,
|
||||
level: Level::Issue,
|
||||
msg_type: "dhcp_leases.unauthorized_mac.unknown".to_owned(),
|
||||
text: format!("Unauthorized device on network: {}", mac),
|
||||
})
|
||||
@ -121,7 +138,19 @@ impl Monitor for DHCPLeases {
|
||||
}
|
||||
}
|
||||
|
||||
if unauthorized_macs.len() > 0 {}
|
||||
if unauthorized_macs.len() > 0 {
|
||||
sender
|
||||
.send(Message {
|
||||
emitter: "dhcp_leases".to_owned(),
|
||||
level: Level::Issue,
|
||||
msg_type: "dhcp_leases.unknown_mac".to_owned(),
|
||||
text: format!(
|
||||
"The following macs are not allowed: {:?}",
|
||||
unauthorized_macs
|
||||
),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// let leases: Vec<(&str, &str)> = lease_rgx
|
||||
// .captures_iter(&config_content)
|
75
src/monitors/dummy_sender.rs
Normal file
75
src/monitors/dummy_sender.rs
Normal file
@ -0,0 +1,75 @@
|
||||
pub use crate::message::{Level, Message};
|
||||
pub use crate::monitor::*;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DummySender {
|
||||
cnt: i64,
|
||||
config: DummySenderConfig,
|
||||
}
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct DummySenderConfig {
|
||||
#[serde(default)]
|
||||
message: Option<String>,
|
||||
#[serde(default)]
|
||||
level: Option<Level>,
|
||||
#[serde(default)]
|
||||
msg_type: Option<String>,
|
||||
#[serde(default)]
|
||||
period: Option<f64>,
|
||||
}
|
||||
|
||||
impl Monitor for DummySender {
|
||||
fn new(
|
||||
config: &HashMap<String, serde_yaml::Value>,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let config_node = config
|
||||
.get("config")
|
||||
.unwrap_or(&serde_yaml::Mapping::new().into())
|
||||
.clone();
|
||||
let config: DummySenderConfig =
|
||||
serde_yaml::from_value(config_node).expect("Invalid config for dummy_sender");
|
||||
|
||||
Ok(DummySender {
|
||||
cnt: 0,
|
||||
config: config,
|
||||
})
|
||||
}
|
||||
fn run(&mut self, sender: &mpsc::Sender<Message>) {
|
||||
log::info!(
|
||||
"Sending message {} from {:?}",
|
||||
self.cnt,
|
||||
std::thread::current().id()
|
||||
);
|
||||
sender
|
||||
.send(Message {
|
||||
emitter: "dummy_sender".to_owned(),
|
||||
level: match &self.config.level {
|
||||
Some(l) => l.clone(),
|
||||
None => Level::Notice,
|
||||
},
|
||||
msg_type: match &self.config.msg_type {
|
||||
Some(s) => s.to_owned(),
|
||||
None => "dummy_sender.message".to_owned(),
|
||||
},
|
||||
text: match &self.config.message {
|
||||
Some(s) => s.to_owned(),
|
||||
None => format!(
|
||||
"This is message {} from {:?}",
|
||||
self.cnt,
|
||||
std::thread::current().id()
|
||||
),
|
||||
},
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
self.cnt += 1;
|
||||
|
||||
let delay_ms = match self.config.period {
|
||||
Some(sec) => (sec * 1000.0) as u64,
|
||||
None => 2000,
|
||||
};
|
||||
std::thread::sleep(std::time::Duration::from_millis(delay_ms));
|
||||
}
|
||||
}
|
18
src/monitors/mod.rs
Normal file
18
src/monitors/mod.rs
Normal file
@ -0,0 +1,18 @@
|
||||
pub mod dhcp_leases;
|
||||
pub mod dummy_sender;
|
||||
pub mod wifi_availability;
|
||||
|
||||
use crate::monitor::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn factory(
|
||||
name: &str,
|
||||
config: &HashMap<String, serde_yaml::Value>,
|
||||
) -> Result<Box<Monitor>, Box<dyn std::error::Error>> {
|
||||
match name {
|
||||
"dummy_sender" => Ok(Box::new(dummy_sender::DummySender::new(&config)?)),
|
||||
"wifi_availability" => Ok(Box::new(wifi_availability::WifiAvailability::new(&config)?)),
|
||||
"dhcp_leases" => Ok(Box::new(dhcp_leases::DHCPLeases::new(&config)?)),
|
||||
_ => panic!("Unknown monitor name: {}", name),
|
||||
}
|
||||
}
|
3357
src/monitors/rnetmon.sublime-workspace
Normal file
3357
src/monitors/rnetmon.sublime-workspace
Normal file
File diff suppressed because one or more lines are too long
@ -1,22 +1,29 @@
|
||||
use crate::message::Message;
|
||||
use crate::message::{Level, Message};
|
||||
use crate::monitor::*;
|
||||
use serde_derive::*;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WifiAvailability {
|
||||
config: WifiAvailabilityConfig,
|
||||
}
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct WifiAvailabilityConfig {
|
||||
iface: String,
|
||||
ssid: String,
|
||||
#[serde(default)]
|
||||
essid: Vec<String>,
|
||||
#[serde(default)]
|
||||
ping_targets: Vec<String>,
|
||||
}
|
||||
|
||||
impl Monitor for WifiAvailability {
|
||||
fn new(config: &HashMap<String, serde_yaml::Value>) -> Self {
|
||||
let config_node = config.get("config").expect("Missing `config` key").clone();
|
||||
fn new(
|
||||
config: &HashMap<String, serde_yaml::Value>,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let config_node = config
|
||||
.get("config")
|
||||
.unwrap_or(&serde_yaml::Mapping::new().into())
|
||||
.clone();
|
||||
let config =
|
||||
serde_yaml::from_value(config_node).expect("Invalid config for wifi_availability");
|
||||
|
||||
@ -24,7 +31,7 @@ impl Monitor for WifiAvailability {
|
||||
// Activate iface
|
||||
//ip link set wlan0 up
|
||||
|
||||
wa
|
||||
Ok(wa)
|
||||
}
|
||||
fn run(&mut self, sender: &mpsc::Sender<Message>) {
|
||||
// iw dev wlan0 link: get is connected
|
||||
@ -43,7 +50,7 @@ impl Monitor for WifiAvailability {
|
||||
.send(Message {
|
||||
emitter: "wifi_availability".to_owned(),
|
||||
msg_type: "ping.failed".to_owned(),
|
||||
level: 10,
|
||||
level: Level::Issue,
|
||||
text: format!("Cannot ping {}", target),
|
||||
})
|
||||
.unwrap();
|
@ -1,52 +1,9 @@
|
||||
extern crate globset;
|
||||
|
||||
pub use std::collections::HashMap;
|
||||
|
||||
use globset::Glob;
|
||||
use std::iter::Map;
|
||||
|
||||
use crate::message::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Filter {
|
||||
level: Level,
|
||||
types: Vec<Glob>,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
fn new(config: &HashMap<String, serde_yaml::Value>) -> Self {
|
||||
let mut globs = vec![];
|
||||
|
||||
let types = config
|
||||
.get("types")
|
||||
.expect("Missing types key")
|
||||
.as_sequence()
|
||||
.expect("types must be a list of strings")
|
||||
.iter()
|
||||
.map(|x| {
|
||||
Glob::new(x.as_str().expect("filter must be a list of strings"))
|
||||
.expect("Invalid glob pattern")
|
||||
.compile_matcher()
|
||||
})
|
||||
.collect();
|
||||
|
||||
// let glob = Glob::new("*.rs")?.compile_matcher();
|
||||
Filter {
|
||||
level: config.get("level").to.unwrap_or(Level::Debug),
|
||||
types: types,
|
||||
}
|
||||
}
|
||||
|
||||
// fn is_message_filtered(&self, message: Message) -> bool {
|
||||
// if message.level < self.level{
|
||||
// return true
|
||||
// }
|
||||
// if
|
||||
// }
|
||||
}
|
||||
|
||||
pub trait Output {
|
||||
fn new(config: &HashMap<String, serde_yaml::Value>) -> Self
|
||||
fn new(config: &HashMap<String, serde_yaml::Value>) -> Result<Self, Box<dyn std::error::Error>>
|
||||
where
|
||||
Self: Sized;
|
||||
fn process_message(&mut self, message: Message);
|
||||
|
38
src/outputs/espeak.rs
Normal file
38
src/outputs/espeak.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use crate::message::Message;
|
||||
pub use crate::output::*;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Espeak {
|
||||
config: EspeakConfig,
|
||||
}
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct EspeakConfig {
|
||||
#[serde(default = "espeak".to_owned())]
|
||||
espeak: String,
|
||||
#[serde(default = vec![])]
|
||||
args: Vec<String>,
|
||||
}
|
||||
|
||||
impl Output for Espeak {
|
||||
fn new(
|
||||
config: &HashMap<String, serde_yaml::Value>,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let node = config
|
||||
.get("config")
|
||||
.unwrap_or(&serde_yaml::Value::Null)
|
||||
.clone();
|
||||
let cfg = serde_yaml::from_value(node).expect("Invalid config for Espeak");
|
||||
|
||||
Ok(Espeak { config: cfg })
|
||||
}
|
||||
fn process_message(&mut self, message: Message) {
|
||||
use std::process::{Command, Stdio};
|
||||
Command::new(&self.config.espeak)
|
||||
.args(&self.config.args)
|
||||
.arg(message.text)
|
||||
.stdout(Stdio::null())
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
}
|
||||
}
|
298
src/outputs/light_beewi_bbl227.rs
Normal file
298
src/outputs/light_beewi_bbl227.rs
Normal file
@ -0,0 +1,298 @@
|
||||
extern crate blurz;
|
||||
|
||||
use blurz::bluetooth_adapter::BluetoothAdapter;
|
||||
use blurz::bluetooth_device::BluetoothDevice;
|
||||
use blurz::bluetooth_gatt_characteristic::BluetoothGATTCharacteristic;
|
||||
use blurz::bluetooth_gatt_service::BluetoothGATTService;
|
||||
use blurz::bluetooth_session::BluetoothSession;
|
||||
use globset::{Glob, GlobMatcher};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::message::{Level, Message};
|
||||
pub use crate::output::*;
|
||||
|
||||
pub struct BluetoothLightbulb {
|
||||
config: BluetoothLightbulbConfig,
|
||||
session: BluetoothSession,
|
||||
levels: HashMap<Level, LightConfig>,
|
||||
msg_types: Vec<(GlobMatcher, LightConfig)>,
|
||||
}
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct BluetoothLightbulbConfig {
|
||||
#[serde(default)]
|
||||
mac: HashMap<String, ()>,
|
||||
#[serde(default)]
|
||||
levels: HashMap<Level, LightConfig>,
|
||||
#[serde(default)]
|
||||
msg_types: HashMap<String, LightConfig>,
|
||||
}
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
enum Animation {
|
||||
None,
|
||||
Smooth,
|
||||
Bounce,
|
||||
Blink,
|
||||
RampUp,
|
||||
RampDown,
|
||||
}
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct LightConfig {
|
||||
#[serde(default)]
|
||||
color: Option<u32>,
|
||||
#[serde(default)]
|
||||
animation: Option<Animation>,
|
||||
#[serde(default)]
|
||||
repeat: Option<u64>,
|
||||
#[serde(default)]
|
||||
speed: Option<f64>,
|
||||
}
|
||||
impl LightConfig {
|
||||
fn merge(&mut self, other: &Self) {
|
||||
if other.color.is_some() {
|
||||
self.color = other.color;
|
||||
}
|
||||
if other.animation.is_some() {
|
||||
self.animation = other.animation.clone();
|
||||
}
|
||||
if other.repeat.is_some() {
|
||||
self.repeat = other.repeat;
|
||||
}
|
||||
if other.speed.is_some() {
|
||||
self.speed = other.speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl BluetoothLightbulb {
|
||||
fn set_color(&self, characs: &Vec<BluetoothGATTCharacteristic>, r: f64, g: f64, b: f64) {
|
||||
let r = (r * 255.0) as u8;
|
||||
let g = (g * 255.0) as u8;
|
||||
let b = (b * 255.0) as u8;
|
||||
|
||||
// println!("r={} g={} b={}", r, g, b);
|
||||
for charac in characs {
|
||||
charac
|
||||
.write_value(vec![0x55, 0x13, r, g, b, '\r' as u8, '\n' as u8], None)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn initiate_scan(&self, adapter: &BluetoothAdapter) {
|
||||
if !adapter.is_discovering().unwrap() {
|
||||
log::info!("bluetooth adapter {}: Set scan ON", adapter.get_id());
|
||||
adapter
|
||||
.start_discovery()
|
||||
.expect("Cannot activate scanning on bluetooth device");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Output for BluetoothLightbulb {
|
||||
fn new(
|
||||
config: &HashMap<String, serde_yaml::Value>,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let config_node = config
|
||||
.get("config")
|
||||
.unwrap_or(&serde_yaml::Mapping::new().into())
|
||||
.clone();
|
||||
let config: BluetoothLightbulbConfig =
|
||||
serde_yaml::from_value(config_node).expect("Invalid config for light_beewi_bbl227");
|
||||
|
||||
let msg_types = config
|
||||
.msg_types
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
Glob::new(k)
|
||||
.expect("Invalid glob pattern")
|
||||
.compile_matcher(),
|
||||
v.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let levels = config
|
||||
.levels
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect();
|
||||
|
||||
let session =
|
||||
BluetoothSession::create_session(None).expect("Could not initialize bluetooth session");
|
||||
|
||||
let ret = BluetoothLightbulb {
|
||||
config: config,
|
||||
msg_types: msg_types,
|
||||
levels: levels,
|
||||
session: session,
|
||||
};
|
||||
|
||||
// Initialize bluetooth device
|
||||
let adapter =
|
||||
BluetoothAdapter::init(&ret.session).expect("Could not initialize bluetooth adapter");
|
||||
// Power on
|
||||
if !adapter.is_powered().unwrap() {
|
||||
log::info!("bluetooth adapter {}: Set power ON", adapter.get_id());
|
||||
adapter
|
||||
.set_powered(true)
|
||||
.expect("Cannot power on bluetooth device");
|
||||
}
|
||||
// Start scan
|
||||
ret.initiate_scan(&adapter);
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
fn process_message(&mut self, message: Message) {
|
||||
let adapter =
|
||||
BluetoothAdapter::init(&self.session).expect("Could not initialize bluetooth adapter");
|
||||
|
||||
let mut characs = vec![];
|
||||
let mut found_macs: HashMap<String, bool> = HashMap::new();
|
||||
for mac in self.config.mac.keys() {
|
||||
found_macs.insert(mac.to_owned(), false);
|
||||
}
|
||||
|
||||
for dev_path in adapter.get_device_list().unwrap() {
|
||||
let device = BluetoothDevice::new(&self.session, dev_path);
|
||||
|
||||
let addr = device.get_address().unwrap();
|
||||
if self.config.mac.contains_key(&addr) {
|
||||
if !device.is_connected().unwrap() {
|
||||
log::info!("Connecting to {}", addr);
|
||||
match device.connect(5000) {
|
||||
Ok(_) => {
|
||||
let power = device.get_tx_power().expect("Could not request tx power");
|
||||
log::info!("Connected to {}, tx power={}", addr, power);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Could not connect to {}: {}", addr, e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for srv_path in device.get_gatt_services().unwrap() {
|
||||
let service = BluetoothGATTService::new(&self.session, srv_path);
|
||||
let service_uuid = service.get_uuid().unwrap();
|
||||
|
||||
if service_uuid == "a8b3fff0-4834-4051-89d0-3de95cddd318" {
|
||||
for charac_path in service.get_gatt_characteristics().unwrap() {
|
||||
let charac = BluetoothGATTCharacteristic::new(
|
||||
&self.session,
|
||||
charac_path.clone(),
|
||||
);
|
||||
let charac_uuid = charac.get_uuid().unwrap();
|
||||
|
||||
if charac_uuid == "a8b3fff1-4834-4051-89d0-3de95cddd318" {
|
||||
characs.push(charac);
|
||||
found_macs.insert(addr.clone(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let macs_not_found: HashMap<String, bool> = found_macs
|
||||
.into_iter()
|
||||
.filter(|(_, v)| *v == false)
|
||||
.collect();
|
||||
|
||||
if macs_not_found.len() > 0 {
|
||||
log::warn!(
|
||||
"Could not find configured lightbulbs: {:?}",
|
||||
macs_not_found.keys()
|
||||
);
|
||||
self.initiate_scan(&adapter);
|
||||
return;
|
||||
}
|
||||
|
||||
// find out color & pattern to play
|
||||
let mut cfg = LightConfig {
|
||||
color: Some(0xffffff),
|
||||
animation: Some(Animation::Smooth),
|
||||
repeat: Some(1),
|
||||
speed: Some(1.0),
|
||||
};
|
||||
for (type_glob, type_cfg) in &self.msg_types {
|
||||
if type_glob.is_match(&message.msg_type) {
|
||||
cfg.merge(type_cfg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(level_cfg) = self.levels.get(&message.level) {
|
||||
cfg.merge(level_cfg);
|
||||
}
|
||||
|
||||
let color = cfg.color.unwrap();
|
||||
let r = ((color >> 16) & 0xff) as f64 / 255.0;
|
||||
let g = ((color >> 8) & 0xff) as f64 / 255.0;
|
||||
let b = ((color >> 0) & 0xff) as f64 / 255.0;
|
||||
let anim = cfg.animation.unwrap();
|
||||
let speed = cfg.speed.unwrap();
|
||||
|
||||
// Play animation
|
||||
log::debug!("Playing {:?} {:?}", anim, color);
|
||||
|
||||
for _ in 0..cfg.repeat.unwrap() {
|
||||
match anim {
|
||||
Animation::None => {
|
||||
self.set_color(&characs, r, g, b);
|
||||
std::thread::sleep(std::time::Duration::from_millis((2000.0 / speed) as u64));
|
||||
}
|
||||
Animation::Bounce => {
|
||||
for _ in 0..4 {
|
||||
self.set_color(&characs, r, g, b);
|
||||
std::thread::sleep(std::time::Duration::from_millis(
|
||||
(250.0 / speed) as u64,
|
||||
));
|
||||
self.set_color(&characs, r * 0.5, g * 0.5, b * 0.5);
|
||||
std::thread::sleep(std::time::Duration::from_millis(
|
||||
(250.0 / speed) as u64,
|
||||
));
|
||||
}
|
||||
}
|
||||
Animation::Smooth => {
|
||||
for i in 0..10 {
|
||||
let mult = i as f64 / 10.0;
|
||||
self.set_color(&characs, r * mult, g * mult, b * mult);
|
||||
}
|
||||
for i in 0..10 {
|
||||
let mult = 1.0 - (i as f64 / 10.0);
|
||||
self.set_color(&characs, r * mult, g * mult, b * mult);
|
||||
}
|
||||
}
|
||||
Animation::Blink => {
|
||||
for i in 0..4 {
|
||||
let (r, g, b) = match i % 2 {
|
||||
0 => (r, g, b),
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
self.set_color(&characs, r, g, b);
|
||||
std::thread::sleep(std::time::Duration::from_millis(
|
||||
(400.0 / speed) as u64,
|
||||
));
|
||||
}
|
||||
}
|
||||
Animation::RampUp => {
|
||||
for i in 0..20 {
|
||||
let mult = i as f64 / 20.0;
|
||||
self.set_color(&characs, r * mult, g * mult, b * mult);
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
self.set_color(&characs, 0.0, 0.0, 0.0);
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
Animation::RampDown => {
|
||||
self.set_color(&characs, r, g, b);
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
for i in 1..20 {
|
||||
let mult = 1.0 - (i as f64 / 20.0);
|
||||
self.set_color(&characs, r * mult, g * mult, b * mult);
|
||||
}
|
||||
self.set_color(&characs, 0.0, 0.0, 0.0);
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.set_color(&characs, 0.0, 0.0, 0.0);
|
||||
}
|
||||
}
|
20
src/outputs/mod.rs
Normal file
20
src/outputs/mod.rs
Normal file
@ -0,0 +1,20 @@
|
||||
pub mod espeak;
|
||||
pub mod light_beewi_bbl227;
|
||||
pub mod stdout;
|
||||
|
||||
use crate::output::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn factory(
|
||||
name: &str,
|
||||
config: &HashMap<String, serde_yaml::Value>,
|
||||
) -> Result<Box<Output>, Box<dyn std::error::Error>> {
|
||||
match name {
|
||||
"stdout" => Ok(Box::new(stdout::Stdout::new(&config)?)),
|
||||
"espeak" => Ok(Box::new(espeak::Espeak::new(&config)?)),
|
||||
"light_beewi_bbl227" => Ok(Box::new(light_beewi_bbl227::BluetoothLightbulb::new(
|
||||
&config,
|
||||
)?)),
|
||||
_ => panic!("Unknown monitor name: {}", name),
|
||||
}
|
||||
}
|
27
src/outputs/stdout.rs
Normal file
27
src/outputs/stdout.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::message::*;
|
||||
pub use crate::output::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Stdout {}
|
||||
|
||||
impl Output for Stdout {
|
||||
fn new(
|
||||
_config: &HashMap<String, serde_yaml::Value>,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
Ok(Stdout {})
|
||||
}
|
||||
fn process_message(&mut self, message: Message) {
|
||||
log::info!(
|
||||
"{}{}: {}\x1B[m",
|
||||
match message.level {
|
||||
Level::Debug => "\x1B[2m",
|
||||
Level::Notice => "",
|
||||
Level::Anomaly => "\x1B[33m",
|
||||
Level::Issue => "\x1B[31m",
|
||||
Level::Critical => "\x1B[31;40;1m",
|
||||
},
|
||||
message.msg_type,
|
||||
message.text
|
||||
);
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
pub mod monitor;
|
||||
pub mod output;
|
@ -1,15 +0,0 @@
|
||||
pub mod dhcp_leases;
|
||||
pub mod tester;
|
||||
pub mod wifi_availability;
|
||||
|
||||
use crate::monitor::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn factory(name: &str, config: &HashMap<String, serde_yaml::Value>) -> Box<Monitor + Send> {
|
||||
match name {
|
||||
"tester" => Box::new(tester::Tester::new(&config)),
|
||||
"wifi_availability" => Box::new(wifi_availability::WifiAvailability::new(&config)),
|
||||
"dhcp_leases" => Box::new(dhcp_leases::DHCPLeases::new(&config)),
|
||||
_ => panic!("Unknown monitor name: {}", name),
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
pub use crate::message::Message;
|
||||
pub use crate::monitor::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tester {
|
||||
cnt: i64,
|
||||
}
|
||||
|
||||
impl Monitor for Tester {
|
||||
fn new(config: &HashMap<String, serde_yaml::Value>) -> Self {
|
||||
Tester { cnt: 0 }
|
||||
}
|
||||
fn run(&mut self, sender: &mpsc::Sender<Message>) {
|
||||
println!("-- Sending message {}", self.cnt);
|
||||
sender
|
||||
.send(Message {
|
||||
emitter: "tester".to_string(),
|
||||
level: 10,
|
||||
msg_type: "string".to_string(),
|
||||
text: format!("This is message number {}", self.cnt),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
self.cnt += 1;
|
||||
std::thread::sleep(std::time::Duration::from_millis(2000));
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
extern crate blurz;
|
||||
|
||||
// use blurz::bluetooth_adapter::BluetoothAdapter;
|
||||
// use blurz::bluetooth_device::BluetoothDevice;
|
||||
// use blurz::bluetooth_gatt_characteristic::BluetoothGATTCharacteristic;
|
||||
// use blurz::bluetooth_gatt_service::BluetoothGATTService;
|
||||
|
||||
// use crate::message::Message;
|
||||
// pub use crate::output::*;
|
||||
// use serde_derive::*;
|
||||
|
||||
// #[derive(Debug)]
|
||||
// pub struct BluetoothLightbulb<'a> {
|
||||
// config: BluetoothLightbulbConfig,
|
||||
// adapter: BluetoothAdapter<'a>,
|
||||
// }
|
||||
// #[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
// struct BluetoothLightbulbConfig {
|
||||
// mac: Vec<String>,
|
||||
// }
|
||||
|
||||
// impl Output for BluetoothLightbulb {
|
||||
// fn new(config: &HashMap<String, serde_yaml::Value>) -> Self {
|
||||
// let config_node = config.get("config").expect("Missing `config` key").clone();
|
||||
// let config =
|
||||
// serde_yaml::from_value(config_node).expect("Invalid config for wifi_availability");
|
||||
|
||||
// let adapter = BluetoothAdapter::init().expect("Cannot initialize bluetooth adapter");
|
||||
|
||||
// BluetoothLightbulb { config: config }
|
||||
// }
|
||||
// fn process_message(&mut self, message: Message) {
|
||||
// println!("Received message: {:?}", message);
|
||||
|
||||
// let device = Device::new(dev_path);
|
||||
// }
|
||||
// }
|
@ -1,19 +0,0 @@
|
||||
use crate::message::Message;
|
||||
pub use crate::output::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Espeak {}
|
||||
|
||||
impl Output for Espeak {
|
||||
fn new(config: &HashMap<String, serde_yaml::Value>) -> Self {
|
||||
Espeak {}
|
||||
}
|
||||
fn process_message(&mut self, message: Message) {
|
||||
use std::process::{Command, Stdio};
|
||||
let res = Command::new("espeak")
|
||||
.arg(message.text)
|
||||
.stdout(Stdio::null())
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
pub mod bluetooth_lightbulb;
|
||||
pub mod espeak;
|
||||
pub mod stdout;
|
||||
|
||||
use crate::output::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn factory(name: &str, config: &HashMap<String, serde_yaml::Value>) -> Box<Output + Send> {
|
||||
match name {
|
||||
"stdout" => Box::new(stdout::Stdout::new(&config)),
|
||||
"espeak" => Box::new(espeak::Espeak::new(&config)),
|
||||
_ => panic!("Unknown monitor name: {}", name),
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
use crate::message::Message;
|
||||
pub use crate::output::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Stdout {}
|
||||
|
||||
impl Output for Stdout {
|
||||
fn new(config: &HashMap<String, serde_yaml::Value>) -> Self {
|
||||
Stdout {}
|
||||
}
|
||||
fn process_message(&mut self, message: Message) {
|
||||
println!("Received message: {:?}", message);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user