#[macro_use]
extern crate slog;
use std::{fs::File, io::BufReader};
use std::process::ExitCode;
use clap::Parser;
use slog::Logger;
use sloggers::Build;
use sloggers::terminal::{Destination, TerminalLoggerBuilder};
use sloggers::types::{Severity, SourceLocation};
#[tokio::main]
async fn main() -> ExitCode {
let mut args = EtcdCliArgs::parse();
let config = if let Ok(f) = File::open(&args.config_path) {
match args.parse_from(BufReader::new(f)) {
Ok(a) => a,
Err(e) => {
eprintln!("Error parsing config file {}: {}", args.config_path.display(), e);
return ExitCode::from(11);
}
}
} else {
EtcdConfig::from(&mut args.config)
};
let log = logger(&config);
match EtcdNode::init(
config, log.clone(),
#[cfg(feature = "tracer")] None,
).await {
Ok(c) => {
if let Err(e) = c.serve().await {
crit!(log, "Error: {}", e);
return ExitCode::from(12);
}
}
Err(e) => {
crit!(log, "Error: {}", e);
return ExitCode::from(13);
}
}
match wait_for_signal().await {
Ok(msg) => {
exit_with_msg(log, msg, 0)
}
Err(msg) => {
exit_with_msg(log, msg, 10)
}
}
}
#[cfg(unix)]
use tokio::signal::unix::*;
use etcds::cli::{EtcdCliArgs, EtcdConfig};
use etcds::cluster::EtcdNode;
#[cfg(unix)]
async fn wait_for_signal() -> Result<String, String> {
let mut int_stream = signal(SignalKind::interrupt()).map_err(|e| e.to_string())?;
let mut term_stream = signal(SignalKind::terminate()).map_err(|e| e.to_string())?;
let mut hangup_stream = signal(SignalKind::hangup()).map_err(|e| e.to_string())?;
let sig = tokio::select! {
_ = int_stream.recv() => {"interrupt"},
_ = term_stream.recv() => {"terminate"},
_ = hangup_stream.recv() => {"hangup"},
};
Ok(sig.to_string())
}
#[cfg(windows)]
async fn wait_for_signal() -> Result<String, String> {
tokio::signal::ctrl_c().await
.map(|_| format!("got CTRL-C."))
.map_err(|err| format!("Unable to listen for shutdown signal: {}", err))
}
pub fn exit_with_msg(log: Logger, msg: String, code: u8) -> ExitCode {
#[cfg(test)]
assert!(false, "\n{}", msg);
crit!(log, "\n{}", msg);
ExitCode::from(code)
}
pub fn logger(cfg: &EtcdConfig) -> Logger {
let mut builder = TerminalLoggerBuilder::new();
builder.channel_size(10240);
builder.destination(Destination::Stdout);
#[cfg(debug_assertions)]
builder.source_location(SourceLocation::LocalFileAndLine);
#[cfg(not(debug_assertions))]
builder.source_location(SourceLocation::None);
builder.level(severity_from_string(&cfg.log_level));
builder.overflow_strategy(sloggers::types::OverflowStrategy::Drop);
builder.build().unwrap()
}
#[inline]
fn severity_from_string(severity: &String) -> Severity {
match severity.to_lowercase().as_str() {
"error" => Severity::Error,
"warning" => Severity::Warning,
"info" => Severity::Info,
"debug" => Severity::Debug,
"trace" => Severity::Trace,
_ => Severity::Critical,
}
}