Files
CAI-Watchdog/src/main.rs
2022-06-27 00:16:39 +03:00

177 lines
5.3 KiB
Rust

#[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::<u64>().unwrap();
let rules_count = cfg["main"]["rules_count"].clone().unwrap().parse::<u8>().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("<email>", &tasks[i].email)
.replace("<header>", &format!("\"Service {} ({}) online\"", tasks[i].service, tasks[i].address))
.replace("<message>", &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("<email>", &tasks[i].email)
.replace("<header>", &format!("\"Service {} ({}) offline\"", tasks[i].service, tasks[i].address))
.replace("<message>", &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));
}
}