CAI-Watchdog/src/main.rs
Alexander I. Chebykin b55f72f2e4 Configuration file options modified.
address renamed to uri, <header> renamed to <subject>
2022-06-27 21:37:11 +03:00

255 lines
8.1 KiB
Rust

#[macro_use]
extern crate ini;
extern crate exitcode;
use std::env;
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 uri: 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));
}
/// Print help and program version information
///
/// # Arguments
///
/// * `args` - Command-line arguments
///
/// # Example
///
/// ```
/// let args: Vec<String> = env::args().collect();
///
/// print_help(args.clone());
/// ```
fn print_help(args: Vec<String>) {
if cfg!(windows) {
if args.len() > 1 && (args[1].to_string() == "/help".to_string() || args[1].to_string() == "/?".to_string()) {
println!("");
println!("Usage: {} [/? | /help | /v | /ver | config_file]", &args[0]);
println!(" /? | /help : This help message");
println!(" /v | /ver : Version info");
println!(" config_file : Configuration file");
std::process::exit(exitcode::OK);
} else if args.len() > 1
&& (args[1].to_string() == "/ver".to_string() || args[1].to_string() == "/v".to_string()) {
const VERSION: &str = env!("CARGO_PKG_VERSION");
println!("");
println!("CAI Watchdog ver {}", VERSION);
std::process::exit(exitcode::OK);
}
} else {
if args.len() > 1 && (args[1].to_string() == "--help".to_string() || args[1].to_string() == "-h".to_string()) {
println!("");
println!("Usage: {} [-h | --help | -v | --ver | config_file]", &args[0]);
println!(" -h | --help : This help message");
println!(" -v | --ver : Version info");
println!(" config_file : Configuration file");
std::process::exit(exitcode::OK);
} else if args.len() > 1
&& (args[1].to_string() == "--ver".to_string() || args[1].to_string() == "-v".to_string()) {
const VERSION: &str = env!("CARGO_PKG_VERSION");
println!("");
println!("CAI Watchdog ver {}", VERSION);
std::process::exit(exitcode::OK);
}
}
}
fn main() {
let args: Vec<String> = env::args().collect();
print_help(args.clone());
let cfg_file = if args.len() > 1 {
&args[1]
} else if cfg!(windows) {
"cai-watchdog.ini"
} else {
"/etc/cai-watchdog.conf"
};
let cfg = ini!(cfg_file);
let check_interval = cfg["main"]["check_interval"].clone().unwrap().parse::<u64>().unwrap() * 1000;
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 uri = cfg[&format!("{}{}", "rule", i)]["uri"].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!("uri {}", uri));
debug_log(format!("email {}", email));
debug_log(format!("command {}", command));
tasks.push(
Rule{
service: cfg[&format!("{}{}", "rule", i)]["service"].clone().unwrap().to_string(),
uri: cfg[&format!("{}{}", "rule", i)]["uri"].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,
}
);
}
let start_notification = cfg["notifications"]["service_start"].clone().unwrap().to_string() == "true".to_string();
let notification_email = cfg["notifications"]["email"].clone().unwrap();
let notification_command = cfg["notifications"]["command"].clone().unwrap();
if start_notification {
debug_log(format!("Service started"));
let shell_cmd = notification_command.to_string()
.replace("<email>", &notification_email.to_string())
.replace("<subject>", &format!("\"Watchdog service started\""))
.replace("<message>", &format!("\"Watchdog service started\""));
debug_log(format!("execute {}", shell_cmd));
execute(shell_cmd);
}
loop {
for i in 0..tasks.len() {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
if check(tasks[i].uri.clone()).await {
if tasks[i].last_state != true {
debug_log(format!("{} state changed to true", tasks[i].uri));
let shell_cmd = tasks[i].command.to_string()
.replace("<email>", &tasks[i].email)
.replace("<subject>", &format!("\"Service {} ({}) is online\"", tasks[i].service, tasks[i].uri))
.replace("<message>", &format!("\"Service {} ({}) is now online\"", tasks[i].service, tasks[i].uri));
debug_log(format!("execute {}", shell_cmd));
execute(shell_cmd);
}
debug_log(format!("{} check is ok", tasks[i].uri));
tasks[i].last_state = true
} else {
if tasks[i].last_state != false {
debug_log(format!("{} state changed to false", tasks[i].uri));
let shell_cmd = tasks[i].command.to_string()
.replace("<email>", &tasks[i].email)
.replace("<subject>", &format!("\"Service {} ({}) is offline\"", tasks[i].service, tasks[i].uri))
.replace("<message>", &format!("\"Service {} ({}) is now offline\"", tasks[i].service, tasks[i].uri));
debug_log(format!("execute {}", shell_cmd));
execute(shell_cmd);
}
debug_log(format!("{} check failed", tasks[i].uri));
tasks[i].last_state = false
}
});
}
thread::sleep(Duration::from_millis(check_interval));
}
}