From b638d510a3484122eaa52e71edff80c770dd261f Mon Sep 17 00:00:00 2001 From: "Alexander I. Chebykin" Date: Wed, 29 Jun 2022 23:48:34 +0300 Subject: [PATCH] Processes watching added Processes watching added --- Cargo.lock | 111 ++++++++++++++- Cargo.toml | 5 +- README.md | 11 +- scripts/unix/etc/cai-watchdog.conf.template | 13 +- .../unix/usr/local/{sbin => bin}/send-mail | 0 .../usr/local/{sbin => bin}/send-telegram | 0 scripts/windows/cai-watchdog.ini.template | 13 +- src/main.rs | 131 +++++++++++++++--- 8 files changed, 257 insertions(+), 27 deletions(-) rename scripts/unix/usr/local/{sbin => bin}/send-mail (100%) rename scripts/unix/usr/local/{sbin => bin}/send-telegram (100%) diff --git a/Cargo.lock b/Cargo.lock index a5e3371..336a7f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,7 +34,7 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] name = "cai-watchdog" -version = "0.3.0" +version = "0.4.0" dependencies = [ "chrono", "exitcode", @@ -42,6 +42,7 @@ dependencies = [ "hashmap", "ini", "reqwest", + "sysinfo", "tokio", ] @@ -92,6 +93,57 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "crossbeam-channel" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "once_cell", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "encoding_rs" version = "0.8.31" @@ -406,6 +458,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -442,6 +503,15 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -587,6 +657,30 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.13" @@ -758,6 +852,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysinfo" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d80929a3b477bce3a64360ca82bfb361eacce1dcb7b1fb31e8e5e181e37c212" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "winapi", +] + [[package]] name = "tempfile" version = "3.3.0" diff --git a/Cargo.toml b/Cargo.toml index 20bf62a..b61251d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cai-watchdog" -version = "0.3.0" +version = "0.4.0" authors = ["Alexander I. Chebykin "] edition = "2018" @@ -16,4 +16,5 @@ reqwest = "0.11" tokio = { version = "1", features = ["full"] } ini = "1.3.0" exitcode = "1.1.2" -hashmap = "0.0.1" \ No newline at end of file +hashmap = "0.0.1" +sysinfo = "0.24.5" diff --git a/README.md b/README.md index 7321491..343454b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CAI-Watchdog -Watchdog for monitoring web-services +Watchdog for monitoring web-services and running processes ## Requirements @@ -37,6 +37,7 @@ service_start = Send program start notification [true | false] [rule1] service = Service name uri = URI to be checked +process = process to be checked email = E-mail address for notifications command = Command to send notification ``` @@ -113,10 +114,14 @@ Watchdog can send notifications on user login. Just add to ```/etc/profile.d/ssh - For Telegram: 1. ```User=$(whoami)``` - 1. ```send-telegram "SSH: User ${Users} is logged in"``` + 1. ```IP=$(echo $SSH_CONNECTION | awk '{ print $1 == "" ? "127.0.0.1" : $1 }')``` + 1. ```ICON_INFO=$(echo -e "\U2139")``` + 1. ```send-telegram "${ICON_INFO} SSH: User ${Users} is logged in from ${IP}"``` - For e-mail: 1. ```User=$(whoami)``` - 1. ```send-mail your@mail.addr 'SSH: User ${Users} is logged in' 'SSH: User ${Users} is logged in'``` + 1. ```IP=$(echo $SSH_CONNECTION | awk '{ print $1 == "" ? "127.0.0.1" : $1 }')``` + 1. ```ICON_INFO=$(echo -e "\U2139")``` + 1. ```send-mail your@mail.addr '${ICON_INFO} SSH: User ${Users} is logged in' '${ICON_INFO} SSH: User ${Users} is logged in from ${IP}'``` ## User logouts monitoring (*nix) diff --git a/scripts/unix/etc/cai-watchdog.conf.template b/scripts/unix/etc/cai-watchdog.conf.template index bea3839..fee784c 100644 --- a/scripts/unix/etc/cai-watchdog.conf.template +++ b/scripts/unix/etc/cai-watchdog.conf.template @@ -1,6 +1,6 @@ [main] -check_interval = 10000 -rules_count = 2 +check_interval = 10 +rules_count = 3 [notifications] email = admin@server.local @@ -10,11 +10,20 @@ service_start = true [rule1] service = Test1 uri = http://127.0.0.1:3000/api/v1/ +process = email = admin@server.local command = send-telegram [rule2] service = Test2 uri = http://127.0.0.1:3300/api/v1/ +process = +email = admin@server.local +command = send-telegram + +[rule3] +service = Apache +uri = +process = httpd email = admin@server.local command = send-telegram diff --git a/scripts/unix/usr/local/sbin/send-mail b/scripts/unix/usr/local/bin/send-mail similarity index 100% rename from scripts/unix/usr/local/sbin/send-mail rename to scripts/unix/usr/local/bin/send-mail diff --git a/scripts/unix/usr/local/sbin/send-telegram b/scripts/unix/usr/local/bin/send-telegram similarity index 100% rename from scripts/unix/usr/local/sbin/send-telegram rename to scripts/unix/usr/local/bin/send-telegram diff --git a/scripts/windows/cai-watchdog.ini.template b/scripts/windows/cai-watchdog.ini.template index 366f78a..6dae4f4 100644 --- a/scripts/windows/cai-watchdog.ini.template +++ b/scripts/windows/cai-watchdog.ini.template @@ -1,6 +1,6 @@ [main] -check_interval = 10000 -rules_count = 2 +check_interval = 10 +rules_count = 3 [notifications] email = admin@server.local @@ -10,11 +10,20 @@ service_start = true [rule1] service = Test1 uri = http://127.0.0.1:3000/api/v1/ +process = email = admin@server.local command = ./send-telegram.ps1 [rule2] service = Test2 uri = http://127.0.0.1:3300/api/v1/ +process = +email = admin@server.local +command = ./send-telegram.ps1 + +[rule3] +service = Apache +uri = +process = httpd.exe email = admin@server.local command = ./send-telegram.ps1 diff --git a/src/main.rs b/src/main.rs index cb20074..79fd960 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use std::path::Path; use std::process::Command; use std::thread; use std::time::Duration; +use sysinfo::{System, SystemExt}; /// Rule description structure pub struct Rule { @@ -14,6 +15,8 @@ pub struct Rule { pub service: String, /// Monitored URI pub uri: String, + /// Monitored process name + pub process: String, /// E-mail address for messages pub email: String, /// This command will be executed on state change @@ -22,7 +25,58 @@ pub struct Rule { pub last_state: bool, } -/// Checks service availability +/// Check rule +/// +/// # Arguments +/// +/// * `rule` Rule to be checked +/// +/// # Example +/// +/// ``` +/// if check(&tasks[i]).await { +/// println!("ok"); +/// } else { +/// println!("uh-oh... something wrong!"); +/// } +/// ``` +async fn check(rule: &Rule) -> bool { + return if rule.uri.to_string() != "".to_string() { + check_uri(rule.uri.clone()).await + } else { + check_process(rule.process.clone()) + }; +} + +/// Check is process running +/// +/// # Arguments +/// +/// * `process_name` Process name to be checked +/// +/// # Example +/// +/// ``` +/// if check_process("httpd".to_string()).await { +/// println!("ok"); +/// } else { +/// println!("uh-oh... something wrong!"); +/// } +/// ``` +fn check_process(process_name: String) -> bool { + let mut sys = System::new_all(); + let mut result: bool = false; + + sys.refresh_all(); + + for _process in sys.processes_by_exact_name(&process_name) { + result = true; + } + + return result; +} + +/// Checks web-service availability /// /// # Arguments /// @@ -31,13 +85,13 @@ pub struct Rule { /// # Example /// /// ``` -/// if if check("https://somesite.local/api/v1/".to_string()).await { +/// if check_uri("https://somesite.local/api/v1/".to_string()).await { /// println!("ok"); /// } else { /// println!("uh-oh... something wrong!"); /// } /// ``` -async fn check(uri: String) -> bool { +async fn check_uri(uri: String) -> bool { if let Err(_e) = reqwest::get(uri).await { return false; } else { @@ -156,6 +210,18 @@ fn main() { let mut just_started = true; + if check_process("svchost.exe".to_string()) { + println!("svchost is running"); + } else { + println!("svchost is not running"); + } + + if check_process("httpd.exe".to_string()) { + println!("httpd is running"); + } else { + println!("httpd is not running"); + } + let cfg_file = if args.len() > 1 { &args[1] } else if cfg!(windows) { @@ -200,6 +266,12 @@ fn main() { "".to_string() }; + let process = if cfg[&format!("{}{}", "rule", i)].contains_key("process") { + cfg[&format!("{}{}", "rule", i)]["process"].clone().unwrap() + } else { + "".to_string() + }; + let email = if cfg[&format!("{}{}", "rule", i)].contains_key("email") { cfg[&format!("{}{}", "rule", i)]["email"].clone().unwrap() } else { @@ -215,6 +287,7 @@ fn main() { debug_log(format!("rule {}", i)); debug_log(format!("service {}", service)); debug_log(format!("uri {}", uri)); + debug_log(format!("process {}", process)); debug_log(format!("email {}", email)); debug_log(format!("command {}", command)); @@ -222,6 +295,7 @@ fn main() { Rule{ service: service, uri: uri, + process: process, email: email, command: command, last_state: false, @@ -265,17 +339,25 @@ fn main() { let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { - if check(tasks[i].uri.clone()).await { + //if check(tasks[i].uri.clone()).await { + if check(&tasks[i]).await { if tasks[i].last_state != true || just_started { if tasks[i].command.to_string() == "".to_string() { println!("\u{2705} {} state changed to true", tasks[i].uri); } else { debug_log(format!("\u{2705} {} state changed to true", tasks[i].uri)); - let shell_cmd = tasks[i].command.to_string() - .replace("", &tasks[i].email) - .replace("", &format!("\"\u{2705} Service {} ({}) is online\"", tasks[i].service, tasks[i].uri)) - .replace("", &format!("\"\u{2705} Service {} ({}) is now online\"", tasks[i].service, tasks[i].uri)); + let shell_cmd = if tasks[i].uri.to_string() != "".to_string() { + tasks[i].command.to_string() + .replace("", &tasks[i].email) + .replace("", &format!("\"\u{2705} Service {} ({}) is online\"", tasks[i].service, tasks[i].uri)) + .replace("", &format!("\"\u{2705} Service {} ({}) is online now\"", tasks[i].service, tasks[i].uri)) + } else { + tasks[i].command.to_string() + .replace("", &tasks[i].email) + .replace("", &format!("\"\u{2705} Process {} ({}) is running\"", tasks[i].service, tasks[i].process)) + .replace("", &format!("\"\u{2705} Process {} ({}) is running now\"", tasks[i].service, tasks[i].process)) + }; debug_log(format!("execute {}", shell_cmd)); @@ -283,7 +365,11 @@ fn main() { } } - debug_log(format!("\u{2705} {} check is ok", tasks[i].uri)); + if tasks[i].uri.to_string() != "".to_string() { + debug_log(format!("\u{2705} {} check is ok", tasks[i].uri)); + } else { + debug_log(format!("\u{2705} {} is running", tasks[i].process)); + } tasks[i].last_state = true; } else { @@ -293,26 +379,37 @@ fn main() { } else { debug_log(format!("\u{274c} {} state changed to false", tasks[i].uri)); - let shell_cmd = tasks[i].command.to_string() - .replace("", &tasks[i].email) - .replace("", &format!("\"\u{274c} Service {} ({}) is offline\"", tasks[i].service, tasks[i].uri)) - .replace("", &format!("\"\u{274c} Service {} ({}) is now offline\"", tasks[i].service, tasks[i].uri)) - .replace("", "WARNING"); + let shell_cmd = if tasks[i].uri.to_string() != "".to_string() { + tasks[i].command.to_string() + .replace("", &tasks[i].email) + .replace("", &format!("\"\u{274c} Service {} ({}) is offline\"", tasks[i].service, tasks[i].uri)) + .replace("", &format!("\"\u{274c} Service {} ({}) is now offline\"", tasks[i].service, tasks[i].uri)) + } else { + tasks[i].command.to_string() + .replace("", &tasks[i].email) + .replace("", &format!("\"\u{274c} Process {} ({}) is not running\"", tasks[i].service, tasks[i].process)) + .replace("", &format!("\"\u{274c} Process {} ({}) is not running now\"", tasks[i].service, tasks[i].process)) + }; debug_log(format!("execute {}", shell_cmd)); execute(shell_cmd); } } - debug_log(format!("\u{274c} {} check failed", tasks[i].uri)); + if tasks[i].uri.to_string() != "".to_string() { + debug_log(format!("\u{274c} {} check failed", tasks[i].uri)); + } else { + debug_log(format!("\u{274c} {} is not running", tasks[i].process)); + } + tasks[i].last_state = false; } - - just_started = false; }); } + just_started = false; + thread::sleep(Duration::from_millis(check_interval)); } }