Minimal functionality implemented

Minimal functionality implemented
This commit is contained in:
Alexander I. Chebykin 2022-06-27 00:16:39 +03:00
parent 239197ffed
commit e0011e421d
10 changed files with 1357 additions and 1 deletions

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Build files
target

1083
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

17
Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "cai-watchdog"
version = "0.1.0"
authors = ["Alexander I. Chebykin <alex.chebykin@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
file-utils = "0.1.5"
chrono = "0.4.19"
# Sync
#reqwest = { version = "0.11", features = ["blocking"] }
# Async
reqwest = "0.11"
tokio = { version = "1", features = ["full"] }
ini = "1.3.0"

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) <year> <copyright holders> Copyright (c) 2022 Alexander I. Chebykin <alex.chebykin@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -0,0 +1,9 @@
[main]
check_interval = 10000
rules_count = 2
[rule1]
address = http://127.0.0.1:3000/api/v1/
email = admin@server.local
[rule2]
address = http://127.0.0.1:3300/api/v1/
email = admin@server.local

View File

@ -0,0 +1,16 @@
[Unit]
Description=Watchdog service
[Service]
User=wwwrun
Group=www
Type=simple
ExecStart=/usr/local/bin/cai-watchdog
KillMode=control-group
NotifyAccess=all
SuccessExitStatus=2
[Install]
WantedBy=multi-user.target
Alias=cai_watchdog

View File

@ -0,0 +1,23 @@
#!/bin/bash
GROUP_ID=<group_id>
BOT_TOKEN=<bot_token>
# this 3 checks (if) are not necessary but should be convenient
if [ "$1" == "-h" ]; then
echo "Usage: `basename $0` \"text message\""
exit 0
fi
if [ -z "$1" ]
then
echo "Add message text as second arguments"
exit 0
fi
if [ "$#" -ne 1 ]; then
echo "You can pass only one argument. For string with spaces put it on quotes"
exit 0
fi
curl -s --data "text=$1" --data "chat_id=$GROUP_ID" 'https://api.telegram.org/bot'$BOT_TOKEN'/sendMessage' > /dev/null

View File

@ -0,0 +1,11 @@
Function Send-Mail {
$EmailFrom = "yourmailadress@somedomain.com"
$EmailTo = $args[0]
$Subject = $args[1]
$Body = $args[0]
$SMTPServer = "smtp.somedomain.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("usr", "pass");
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
}

View File

@ -0,0 +1,10 @@
Function Send-Telegram {
Param([Parameter(Mandatory=$true)][String]$Message)
$Telegramtoken = "Your_Telegram_Token"
$Telegramchatid = "Your_Telegram_Chat_ID"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$Response = Invoke-RestMethod -Uri "https://api.telegram.org/bot$($Telegramtoken)/sendMessage?chat_id=$($Telegramchatid)&text=$($Message)"
}

176
src/main.rs Normal file
View File

@ -0,0 +1,176 @@
#[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));
}
}