#[macro_use] extern crate ini; use std::thread; use std::time::Duration; use std::process::Command; /// Rule description structure pub struct Rule { /// Monitored service name pub service: String, /// Monitored URI pub address: String, /// E-mail address for messages pub email: String, /// This command will be executed on state change pub command: String, /// Last state pub last_state: bool, } /// Checks service availability /// /// # Arguments /// /// * `uri` - Service URI to be checked /// /// # Example /// /// ``` /// if if check("https://somesite.local/api/v1/".to_string()).await { /// println!("ok"); /// } else { /// println!("uh-oh... something wrong!"); /// } /// ``` async fn check(uri: String) -> bool { if let Err(_e) = reqwest::get(uri).await { return false; } else { return true; } } /// Output to console only in debug version /// /// # Arguments /// /// * `text` - Message text /// /// # Example /// /// ``` /// debug_log("Debug version started".to_string()); /// ``` fn debug_log(text: String) { if cfg!(debug_assertions) { println!("{}", text); } } /// Execute shell command /// /// In linux sh command interpreter will be called. /// In Windows PowerShell command interpreter will be called. /// /// # Arguments /// /// * `command` - Command to be executed /// /// # Example /// /// ``` /// execute("gedit".to_string()); /// ``` fn execute(command: String) { debug_log(format!("execute {}", command)); let output = if cfg!(target_os = "windows") { Command::new("powershell") .args(["-NoLogo", "-NonInteractive", "-Command", &command]) .output() .expect("failed to execute process") } else { Command::new("sh") .arg("-c") .arg(&command) .output() .expect("failed to execute process") }; let result = output.stdout; debug_log(format!("{:?}", result)); } fn main() { let cfg_file = if cfg!(windows) { "cai-watchdog.cfg" } else { "/etc/cai-watchdog.cfg" }; let cfg = ini!(cfg_file); let check_interval = cfg["main"]["check_interval"].clone().unwrap().parse::().unwrap(); let rules_count = cfg["main"]["rules_count"].clone().unwrap().parse::().unwrap(); let mut tasks = vec![]; for i in 1..rules_count + 1 { let service = cfg[&format!("{}{}", "rule", i)]["service"].clone().unwrap(); let address = cfg[&format!("{}{}", "rule", i)]["address"].clone().unwrap(); let email = cfg[&format!("{}{}", "rule", i)]["email"].clone().unwrap(); let command = cfg[&format!("{}{}", "rule", i)]["command"].clone().unwrap(); debug_log(format!("rule {}", i)); debug_log(format!("service {}", service)); debug_log(format!("address {}", address)); debug_log(format!("email {}", email)); debug_log(format!("command {}", command)); tasks.push( Rule{ service: cfg[&format!("{}{}", "rule", i)]["service"].clone().unwrap().to_string(), address: cfg[&format!("{}{}", "rule", i)]["address"].clone().unwrap().to_string(), email: cfg[&format!("{}{}", "rule", i)]["email"].clone().unwrap().to_string(), command: cfg[&format!("{}{}", "rule", i)]["command"].clone().unwrap().to_string(), last_state: false, } ); } loop { for i in 0..tasks.len() { let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { if check(tasks[i].address.clone()).await { if tasks[i].last_state != true { debug_log(format!("{} state changed to true", tasks[i].address)); let shell_cmd = tasks[i].command.to_string() .replace("", &tasks[i].email) .replace("
", &format!("\"Service {} ({}) online\"", tasks[i].service, tasks[i].address)) .replace("", &format!("\"Service {} ({}) is now online\"", tasks[i].service, tasks[i].address)); debug_log(format!("execute {}", shell_cmd)); execute(shell_cmd); } debug_log(format!("{} check is ok", tasks[i].address)); tasks[i].last_state = true } else { if tasks[i].last_state != false { debug_log(format!("{} state changed to false", tasks[i].address)); let shell_cmd = tasks[i].command.to_string() .replace("", &tasks[i].email) .replace("
", &format!("\"Service {} ({}) offline\"", tasks[i].service, tasks[i].address)) .replace("", &format!("\"Service {} ({}) is now offline\"", tasks[i].service, tasks[i].address)); debug_log(format!("execute {}", shell_cmd)); execute(shell_cmd); } debug_log(format!("{} check failed", tasks[i].address)); tasks[i].last_state = false } }); } thread::sleep(Duration::from_millis(check_interval)); } }