Merge pull request 'v1' (#23) from v1 into master

Reviewed-on: #23
This commit is contained in:
Alexander I. Chebykin 2025-04-11 10:29:37 +03:00
commit 878386bdd3
9 changed files with 1240 additions and 520 deletions

1153
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,20 @@
[package] [package]
name = "cai-watchdog" name = "cai-watchdog"
version = "0.8.0" version = "0.9.0"
authors = ["Alexander I. Chebykin <alex.chebykin@gmail.com>"] authors = ["Alexander I. Chebykin <alex.chebykin@gmail.com>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
file-utils = "0.1.5" file-utils = "0.1"
chrono = "0.4.23" chrono = "0.4"
# Sync # Sync
#reqwest = { version = "0.11", features = ["blocking"] } #reqwest = { version = "0.11", features = ["blocking"] }
# Async # Async
reqwest = "0.11" reqwest = "0.12"
tokio = { version = "1", features = ["full"] } tokio = { version = "1.43", features = ["full"] }
ini = "1.3.0" ini = "1.3"
exitcode = "1.1.2" exitcode = "1.1"
hashmap = "0.0.1" sysinfo = "0.33"
sysinfo = "0.26.7" sys-locale = "0.3"
sys-locale = "0.2.3"

View File

@ -7,29 +7,16 @@ extern crate exitcode;
use std::env; use std::env;
use std::path::Path; use std::path::Path;
use std::process::Command;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use sysinfo::{System, SystemExt};
mod mod_locales; mod mods;
mod mod_fs;
/// Rule description structure use crate::mods::mod_debug::*;
struct Rule { use crate::mods::mod_fs::*;
/// Monitored service name use crate::mods::mod_i18n::*;
pub service: String, use crate::mods::mod_os::*;
/// Monitored URI use crate::mods::mod_tasks::*;
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
pub command: String,
/// Last state
pub last_state: bool,
}
/// Check rule /// Check rule
/// ///
@ -54,34 +41,6 @@ async fn check(rule: &Rule) -> bool {
}; };
} }
/// 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 /// Checks web-service availability
/// ///
/// # Arguments /// # Arguments
@ -105,59 +64,6 @@ async fn check_uri(uri: String) -> bool {
} }
} }
/// 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) {
let locale = mod_locales::Locale::new();
debug_log(format!("{} {}", locale.t().execute, command));
let output = if cfg!(target_os = "windows") {
Command::new("powershell")
.args(["-NoLogo", "-NonInteractive", "-Command", &command])
.output()
.expect(&locale.t().failed_to_execute_process) //.expect("failed to execute process")
} else {
Command::new("sh")
.arg("-c")
.arg(&command)
.output()
.expect(&locale.t().failed_to_execute_process) //.expect("failed to execute process")
};
let result = output.stdout;
debug_log(format!("{:?}", result));
}
/// Print help and program version information /// Print help and program version information
/// ///
/// # Arguments /// # Arguments
@ -172,7 +78,7 @@ fn execute(command: String) {
/// print_help(args.clone()); /// print_help(args.clone());
/// ``` /// ```
fn print_help(args: Vec<String>) { fn print_help(args: Vec<String>) {
let locale = mod_locales::Locale::new(); let locale = Locale::new();
if cfg!(windows) { if cfg!(windows) {
if args.len() > 1 && (args[1].to_string() == "/help".to_string() || args[1].to_string() == "/?".to_string()) { if args.len() > 1 && (args[1].to_string() == "/help".to_string() || args[1].to_string() == "/?".to_string()) {
@ -219,13 +125,13 @@ fn print_help(args: Vec<String>) {
} }
fn main() { fn main() {
let locale = mod_locales::Locale::new(); let locale = Locale::new();
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
print_help(args.clone()); print_help(args.clone());
let mut config_file = mod_fs::get_exe_path(); let mut config_file = get_exe_path();
if cfg!(windows) { if cfg!(windows) {
config_file = format!("{}\\cai-watchdog.ini", config_file); config_file = format!("{}\\cai-watchdog.ini", config_file);
@ -349,95 +255,10 @@ fn main() {
let rt = tokio::runtime::Runtime::new().unwrap(); let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async { rt.block_on(async {
//if check(tasks[i].uri.clone()).await {
if check(&tasks_web[i]).await { if check(&tasks_web[i]).await {
if tasks_web[i].last_state != true || just_started_web { execute_web_task(&mut tasks_web[i], true, just_started_web);
if tasks_web[i].command.to_string() == "".to_string() {
println!("\u{2705} {} {}",
tasks_web[i].uri,
locale.t().state_changed_to_true
);
} else {
debug_log(format!("\u{2705} {} {}",
tasks_web[i].uri,
locale.t().state_changed_to_true)
);
let shell_cmd =
tasks_web[i].command.to_string()
.replace("<email>", &tasks_web[i].email)
.replace("<subject>",
&format!(
"\"\u{2705} {} {} ({}) {}\"",
locale.t().service,
tasks_web[i].service,
tasks_web[i].uri,
locale.t().is_online
)
)
.replace("<message>",
&format!(
"\"\u{2705} {} {} ({}) {}\"",
locale.t().service,
tasks_web[i].service,
tasks_web[i].uri,
locale.t().is_online_now
)
)
.replace("<service>", &tasks_web[i].service)
.replace("<uri>", &tasks_web[i].uri)
.replace("<state>", "online");
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
execute(shell_cmd);
}
}
debug_log(format!("\u{2705} {} {}", tasks_web[i].uri, locale.t().check_is_ok));
tasks_web[i].last_state = true;
} else { } else {
if tasks_web[i].last_state != false || just_started_web { execute_web_task(&mut tasks_web[i], false, just_started_web);
if tasks_web[i].command.to_string() == "".to_string() {
println!("\u{274c} {} {}", tasks_web[i].uri, locale.t().state_changed_to_false);
} else {
debug_log(format!("\u{274c} {} {}", tasks_web[i].uri, locale.t().state_changed_to_false));
let shell_cmd =
tasks_web[i].command.to_string()
.replace("<email>", &tasks_web[i].email)
.replace("<subject>", &
format!(
"\"\u{274c} {} {} ({}) {}\"",
locale.t().service,
tasks_web[i].service,
tasks_web[i].uri,
locale.t().is_offline
)
)
.replace("<message>", &
format!(
"\"\u{274c} {} {} ({}) {}\"",
locale.t().service,
tasks_web[i].service,
tasks_web[i].uri,
locale.t().is_offline_now
)
)
.replace("<service>", &tasks_web[i].service)
.replace("<uri>", &tasks_web[i].uri)
.replace("<state>", "offline");
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
execute(shell_cmd);
}
}
debug_log(format!("\u{274c} {} {}", tasks_web[i].uri, locale.t().check_failed));
tasks_web[i].last_state = false;
} }
}); });
} }
@ -450,96 +271,15 @@ fn main() {
let mut just_started_prc = true; let mut just_started_prc = true;
let locale = mod_locales::Locale::new();
loop { loop {
for i in 0..tasks_prc.len() { for i in 0..tasks_prc.len() {
let rt = tokio::runtime::Runtime::new().unwrap(); let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async { rt.block_on(async {
//if check(tasks[i].uri.clone()).await {
if check(&tasks_prc[i]).await { if check(&tasks_prc[i]).await {
if tasks_prc[i].last_state != true || just_started_prc { execute_os_task(&mut tasks_prc[i], true, just_started_prc);
if tasks_prc[i].command.to_string() == "".to_string() {
println!("\u{2705} {} {}", tasks_prc[i].uri, locale.t().state_changed_to_true);
} else {
debug_log(format!("\u{2705} {} {}", tasks_prc[i].uri, locale.t().state_changed_to_true));
let shell_cmd =
tasks_prc[i].command.to_string()
.replace("<email>", &tasks_prc[i].email)
.replace("<subject>",
&format!(
"\"\u{2705} {} {} ({}) {}\"",
locale.t().process,
tasks_prc[i].service,
tasks_prc[i].process,
locale.t().is_running
)
)
.replace("<message>",
&format!(
"\"\u{2705} {} {} ({}) {}\"",
locale.t().process,
tasks_prc[i].service,
tasks_prc[i].process,
locale.t().is_running_now
)
)
.replace("<service>", &tasks_prc[i].service)
.replace("<process>", &tasks_prc[i].process)
.replace("<state>", "running");
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
execute(shell_cmd);
}
}
debug_log(format!("\u{2705} {} {}", tasks_prc[i].process, locale.t().is_running));
tasks_prc[i].last_state = true;
} else { } else {
if tasks_prc[i].last_state != false || just_started_prc { execute_os_task(&mut tasks_prc[i], false, just_started_prc);
if tasks_prc[i].command.to_string() == "".to_string() {
println!("\u{274c} {} {}", tasks_prc[i].uri, locale.t().state_changed_to_false);
} else {
debug_log(format!("\u{274c} {} {}", tasks_prc[i].uri, locale.t().state_changed_to_false));
let shell_cmd =
tasks_prc[i].command.to_string()
.replace("<email>", &tasks_prc[i].email)
.replace("<subject>",
&format!(
"\"\u{274c} {} {} ({}) {}\"",
locale.t().process,
tasks_prc[i].service,
tasks_prc[i].process,
locale.t().is_not_running
)
)
.replace("<message>",
&format!(
"\"\u{274c} {} {} ({}) {}\"",
locale.t().process,
tasks_prc[i].service,
tasks_prc[i].process,
locale.t().is_not_running_now
)
)
.replace("<service>", &tasks_prc[i].service)
.replace("<process>", &tasks_prc[i].process)
.replace("<state>", "stopped");
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
execute(shell_cmd);
}
}
debug_log(format!("\u{274c} {} {}", tasks_prc[i].process, locale.t().is_not_running));
tasks_prc[i].last_state = false;
} }
}); });
} }

5
src/mods/mod.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod mod_debug;
pub mod mod_fs;
pub mod mod_i18n;
pub mod mod_os;
pub mod mod_tasks;

16
src/mods/mod_debug.rs Normal file
View File

@ -0,0 +1,16 @@
/// Output to console only in debug version
///
/// # Arguments
///
/// * `text` - Message text
///
/// # Example
///
/// ```
/// debug_log("Debug version started".to_string());
/// ```
pub fn debug_log(text: String) {
if cfg!(debug_assertions) {
println!("{}", text);
}
}

View File

@ -21,5 +21,6 @@ pub fn get_exe_path() -> String {
exe_path = ".".to_string(); exe_path = ".".to_string();
}, },
} }
return exe_path; return exe_path;
} }

View File

@ -6,7 +6,7 @@
use std::borrow::Borrow; use std::borrow::Borrow;
use std::path::Path; use std::path::Path;
use crate::mod_fs; use crate::mods::mod_fs;
use sys_locale::get_locale; use sys_locale::get_locale;
pub struct Lang { pub struct Lang {
@ -180,6 +180,6 @@ impl Locale {
/// println!("{}", locale.t().exiting); /// println!("{}", locale.t().exiting);
/// ``` /// ```
pub fn t(&self) -> &Lang { pub fn t(&self) -> &Lang {
return self.locale.borrow().clone(); return self.locale.borrow();
} }
} }

70
src/mods/mod_os.rs Normal file
View File

@ -0,0 +1,70 @@
use std::ffi::OsStr;
use std::process::Command;
use sysinfo::System;
use crate::mods::mod_debug::*;
use crate::mods::mod_i18n::*;
/// 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!");
/// }
/// ```
pub 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(OsStr::new(&process_name)) {
result = true;
}
return result;
}
/// 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());
/// ```
pub fn execute(command: String) {
let locale = Locale::new();
debug_log(format!("{} {}", locale.t().execute, command));
let output = if cfg!(target_os = "windows") {
Command::new("powershell")
.args(["-NoLogo", "-NonInteractive", "-Command", &command])
.output()
.expect(&locale.t().failed_to_execute_process) //.expect("failed to execute process")
} else {
Command::new("sh")
.arg("-c")
.arg(&command)
.output()
.expect(&locale.t().failed_to_execute_process) //.expect("failed to execute process")
};
let result = output.stdout;
debug_log(format!("{:?}", result));
}

206
src/mods/mod_tasks.rs Normal file
View File

@ -0,0 +1,206 @@
use crate::mods::mod_debug::*;
use crate::mods::mod_i18n::*;
use crate::mods::mod_os::*;
pub struct Rule {
/// Monitored service name
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
pub command: String,
/// Last state
pub last_state: bool,
}
pub fn execute_web_task(task: &mut Rule, current_state:bool, just_started: bool) {
let locale = Locale::new();
if task.last_state != current_state || just_started {
if task.command.to_string() == "".to_string() {
if current_state {
println!("\u{2705} {} {}", task.uri, locale.t().state_changed_to_true.clone());
} else {
println!("\u{274c} {} {}", task.uri, locale.t().state_changed_to_false.clone());
}
} else {
if current_state {
debug_log(
format!(
"\u{2705} {} {}",
task.uri,
locale.t().state_changed_to_true.clone()
)
);
} else {
debug_log(
format!(
"\u{274c} {} {}",
task.uri,
locale.t().state_changed_to_false.clone()
)
);
}
let subject: String = if current_state {
format!(
"\"\u{2705} {} {} ({}) {}\"",
locale.t().service,
task.service,
task.uri,
locale.t().is_online.clone()
)
} else {
format!(
"\"\u{274c} {} {} ({}) {}\"",
locale.t().service,
task.service,
task.uri,
locale.t().is_offline.clone()
)
};
let shell_cmd =
task.command.to_string()
.replace("<email>", &task.email)
.replace("<subject>", &subject)
.replace("<message>", &subject)
.replace("<service>", &task.service)
.replace("<uri>", &task.uri)
.replace(
"<state>",
if current_state {
"online"
} else {
"offline"
}
);
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
execute(shell_cmd);
}
}
if current_state {
debug_log(
format!(
"\u{2705} {} {}",
task.uri,
locale.t().check_is_ok.clone()
)
)
} else {
debug_log(
format!(
"\u{274c} {} {}",
task.uri,
locale.t().check_failed.clone()
)
);
}
task.last_state = current_state;
}
pub fn execute_os_task(task: &mut Rule, current_state:bool, just_started: bool) {
let locale = Locale::new();
if task.last_state != current_state || just_started {
if task.command.to_string() == "".to_string() {
if current_state {
println!(
"\u{2705} {} {}",
task.uri,
locale.t().state_changed_to_true.clone()
);
} else {
println!(
"\u{274c} {} {}",
task.uri,
locale.t().state_changed_to_false.clone()
)
}
} else {
if current_state {
debug_log(
format!(
"\u{2705} {} {}",
task.uri,
locale.t().state_changed_to_true.clone()
)
)
} else {
debug_log(
format!(
"\u{274c} {} {}",
task.uri,
locale.t().state_changed_to_false.clone()
)
)
}
let subject: String = if current_state {
format!(
"\"\u{2705} {} {} ({}) {}\"",
locale.t().process,
task.service,
task.process,
locale.t().is_running.clone()
)
} else {
format!(
"\"\u{274c} {} {} ({}) {}\"",
locale.t().process,
task.service,
task.process,
locale.t().is_not_running.clone()
)
};
let shell_cmd =
task.command.to_string()
.replace("<email>", &task.email)
.replace("<subject>", &subject)
.replace("<message>", &subject)
.replace("<service>", &task.service)
.replace("<process>", &task.process)
.replace(
"<state>",
if current_state {
"running"
} else {
"stopped"
}
);
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
execute(shell_cmd);
}
}
if current_state {
debug_log(
format!(
"\u{2705} {} {}",
task.process,
locale.t().is_running
)
);
} else {
debug_log(
format!(
"\u{274c} {} {}",
task.process,
locale.t().is_not_running
)
);
}
task.last_state = current_state;
}