diff --git a/Cargo.lock b/Cargo.lock index ac4be7d..70ff3d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,20 @@ name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "getopts" version = "0.2.21" @@ -196,11 +210,33 @@ dependencies = [ "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lazycell" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "libc" version = "0.2.62" @@ -214,6 +250,67 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mio" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-child-process" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -357,6 +454,11 @@ dependencies = [ "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "sup" version = "0.1.0" @@ -367,6 +469,8 @@ dependencies = [ "fern 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-child-process 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -421,6 +525,11 @@ name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.3.8" @@ -430,6 +539,11 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -451,6 +565,15 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" @@ -476,10 +599,20 @@ dependencies = [ "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fern 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e69ab0d5aca163e388c3a49d284fed6c3d0810700e77c5ae2756a50ec1a4daaa" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +"checksum mio-child-process 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93e2c2ad8adb5feea8031c9b23b17e359dd65a75b32214660686edec80be0bf4" +"checksum mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" @@ -498,13 +631,17 @@ dependencies = [ "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" "checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" "checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef84b96d10db72dd980056666d7f1e7663ce93d82fa33b63e71c966f4cf5032" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/Cargo.toml b/Cargo.toml index 45e9c8d..2a41d51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,8 @@ dirs = "2.0" fern = { version = "0.5", features = ["colored"] } getopts = "0.2" log = "0.4" +mio = "0.6" +mio-child-process = "0.2" serde = "1.0" serde_derive = "1.0" toml = "0.5" diff --git a/src/app.rs b/src/app.rs index 85a6e57..1b696b4 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,19 +2,29 @@ use std::process::{Command, ExitStatus}; use crate::result::Result; +/// structure to define an app config #[derive(Clone, Debug, Deserialize)] pub struct App { + /// name of the app pub name: String, + /// path/command to run pub command: String, + /// arguments pub args: Vec, + /// should the app be restarted if it exits successfully pub restart_on_success: Option, + /// should the app be restarted if it exits unsuccessfully pub restart_on_failure: Option, + /// should the app be restarted if it is terminated pub restart_on_terminate: Option, + /// on sup startup, should we wait for this app to run before continuting pub wait: Option, + /// should this app keep sup from exiting, if it's running pub hold: Option, } impl App { + /// start the application and wait for it to exit pub fn wait_start(&self) -> Result<()> { info!("starting application {}", &self.name); @@ -41,7 +51,8 @@ impl App { } } - pub fn handle_exit(&self, status: ExitStatus) -> bool { + /// see if the app should be restarted + pub fn check_restart(&self, status: ExitStatus) -> bool { if status.success() { info!("application exited: {}", &self.name); self.restart_on_success.unwrap_or(false) diff --git a/src/apps.rs b/src/apps.rs index 1c6c8a2..4c1f0f4 100644 --- a/src/apps.rs +++ b/src/apps.rs @@ -3,6 +3,8 @@ use std::fs::File; use std::io::Read; use std::path::PathBuf; +use mio::{Poll, PollOpt, Ready}; + use crate::app::App; use crate::proc::Proc; use crate::result::Result; @@ -13,8 +15,10 @@ pub struct Apps { } impl Apps { + /// load apps from toml file pub fn load(p: PathBuf) -> Result { let path: String = p.display().to_string(); + match File::open(p) { Ok(mut file) => { let mut buf = String::new(); @@ -43,10 +47,12 @@ impl Apps { self.app.clone() } - pub fn start(&self) -> (Vec, Option) { + pub fn start(&self) -> (Vec, Option, Poll) { let mut procs: Vec = Vec::new(); let mut holds: Option = None; + let poll = Poll::new().unwrap(); + for app in self.apps() { if app.wait.unwrap_or(false) { if let Err(e) = app.wait_start() { @@ -60,6 +66,8 @@ impl Apps { if proc.app.hold.unwrap_or(false) { holds = Some(holds.unwrap_or(0) + 1); } + poll.register(&proc.process, proc.token, Ready::all(), PollOpt::edge()) + .unwrap(); procs.push(proc); } Err(e) => { @@ -70,60 +78,8 @@ impl Apps { } } - (procs, holds) - } - - pub fn run(&self, procs: Vec, holds: Option) -> Result> { - let mut procs = procs; - let mut holds = holds; - - if holds.unwrap_or(0) < 1 { - error!("no holds configured"); - return Ok(procs); - } - - while holds > Some(0) { - let mut newprocs: Vec = Vec::new(); - - while let Some(mut proc) = procs.pop() { - match proc.child.try_wait() { - Ok(Some(status)) => { - let hold = proc.app.hold.unwrap_or(false); - if proc.app.handle_exit(status) { - let name = proc.app.name.clone(); - match Proc::start(proc.app) { - Ok(p) => newprocs.push(p), - Err(e) => { - error!("error restarting {}", name); - error!("{:?}", e); - if hold { - holds = Some(holds.unwrap_or(0) - 1); - } - } - }; - } else if hold { - holds = Some(holds.unwrap_or(0) - 1); - } - } - Ok(None) => { - if let Some(s) = &proc.check_stdout() { - for line in s.lines() { - info!("[{}] stdout: {}", &proc.app.name, line); - } - } - if let Some(s) = &proc.check_stderr() { - for line in s.lines() { - info!("[{}] stderr: {}", &proc.app.name, line); - } - } - newprocs.push(proc); - } - Err(e) => error!("error: {:?}", e), - } - } - procs = newprocs; - } + info!("holds: {:?}", holds); - Ok(procs) + (procs, holds, poll) } } diff --git a/src/config.rs b/src/config.rs index 01c8166..e21da7d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -54,7 +54,7 @@ impl Config { let log_file = if let Some(f) = matches.opt_str("f") { PathBuf::from(f) } else { - let mut p = PathBuf::new(); + let mut p = PathBuf::from(&config_directory); p.push("sup.log"); p }; diff --git a/src/logger.rs b/src/logger.rs index cbbc211..4107a2d 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -2,13 +2,13 @@ use std::path::PathBuf; use chrono::Local; use colored::Colorize; -use fern::{Dispatch, log_file}; use fern::colors::{Color, ColoredLevelConfig}; +use fern::{log_file, Dispatch}; use log::LevelFilter; use crate::result::Result; -pub fn setup_logger(logfile: PathBuf, level: LevelFilter) -> Result<()> { +pub fn setup(logfile: PathBuf, level: LevelFilter) -> Result<()> { Dispatch::new() .format(|out, message, record| { let colors = ColoredLevelConfig::new() @@ -20,7 +20,11 @@ pub fn setup_logger(logfile: PathBuf, level: LevelFilter) -> Result<()> { out.finish(format_args!( "{} {} {}", - Local::now().format("%Y-%m-%dT%H:%M:%S%.3f%z").to_string().white().bold(), + Local::now() + .format("%Y-%m-%dT%H:%M:%S%.3f%z") + .to_string() + .white() + .bold(), colors.color(record.level()), message )) diff --git a/src/main.rs b/src/main.rs index 8e6c282..a85c164 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ extern crate fern; extern crate getopts; #[macro_use] extern crate log; +extern crate mio; +extern crate mio_child_process; extern crate serde; #[macro_use] extern crate serde_derive; @@ -16,6 +18,7 @@ mod config; mod logger; mod proc; mod result; +mod run; use std::path::PathBuf; @@ -23,11 +26,12 @@ use apps::Apps; use config::Config; use proc::Proc; use result::Result; +use run::run; fn main() { let config = Config::from_args(); - if let Err(e) = logger::setup_logger(config.log_file, config.log_level) { + if let Err(e) = logger::setup(config.log_file, config.log_level) { println!("FATAL: Error initializing logger: {:?}", e); std::process::exit(1); } @@ -51,7 +55,7 @@ fn main() { fn startup(apps_file: PathBuf) -> Result> { let apps = Apps::load(apps_file)?; - let (procs, holds) = apps.start(); - let procs = apps.run(procs, holds)?; + let (procs, holds, poll) = apps.start(); + let procs = run(procs, holds, poll)?; Ok(procs) } diff --git a/src/proc.rs b/src/proc.rs index 7173bcc..e0ffc52 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,12 +1,16 @@ -use std::io::Read; -use std::process::{Child, Command, Stdio}; +use std::convert::TryInto; +use std::process::{Command, Stdio}; + +use mio::Token; +use mio_child_process::{CommandAsync, Process}; use crate::app::App; use crate::result::Result; pub struct Proc { pub app: App, - pub child: Child, + pub process: Process, + pub token: Token, } impl Proc { @@ -18,55 +22,18 @@ impl Proc { command.stderr(Stdio::piped()); command.args(&app.args); - let child = command.spawn()?; + let process = command.spawn_async()?; + let token = Token(process.id().try_into()?); - Ok(Proc { app, child }) + Ok(Proc { + app, + process, + token, + }) } pub fn stop(&mut self) { info!("stopping {}", &self.app.name); - let _ = self.child.kill(); - } - - pub fn check_stdout(&mut self) -> Option { - let child_stdout = self.child.stdout.as_mut(); - if let Some(stdout) = child_stdout { - let mut buf = String::new(); - match stdout.read_to_string(&mut buf) { - Ok(size) => { - if size > 0 { - return Some(buf); - } else { - return None; - } - } - Err(e) => { - error!("couldn't read stdout from {}: {:?}", &self.app.name, e); - return None; - } - } - } - None - } - - pub fn check_stderr(&mut self) -> Option { - let child_stderr = self.child.stderr.as_mut(); - if let Some(stderr) = child_stderr { - let mut buf = String::new(); - match stderr.read_to_string(&mut buf) { - Ok(size) => { - if size > 0 { - return Some(buf); - } else { - return None; - } - } - Err(e) => { - error!("couldn't read stderr from {}: {:?}", &self.app.name, e); - return None; - } - } - } - None + let _ = self.process.kill(); } } diff --git a/src/run.rs b/src/run.rs new file mode 100644 index 0000000..3f718ba --- /dev/null +++ b/src/run.rs @@ -0,0 +1,103 @@ +use std::sync::mpsc::TryRecvError; + +use mio::{Events, Poll, PollOpt, Ready, Token}; +use mio_child_process::{ProcessEvent, StdioChannel}; + +use crate::proc::Proc; +use crate::result::Result; + +/// check for holds and start the loop +pub fn run(procs: Vec, holds: Option, poll: Poll) -> Result> { + if holds.unwrap_or(0) < 1 { + error!("no holds configured"); + return Ok(procs); + } + + Ok(run_loop(procs, holds, poll)) +} + +/// restart an App, return the new Proc or report error +fn restart_app(proc: Proc) -> Result { + let app = proc.app.clone(); + let name = proc.app.name.clone(); + + match Proc::start(app) { + Ok(p) => Ok(p), + Err(e) => { + error!("error restarting {}", name); + error!("{:?}", e); + Err(e) + } + } +} + +fn find_proc_by_token(token: Token, procs: &[Proc]) -> Option { + for (counter, proc) in procs.iter().enumerate() { + if proc.token == token { + return Some(counter); + } + } + + None +} + +fn process_event_data(channel: StdioChannel, data: String, app_name: &str) { + let c = match channel { + StdioChannel::Stdout => "stdout", + StdioChannel::Stderr => "stderr", + }; + + info!("[{}] {}: {}", c, app_name, data); +} + +/// run the main loop until out of holds +fn run_loop(procs: Vec, holds: Option, poll: Poll) -> Vec { + let mut procs = procs; + let mut holds = holds; + + let mut events = Events::with_capacity(1024); + + while holds > Some(0) { + poll.poll(&mut events, None).unwrap(); + + for event in events.iter() { + if let Some(i) = find_proc_by_token(event.token(), &procs) { + let mut proc = procs.remove(i); + + match proc.process.try_recv() { + Ok(ProcessEvent::Data(channel, data)) => { + process_event_data(channel, data, &proc.app.name); + procs.push(proc); + } + Ok(ProcessEvent::CommandError(e)) => { + error!("[{}]: command error: {:?}", &proc.app.name, e) + } + Ok(ProcessEvent::IoError(channel, e)) => { + error!("[{}]: io error on {:?}: {:?}", &proc.app.name, channel, e) + } + Ok(ProcessEvent::Utf8Error(channel, e)) => { + error!("[{}]: utf8 error on {:?}: {:?}", &proc.app.name, channel, e) + } + Ok(ProcessEvent::Exit(status)) => { + let hold = proc.app.hold.unwrap_or(false); + if proc.app.check_restart(status) { + if let Ok(p) = restart_app(proc) { + poll.register(&p.process, p.token, Ready::all(), PollOpt::edge()) + .unwrap(); + procs.push(p); + } else if hold { + holds = Some(holds.unwrap_or(0) - 1); + } + } else if hold { + holds = Some(holds.unwrap_or(0) - 1); + } + } + Err(TryRecvError::Empty) => {} + Err(TryRecvError::Disconnected) => {} + }; + } + } + } + + procs +}