diff --git a/Cargo.lock b/Cargo.lock index 1e5a95f..a95fd99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "arc-swap" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" + [[package]] name = "argparse" version = "0.2.2" @@ -104,6 +110,17 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "chrono" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -371,6 +388,15 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "hermit-abi" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +dependencies = [ + "libc", +] + [[package]] name = "hmac" version = "0.7.1" @@ -494,10 +520,12 @@ name = "keeprunningd" version = "0.1.0" dependencies = [ "argparse", + "chrono", "dingtalk", "rust_util", "serde", "serde_json", + "tokio", ] [[package]] @@ -572,12 +600,35 @@ dependencies = [ "kernel32-sys", "libc", "log", - "miow", + "miow 0.2.1", "net2", "slab", "winapi 0.2.8", ] +[[package]] +name = "mio-named-pipes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" +dependencies = [ + "log", + "mio", + "miow 0.3.3", + "winapi 0.3.8", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + [[package]] name = "miow" version = "0.2.1" @@ -590,6 +641,16 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "miow" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" +dependencies = [ + "socket2", + "winapi 0.3.8", +] + [[package]] name = "native-tls" version = "0.2.4" @@ -619,6 +680,35 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "opaque-debug" version = "0.2.3" @@ -958,6 +1048,16 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "signal-hook-registry" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" +dependencies = [ + "arc-swap", + "libc", +] + [[package]] name = "slab" version = "0.4.2" @@ -970,6 +1070,18 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" +[[package]] +name = "socket2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.8", +] + [[package]] name = "subtle" version = "1.0.0" @@ -1043,10 +1155,28 @@ dependencies = [ "futures-core", "iovec", "lazy_static", + "libc", "memchr", "mio", + "mio-named-pipes", + "mio-uds", + "num_cpus", "pin-project-lite", + "signal-hook-registry", "slab", + "tokio-macros", + "winapi 0.3.8", +] + +[[package]] +name = "tokio-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fbf7973..1f6402f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,11 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +tokio = { version = "0.2.6", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" argparse = "0.2.2" rust_util = "0.2.4" dingtalk = "1.3.2" - +chrono = "0.4.11" +# log = "0.4.8" diff --git a/keeprunningd.json b/keeprunningd.json new file mode 100644 index 0000000..04e8029 --- /dev/null +++ b/keeprunningd.json @@ -0,0 +1,10 @@ +{ + "check_inverval_secs": 1, + "show_debug_output": true, + "notify_token": "------------", + "items": [{ + "grep_tokens": ["java", "app"], + "title": "check java" + }] +} + diff --git a/src/main.rs b/src/main.rs index 45bc743..c4f731a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,23 @@ -use std::process::Command; +use std::{ + fs, + panic, + thread, + time::Duration, + process::Command, + sync::Arc, +}; +use chrono::prelude::*; use serde::{ Deserialize, Serialize, }; -use rust_util::util_msg::{ print_info, print_error, }; +use rust_util::{ + util_file::locate_file, + util_msg::{ print_info, print_error, print_debug, } +}; +use dingtalk::DingTalk; #[derive(Debug, Clone, Serialize, Deserialize)] struct KeepRunningConfig { + check_inverval_secs: Option, + show_debug_output: Option, notify_token: String, items: Vec, } @@ -15,18 +29,91 @@ struct KeepRunningConfigItem { } fn main() { - print_info("Start run: ps aux"); - let output_str = match ps_aux() { - Some(output_str) => output_str, None => return, - }; - let lines = read_str_to_lines(&output_str); + panic::set_hook(Box::new(|panic_info| { + print_error(&format!("Panic in running keeprunningd: {:?}", panic_info)); + })); - // println!("{}", output_str); - // for ln in &lines { - // println!(">>>>>>> {}", ln); - // } - println!("{:?}", lines.iter().filter(|ln| ln.contains("java") && ln.contains("aaa")).collect::>()); - println!("{}", lines.len()); + let config_file_opt = locate_file(&[ + "keeprunningd.json".into(), + "~/keeprunningd.json".into(), + "/etc/keeprunningd.json".into() + ]); + + let config_file = match config_file_opt { + None => { + print_error("Cannot find config file!"); + return; + }, + Some(config_file) => { + print_info(&format!("Find config file: {:?}", &config_file)); + config_file + }, + }; + + let config_file_content = match fs::read_to_string(&config_file) { + Ok(c) => c, Err(err) => { + print_error(&format!("Read config file {:?}, error: {}", &config_file, err)); + return; + }, + }; + + let keep_running_config: KeepRunningConfig = match serde_json::from_str(&config_file_content) { + Ok(c) => c, Err(err) => { + print_error(&format!("Parse config file: {:?}, error: {}", &config_file, err)); + return; + }, + }; + + if keep_running_config.show_debug_output.unwrap_or(false) { + if let Ok(json) = serde_json::to_string_pretty(&keep_running_config) { + print_debug(&format!("Config: {}", json)); + } + } + + let keep_running_config: Arc = Arc::new(keep_running_config); + for check_cnt in 0.. { + print_info(&format!("Check index: {} @{:?}", check_cnt, Local::now())); + keep_runningd(keep_running_config.clone()); + thread::sleep(Duration::from_secs(keep_running_config.check_inverval_secs.unwrap_or(60 * 60))); + } +} + +fn keep_runningd(keep_running_config: Arc) { + let t = thread::spawn(move || { + let ps_aux_lines = read_str_to_lines(&match ps_aux() { Some(p) => p, None => return, }); + for keep_running_config_item in &keep_running_config.items { + let check_lines = ps_aux_lines.iter().filter(|ln| { + keep_running_config_item.grep_tokens.iter().all(|t| ln.contains(t)) + }).collect::>(); + if check_lines.is_empty() { // if check fail! + print_info("Send DingTalk notification!"); + use tokio::runtime; + match runtime::Builder::new().basic_scheduler().enable_all().build() { + Err(err) => print_error(&format!("Prepare tokio runtime error: {}", err)), + Ok(mut rt) => { + let mut sb = String::with_capacity(1024); + sb.push_str(&format!("Check failed: {}", &keep_running_config_item.title)); + sb.push_str(&format!("\ncheck time: {:?}", Local::now())); + rt.block_on(send_notify(&keep_running_config.notify_token, &sb)); + }, + } + } else if keep_running_config.show_debug_output.unwrap_or(false) { + print_debug(&format!("Find: {:?}", &check_lines)); + } + } + }); + if let Err(err) = t.join() { + print_error(&format!("Join check thread error: {:?}", err)); + } +} + +async fn send_notify(notify_token: &str, text: &str) { + match DingTalk::from_token(¬ify_token) { + Err(err) => print_error(&format!("Prepare DingTalk error: {}", err)), + Ok(dt) => if let Err(err) = dt.send_text(text).await { + print_error(&format!("Send DingTalk message error: {}", err)); + }, + } } fn ps_aux() -> Option { @@ -39,16 +126,17 @@ fn ps_aux() -> Option { }, }; match String::from_utf8(output.stdout) { - Ok(output_str) => Some(output_str), Err(err) => { + Ok(output_str) => Some(output_str), + Err(err) => { print_error(&format!("Get ps output as utf8 error: {}", err)); - return None; + None }, } } +/// Split string to lines, splited by '\r', '\n' or "\r\n" fn read_str_to_lines(s: &str) -> Vec { let mut r = vec![]; - let mut line = String::new(); let mut cs = s.chars(); while let Some(c) = cs.next() { @@ -64,6 +152,8 @@ fn read_str_to_lines(s: &str) -> Vec { } else { line.push(nc); } + } else { + break; } } } else {