use radvisor::cli::{self, Command, Opts, RunCommand};
use radvisor::collection;
use radvisor::polling;
use radvisor::polling::providers::Provider;
use radvisor::shared::{CollectionEvent, IntervalWorkerContext};
use radvisor::shell::{self, Shell};
use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use bus::Bus;
#[cfg(not(target_os = "linux"))]
fn target_check() {
compile_error!(
"rAdvisor only compiles for Linux targets. To request support for additional \
platforms, feel free to file an issue at https://github.com/elba-docker/radvisor/issues/new"
);
}
fn main() {
human_panic::setup_panic!(human_panic::Metadata {
name: env!("CARGO_PKG_NAME").into(),
version: env!("CARGO_PKG_VERSION").into(),
authors: env!("CARGO_PKG_AUTHORS").into(),
homepage: "https://github.com/elba-docker/radvisor/issues/new".into(),
});
let opts: Opts = cli::load();
let shell = Arc::new(shell::Shell::new(&opts.shell_options));
match opts.command {
Command::Run(run_opts) => {
run(run_opts, shell);
},
}
}
fn run(opts: RunCommand, shell: Arc<Shell>) {
let (tx, rx): (Sender<CollectionEvent>, Receiver<CollectionEvent>) = mpsc::channel();
let polling_opts = opts.provider.polling().clone();
let collection_opts = opts.provider.collection().clone();
let term_bus = initialize_term_handler(Arc::clone(&shell));
let mut term_bus_handle = term_bus.lock().unwrap();
let polling_context = IntervalWorkerContext {
interval: polling_opts.interval,
term_rx: term_bus_handle.add_rx(),
shell: Arc::clone(&shell),
};
let collection_context = IntervalWorkerContext {
interval: collection_opts.interval,
term_rx: term_bus_handle.add_rx(),
shell: Arc::clone(&shell),
};
drop(term_bus_handle);
let polling_thread: thread::JoinHandle<()> = thread::Builder::new()
.name(String::from("poll"))
.spawn(move || {
let mut provider: Box<dyn Provider> = opts.provider.get_impl();
let provider_shell = Arc::clone(&polling_context.shell);
if let Err(err) = provider.initialize(&opts, provider_shell) {
let mut message = err.suggestion.clone();
if let Some(original) = err.original {
polling_context.shell.verbose(|_| {
let formatted = format!("\n\n{}", original);
message.push_str(&formatted);
});
}
polling_context.shell.error(message);
std::process::exit(1);
}
polling::run(&tx, polling_context, provider)
})
.unwrap();
let collection_thread: thread::JoinHandle<()> = thread::Builder::new()
.name(String::from("collect"))
.spawn(move || collection::run(&rx, collection_context, &collection_opts))
.unwrap();
if collection_thread.join().is_err() {
shell.error("Error: collection thread resulted in panic");
}
if polling_thread.join().is_err() {
shell.error("Error: polling thread resulted in panic");
}
shell.status("Exiting", "rAdvisor");
}
fn initialize_term_handler(shell: Arc<Shell>) -> Arc<Mutex<Bus<()>>> {
let term_bus = Arc::new(Mutex::new(Bus::new(1)));
let term_bus_c = Arc::clone(&term_bus);
ctrlc::set_handler(move || handle_termination(&term_bus_c, Arc::clone(&shell)))
.expect("Error: could not create SIGINT handler");
term_bus
}
fn handle_termination(bus_lock: &Arc<Mutex<Bus<()>>>, shell: Arc<Shell>) -> ! {
let mut bus = bus_lock.lock().unwrap();
bus.broadcast(());
thread::sleep(Duration::from_millis(2000));
shell.warn("Could not shutdown gracefully on the first try. Trying again...");
bus.broadcast(());
thread::sleep(Duration::from_millis(1000));
shell.warn("Forcibly closing; buffers may not be flushed.");
std::process::exit(2);
}