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; #[derive(Debug)] pub enum ReloadFrequency { High = 50, Medium = 20, Low = 10, } #[derive(Debug, Clone)] pub struct Context { pub cfg: Config, configfile: String, inotify: Inotify, pub datamatrix: DataMatrix, pub mode: String, } impl Context { pub fn new() -> Self { let cli = Context::argparse(); let configfile = cli.value_of("config").unwrap_or("config.json").to_string(); let mut ctx = Context { cfg: Config::new(), configfile: configfile, inotify: Inotify::init(InitFlags::IN_NONBLOCK).unwrap(), datamatrix: DataMatrix::new(), mode: cli.value_of("mode").unwrap_or("draw").to_string(), }; 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") .takes_value(true) .help("Sets a custom config file"), ) .arg( Arg::new("mode") .short('m') .long("mode") .value_name("mode") .default_value("draw") .possible_values(&["draw", "test", "input", "effect"]) .takes_value(true) .help("Sets a when running"), ) .arg( Arg::new("debug") .short('d') .takes_value(false) .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, pub sloweffect: Option, pub refresh: Option, pub limit: Option, } 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; 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 = 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(); } }