From b7a90a81bae67d15ac7b98723a82f17e372d267b Mon Sep 17 00:00:00 2001 From: Paul Lecuq Date: Sun, 10 Oct 2021 22:08:21 +0200 Subject: [PATCH] WIP: added hot reload of config using inotify --- .gitignore | 4 ++- Cargo.lock | 35 +++++++++++++++++---- Cargo.toml | 3 +- src/config/mod.rs | 72 +++++++++++++++++++++++++++++++------------ src/main.rs | 18 +++-------- src/padcontrol/mod.rs | 52 +++++++++++++++++++++++-------- src/zabbix/api.rs | 11 ++----- 7 files changed, 133 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index 8e38330..da010c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target -/config.json \ No newline at end of file +/config.json + +*.swp \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 7223592..029c15a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,7 @@ dependencies = [ "alsa-sys", "bitflags", "libc", - "nix", + "nix 0.15.0", ] [[package]] @@ -64,9 +64,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" @@ -470,9 +470,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.101" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" [[package]] name = "log" @@ -501,6 +501,15 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + [[package]] name = "micromath" version = "1.1.1" @@ -519,7 +528,7 @@ dependencies = [ "js-sys", "libc", "memalloc", - "nix", + "nix 0.15.0", "wasm-bindgen", "web-sys", "winapi", @@ -584,6 +593,19 @@ dependencies = [ "void", ] +[[package]] +name = "nix" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -1217,6 +1239,7 @@ dependencies = [ "clap", "embedded-graphics", "launchy", + "nix 0.23.0", "reqwest", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 8641afe..91515eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,5 @@ embedded-graphics = { version = "0.7.1", optional = true } launchy = { git = "https://github.com/kangalioo/launchy", branch = "master" } reqwest = { version = "0.11.4", features = ["blocking", "json"] } serde = { version = "1.0.130", features = ["derive"] } -serde_json = "1.0.68" \ No newline at end of file +serde_json = "1.0.68" +nix = "0.23.0" \ No newline at end of file diff --git a/src/config/mod.rs b/src/config/mod.rs index d3317ee..80557cb 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,10 +1,58 @@ use clap::{App, Arg, ArgMatches}; +use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify, InotifyEvent}; use serde::{Deserialize, Serialize}; use serde_json::Error; use std::string::String; use std::{fs::File, io::Read}; -#[derive(Debug, Deserialize, Serialize)] +pub struct Context { + pub cfg: Config, + pub configfile: String, + pub events: Option>, + pub inotify: Inotify, +} + +impl Context { + pub fn new() -> Self { + let configfile = Context::argparse() + .value_of("config") + .unwrap_or("config.json") + .to_string(); + + let mut ctx = Context { + cfg: Config::new(), + configfile: configfile, + events: None, + inotify: Inotify::init(InitFlags::empty()).unwrap(), + }; + + println!("Loading {} file ...", ctx.configfile); + ctx.cfg.load(&ctx.configfile); + + ctx.inotify + .add_watch(ctx.configfile.as_str(), AddWatchFlags::IN_MODIFY) + .unwrap(); + + ctx + } + pub fn argparse() -> ArgMatches<'static> { + App::new("Zabbix Launch") + .version("1.0") + .author("PaulBSD ") + .about("Lights up Launchpad mini using Zabbix data") + .arg( + Arg::with_name("config") + .short("c") + .long("config") + .value_name("FILE") + .help("Sets a custom config file") + .takes_value(true), + ) + .get_matches() + } +} + +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct Config { pub server: String, pub username: String, @@ -15,7 +63,7 @@ pub struct Config { pub limit: Option, } -impl Config { +impl<'a> Config { pub fn new() -> Self { Self { server: String::from("https://zabbix.acme.com/api_jsonrpc.php"), @@ -43,7 +91,7 @@ impl Config { } } - pub fn load<'a>(&mut self, configfile: &'a str) { + pub fn load(&'a mut self, configfile: &str) { let fileopen: Result; let filemeta = std::fs::metadata(configfile); let fileexists = match filemeta { @@ -74,24 +122,8 @@ impl Config { self.save(&configfile); } - pub fn save<'a>(&self, configfile: &'a str) { + pub fn save(&self, configfile: &str) { let file = File::create(configfile).unwrap(); serde_json::to_writer_pretty(file, &self).unwrap(); } } - -pub fn argparse<'a>() -> ArgMatches<'a> { - App::new("Zabbix Launch") - .version("1.0") - .author("PaulBSD ") - .about("Lights up Launchpad mini using Zabbix data") - .arg( - Arg::with_name("config") - .short("c") - .long("config") - .value_name("FILE") - .help("Sets a custom config file") - .takes_value(true), - ) - .get_matches() -} diff --git a/src/main.rs b/src/main.rs index d10b60f..8eed984 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,24 +2,16 @@ pub mod config; mod padcontrol; mod zabbix; -use config::Config; +use config::Context; use zabbix::problems::ZabbixLayout; fn main() { let mut datamatrix = ZabbixLayout { layout: Vec::new() }; - // parse arguments - let matches = config::argparse(); - // load configuration - let configfile = matches.value_of("config").unwrap_or("config.json"); - let mut cfg = Config::new(); - cfg.load(&configfile); - zabbix::api::get_zabbix_authtoken(&mut cfg); - cfg.save(&configfile); - - // init/connect to launchpad and clear it + let mut context = Context::new(); + zabbix::api::get_zabbix_authtoken(&mut context.cfg); let (mut canvas, mut _poller) = padcontrol::initpad(); - //padcontrol::input(&mut canvas, &mut _poller); - padcontrol::draw(&mut canvas, &mut datamatrix, &mut cfg); + padcontrol::draw(&mut canvas, &mut datamatrix, &mut context); + //padcontrol::test(&mut context); } diff --git a/src/padcontrol/mod.rs b/src/padcontrol/mod.rs index b7edc9a..968a8ba 100644 --- a/src/padcontrol/mod.rs +++ b/src/padcontrol/mod.rs @@ -1,16 +1,14 @@ -use std::time::Duration; -use std::thread::sleep; -use crate::config::Config; +use crate::config::Context; use crate::zabbix::api::get_zabbix_problems; use crate::zabbix::problems::ZabbixLayout; use launchy::Color; use launchy::{self, Canvas, CanvasLayout, CanvasLayoutPoller, MsgPollingWrapper, Pad}; - +use std::thread::sleep; +use std::time::Duration; pub const MATRIX_WIDTH: i32 = 8; pub const MATRIX_SIZE: i32 = MATRIX_WIDTH * MATRIX_WIDTH; - pub fn initpad() -> (CanvasLayout<'static>, CanvasLayoutPoller) { let (mut canvas, poller) = launchy::CanvasLayout::new_polling(); match canvas.add_by_guess_rotated::(0, 0, launchy::Rotation::None) { @@ -35,10 +33,37 @@ pub fn _input(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) { } } -pub fn draw(canvas: &mut CanvasLayout, datamatrix: &mut ZabbixLayout, cfg: &mut Config) { - println!("Refresh rate is {} seconds", cfg.refresh.unwrap()); +pub fn test<'a>(ctx: &mut Context) { + println!("Refresh rate is {} seconds", ctx.cfg.refresh.unwrap()); loop { - let zabbix_data_result = get_zabbix_problems(cfg); + println!("test"); + let events = match ctx.inotify.read_events() { + Ok(ok) => ok, + Err(_) => continue, + }; + println!("nombre d'events: {}", events.len()); + for i in events { + println!("test event"); + println!("{:?}", i); + } + let zabbix_data_result = get_zabbix_problems(&ctx.cfg); + let zabbix_data = match zabbix_data_result { + Ok(z) => z, + Err(_) => { + println!("Error requesting zabbix service"); + sleep(Duration::from_secs(10)); + continue; + } + }; + println!("{:?}", zabbix_data); + sleep(Duration::from_secs(ctx.cfg.refresh.unwrap_or(5))); + } +} + +pub fn draw(canvas: &mut CanvasLayout, datamatrix: &mut ZabbixLayout, ctx: &mut Context) { + println!("Refresh rate is {} seconds", ctx.cfg.refresh.unwrap()); + loop { + let zabbix_data_result = get_zabbix_problems(&ctx.cfg); let zabbix_data = match zabbix_data_result { Ok(zabbix_data) => zabbix_data, Err(_) => { @@ -50,8 +75,11 @@ pub fn draw(canvas: &mut CanvasLayout, datamatrix: &mut ZabbixLayout, cfg: &mut datamatrix.compute(&zabbix_data); clear(canvas); let mut max = 0; - for (i,j) in datamatrix.layout.iter().enumerate() { - let p = Pad { x: ((i as i32) % MATRIX_WIDTH ), y: ((i as i32) / MATRIX_WIDTH )+1 }; + for (i, j) in datamatrix.layout.iter().enumerate() { + let p = Pad { + x: ((i as i32) % MATRIX_WIDTH), + y: ((i as i32) / MATRIX_WIDTH) + 1, + }; let c = match j.status { 5 => Color::RED, 4 => Color::from_hue(0.05), @@ -64,12 +92,12 @@ pub fn draw(canvas: &mut CanvasLayout, datamatrix: &mut ZabbixLayout, cfg: &mut if max >= MATRIX_SIZE { break; } - if cfg.sloweffect.unwrap() { + if ctx.cfg.sloweffect.unwrap() { sleep(Duration::from_millis(15)); } canvas.flush().unwrap(); } - sleep(Duration::from_secs(cfg.refresh.unwrap_or(5))); + sleep(Duration::from_secs(ctx.cfg.refresh.unwrap_or(5))); } } diff --git a/src/zabbix/api.rs b/src/zabbix/api.rs index e51157a..f7ac7ea 100644 --- a/src/zabbix/api.rs +++ b/src/zabbix/api.rs @@ -16,19 +16,13 @@ pub fn get_zabbix_authtoken(cfg: &mut Config) { cfg.authtoken = Some(values["result"].as_str().unwrap().to_string()); } -pub fn get_zabbix_problems(cfg: &mut Config) -> Result { - let body = query_triggers( - &cfg.authtoken.as_ref().unwrap_or(&String::from("")), - ); +pub fn get_zabbix_problems(cfg: &Config) -> Result { + let body = query_triggers(&cfg.authtoken.as_ref().unwrap_or(&String::from(""))); let resp = reqwest::blocking::Client::new() .post(&cfg.server) .json(&body) .send(); - //let res = match resp { - // Ok(res) => res.json() - //}; - Ok(resp.unwrap().json().unwrap()) } @@ -67,7 +61,6 @@ fn _query_problems(zabbix_token: &String, zabbix_limit: i64) -> Value { }) } - pub fn query_triggers(zabbix_token: &String) -> Value { let zabbix_api_function = "trigger.get"; json!({