use crate::zabbix::api::get_zabbix_authtoken; use crate::zabbix::problems::DataMatrix; use clap::{Arg, ArgMatches, Command}; use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify}; use serde::{Deserialize, Serialize}; use serde_json::Error as JsonError; use serde_json::Value as JsonValue; use std::fs::File; use std::io::Read; use std::string::String; use std::thread::sleep; use std::time::Duration; pub enum ReloadFrequency { High = 50, Medium = 20, Low = 10, } #[derive(Debug, Clone)] pub enum Mode { Draw, Test, Input, Effect, } #[derive(Debug)] pub struct Context { pub cfg: Config, configfile: String, inotify: Inotify, pub datamatrix: DataMatrix, pub mode: Option<Mode>, } impl Context { pub fn new() -> Self { let argp: ArgMatches = Context::argparse(); let configfile = argp .get_one::<String>("config") .unwrap_or(&"config.json".to_string()) .to_string(); let mut ctx = Context { cfg: Config::new(), configfile: configfile, inotify: Inotify::init(InitFlags::IN_NONBLOCK).unwrap(), datamatrix: DataMatrix::new(), mode: match argp.get_one::<String>("mode").unwrap().as_str() { "draw" => Some(Mode::Draw), "test" => Some(Mode::Test), "input" => Some(Mode::Input), "effect" => Some(Mode::Effect), _ => { println!("No valid mode choosen"); None } }, }; println!("Loading {configfile} file ...", configfile = ctx.configfile); ctx.cfg.load(&ctx.configfile); println!( "Adding inotify watch on {configfile} file ...", configfile = ctx.configfile ); ctx.inotify .add_watch(ctx.configfile.as_str(), AddWatchFlags::IN_MODIFY) .unwrap(); get_zabbix_authtoken(&mut ctx.cfg); ctx } fn argparse() -> ArgMatches { Command::new(env!("CARGO_PKG_NAME")) .version(env!("CARGO_PKG_VERSION")) .author(env!("CARGO_PKG_AUTHORS")) .about(env!("CARGO_PKG_DESCRIPTION")) .arg( Arg::new("config") .short('c') .long("config") .value_name("FILE") .default_value("/etc/zabbixlaunch/config.json") .help("Sets a custom config file"), ) .arg( Arg::new("mode") .short('m') .long("mode") .value_name("mode") .default_value("draw") .value_parser(["draw", "test", "input", "effect"]) .help("Sets a when running"), ) .arg(Arg::new("debug").short('d').help("Enable debugging")) .get_matches() } pub fn hotreload(&mut self) { let mut i = 0; let waitmilli = self.cfg.refresh.unwrap_or(5) * 1000; while i < waitmilli { let waitinc = waitmilli / ReloadFrequency::Medium as u64; let events = match self.inotify.read_events() { Ok(ev) => ev, Err(_) => vec![], }; if events.len() > 0 { println!("Reloading {cfg}", cfg = self.configfile); self.cfg.load(self.configfile.as_str()); get_zabbix_authtoken(&mut self.cfg); break; } else { sleep(Duration::from_millis(waitinc)); } i += waitinc; } } } #[derive(Debug, Deserialize, Serialize, Clone)] pub struct Config { pub server: String, pub username: String, pub password: String, #[serde(skip_serializing)] pub authtoken: Option<String>, pub sloweffect: Option<bool>, pub refresh: Option<u64>, pub limit: Option<u64>, } impl<'a> Config { pub fn new() -> Self { Self { server: "https://zabbix.acme.com/api_jsonrpc.php".to_string(), username: "bob".to_string(), password: "password".to_string(), authtoken: Some("token".to_string()), sloweffect: Some(true), refresh: Some(5u64), limit: Some(20u64), } } fn load(&'a mut self, configfile: &str) { let fileopen: Result<File, std::io::Error>; let filemeta = std::fs::metadata(configfile); let mut error_reading = false; let fileexists = match filemeta { Ok(_) => true, Err(_) => false, }; if !fileexists { File::create(configfile).unwrap(); } fileopen = File::open(configfile); let mut file = match fileopen { Ok(f) => f, Err(err) => { panic!("{err}", err = err); } }; let mut contents = String::new(); file.read_to_string(&mut contents).unwrap(); let parse: Result<JsonValue, JsonError> = serde_json::from_str(contents.as_str()); let ncfg = match parse { Ok(ncfg) => { let tmpcfg = Config::new(); Config { server: ncfg["server"] .as_str() .unwrap_or(tmpcfg.server.as_str()) .to_string(), username: ncfg["username"] .as_str() .unwrap_or(tmpcfg.username.as_str()) .to_string(), password: ncfg["password"] .as_str() .unwrap_or(tmpcfg.password.as_str()) .to_string(), authtoken: Some( ncfg["authtoken"] .as_str() .unwrap_or(self.authtoken.clone().unwrap().as_str()) .to_string(), ), sloweffect: Some( ncfg["sloweffect"] .as_bool() .unwrap_or(tmpcfg.sloweffect.unwrap()), ), refresh: Some(ncfg["refresh"].as_u64().unwrap_or(tmpcfg.refresh.unwrap())), limit: Some(ncfg["limit"].as_u64().unwrap_or(tmpcfg.limit.unwrap())), } } Err(err) => { error_reading = true; println!("error occured: '{}'", err); Config::new() } }; *self = ncfg; if !fileexists || error_reading { self.save(&configfile); } } fn save(&self, configfile: &str) { let file = File::create(configfile).unwrap(); serde_json::to_writer_pretty(file, &self).unwrap(); } }