diff --git a/Cargo.lock b/Cargo.lock index 0e813b6..311ef9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,174 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "proc-macro2" version = "1.0.86" @@ -26,20 +188,42 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags", +] + [[package]] name = "runner-rs" version = "0.1.0" dependencies = [ "serde", "serde_json", + "tokio", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.203" @@ -71,6 +255,31 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "syn" version = "2.0.68" @@ -82,8 +291,183 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml index ae13396..77fcacd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.118" +tokio = { version = "1.38.0", features = ["full", "time"] } diff --git a/settings.json b/settings.json index 4d37d8a..ba72219 100644 --- a/settings.json +++ b/settings.json @@ -15,7 +15,11 @@ ], "services" : [{ "hostname" : "ya.ru", - "port" : 443 + "port" : 443, + "triggers" : { + "wait" : 200, + "delay" : 3 + } }] } }] diff --git a/src/main.rs b/src/main.rs index 1973ec3..49de701 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,14 @@ use serde::{Deserialize, Serialize}; -use serde_json::{self, error}; +use serde_json; +use std::fmt::Debug; use std::fs; use std::path::Path; use std::process::Command; +// to use in time-trigger +use tokio::time::{sleep, Duration}; +// to store condition between asynchronous tasks +use tokio::sync::mpsc; + enum CustomError { Fatal, @@ -38,9 +44,17 @@ struct Files { struct Services { hostname : String, port : u32, + triggers : ServiceTriggers, } -fn main() { +#[derive(Debug, Serialize, Deserialize)] +struct ServiceTriggers { + wait : u32, + delay: u32, +} + +#[tokio::main] +async fn main() { let processes = load_processes("settings.json"); let mut error_counter = 0; @@ -48,47 +62,90 @@ fn main() { eprintln!("Error: Processes list is null, runner-rs initialization is stopped"); return; } + let mut handler: Vec> = vec![]; + let mut proc_clone: &TrackingProcess; + let mut tx_clone: mpsc::Sender; - processes.processes.iter().for_each(|proc| { + for proc in processes.processes.iter() { println!("\nProcess '{}' on stage:\n{}\nDepends on {} file(s), {} service(s)\n", proc.name, proc.path, proc.dependencies.files.len(), proc.dependencies.services.len()); - proc.dependencies.files.iter().for_each(|file| { + // creating msg channel + let (tx, mut rx) = mpsc::channel::(1); + + + for file in proc.dependencies.files.iter() { if let Err(_) = check_file(&file.filename, &file.src) { - println!("Error: Process {} cannot run without file {}{}", proc.name, file.src, file.filename); + eprintln!("Error: Process {} cannot run without file {}{}", proc.name, file.src, file.filename); error_counter += 1; } - }); - proc.dependencies.services.iter().for_each(|ser| { + } + for ser in proc.dependencies.services.iter() { if let Err(_) = check_service(&ser.hostname, &ser.port) { - println!("Error: Process {} cannot run while service {}:{} is down", proc.name, ser.hostname, ser.port); + eprintln!("Error: Process {} cannot run while service {}:{} is down", proc.name, ser.hostname, ser.port); error_counter += 1; } - }); + } - if (error_counter > 0) { + if error_counter > 0 { return; } - match start_process(&proc.name, &proc.path) { - Ok(_) => { - println!("Success!"); - }, - Err(_) => { - println!("Error: Cannot run process"); - }, - } - return; - }); + proc_clone = proc.clone(); + tx_clone = tx.clone(); + let event = tokio::spawn(async move { + run_daemons(proc_clone, tx_clone, &mut rx).await; + }); + handler.push(event); + } + for i in handler { + i.await.unwrap(); + } + return; } + +async fn run_daemons( + proc: &TrackingProcess, + tx: mpsc::Sender, + rx: &mut mpsc::Receiver +) { + + let run_hand = running_handler(&proc.name, &proc.path, tx.clone()); + let file_hand = file_handler(&proc.dependencies.files, tx.clone()); + let serv_hand = service_handler(&proc.dependencies.services, tx.clone()); + + tokio::select! { + _ = run_hand => {}, + _ = file_hand => {}, + _ = serv_hand => {}, + _ = rx.recv() => { + terminate_process(&proc.name).await; + println!("Process stopped due to error with dependency handling"); + }, + } +} + fn load_processes(json_filename: &str) -> Processes{ let read = fs::read_to_string(json_filename).expect(format!("Missing '{}' file. Cannot start runner", json_filename).as_str()); serde_json::from_str::(&read).expect(format!("Parsing error in '{}' file. Cannot start runner", json_filename).as_str()) } -fn start_process(name: &str, path: &str) -> Result<(), CustomError> { +fn is_active(name: &str)-> bool { + let output = Command::new("pidof") + .arg(name) + .output() + .expect("Failed to execute command 'pidof'"); + !String::from_utf8_lossy(&output.stdout).trim().is_empty() +} +async fn terminate_process (name: &str) { + let output = Command::new("pkill") + .arg(name) + .output() + .expect("Failed to execute command 'pkill'"); +} +async fn start_process(name: &str, path: &str, tx: mpsc::Sender) -> Result<(), CustomError> { let runsh = format!("{}{}", path, "/run.sh"); let mut command = Command::new("bash"); command.arg(runsh); @@ -96,10 +153,48 @@ fn start_process(name: &str, path: &str) -> Result<(), CustomError> { match command.spawn() { Ok(mut child) => { println!("Process {} is running now!", name); - child.wait(); - return Ok(()); + Ok(()) }, - Err(_) => return Err(CustomError::Fatal), + Err(_) => { + let _ = tx.send(1).await; + return Err(CustomError::Fatal) + }, + } +} +// check process status daemon +async fn running_handler(name: &str, path: &str, tx: mpsc::Sender){ + loop { + let output = Command::new("pidof") + .arg(name) + .output() + .expect("Failed to execute command 'pidof'"); + + // is down + if !is_active(name) { + match start_process(name, path, tx.clone()).await { + Ok(_) => {}, + Err(_) => { + tx.send(1).await.unwrap(); + }, + } + } + tokio::time::sleep(Duration::from_millis(100)).await; + } +} + +async fn file_handler(files: &Vec, tx: mpsc::Sender) { + loop { + for file in files { + match check_file(&file.filename, &file.src) { + Ok(_) => { + println!("{} is still in directory!", &file.filename); + }, + Err(_) => { + tx.send(1).await.unwrap(); + }, + } + } + tokio::time::sleep(Duration::from_millis(100)).await; } } fn check_file(filename: &str, path: &str) -> Result<(), CustomError> { @@ -111,13 +206,29 @@ fn check_file(filename: &str, path: &str) -> Result<(), CustomError> { Err(CustomError::Fatal) } } + +async fn service_handler(services: &Vec, tx: mpsc::Sender) { + loop { + for serv in services { + match check_service(&serv.hostname, &serv.port) { + Ok(_) => { + println!("{} is up!", &serv.hostname); + }, + Err(_) => { + tx.send(1).await.unwrap(); + }, + } + } + tokio::time::sleep(Duration::from_millis(100)).await; + } +} fn check_service(host: &str, port: &u32) -> Result<(), CustomError> { let mut command = Command::new("bash"); command.args(["service-checker.sh", host, &port.to_string()]); match command.output() { Ok(output) => { - if (output.status.success()) { + if output.status.success() { return Ok(()); } else { return Err(CustomError::Fatal);