mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 14:48:08 -05:00
util/cli/async_daemonize: use log_file to explicitly write to, impl signal handler using async signal hooks
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -1802,13 +1802,14 @@ name = "darkfid2"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"ctrlc",
|
||||
"darkfi",
|
||||
"darkfi-contract-test-harness",
|
||||
"darkfi-sdk",
|
||||
"easy-parallel",
|
||||
"log",
|
||||
"serde",
|
||||
"signal-hook",
|
||||
"signal-hook-async-std",
|
||||
"simplelog",
|
||||
"sled",
|
||||
"smol",
|
||||
|
||||
@@ -9,15 +9,18 @@ license = "AGPL-3.0-only"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
async-std = "1.12.0"
|
||||
ctrlc = { version = "3.4.0", features = ["termination"] }
|
||||
darkfi = {path = "../../", features = ["async-runtime", "util"]}
|
||||
darkfi-contract-test-harness = {path = "../../src/contract/test-harness"}
|
||||
darkfi-sdk = {path = "../../src/sdk"}
|
||||
easy-parallel = "3.3.0"
|
||||
log = "0.4.19"
|
||||
simplelog = "0.12.1"
|
||||
sled = "0.34.7"
|
||||
|
||||
# Daemon
|
||||
async-std = "1.12.0"
|
||||
easy-parallel = "3.3.0"
|
||||
signal-hook-async-std = "0.2.2"
|
||||
signal-hook = "0.3.15"
|
||||
simplelog = "0.12.1"
|
||||
smol = "1.3.0"
|
||||
|
||||
# Argument parsing
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
## uncommenting, or by using the command-line.
|
||||
|
||||
# Enable testing mode for local testing
|
||||
testing_node = false
|
||||
testing_mode = false
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use async_std::sync::Arc;
|
||||
use async_std::{stream::StreamExt, sync::Arc};
|
||||
use log::info;
|
||||
use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
|
||||
|
||||
@@ -28,6 +28,7 @@ use darkfi::{
|
||||
validator::{Validator, ValidatorConfig, ValidatorPtr},
|
||||
Result,
|
||||
};
|
||||
use darkfi_contract_test_harness::vks;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -45,7 +46,11 @@ struct Args {
|
||||
|
||||
#[structopt(long)]
|
||||
/// Enable testing mode for local testing
|
||||
testing_node: bool,
|
||||
testing_mode: bool,
|
||||
|
||||
#[structopt(short, long)]
|
||||
/// Set log file to ouput into
|
||||
log: Option<String>,
|
||||
|
||||
#[structopt(short, parse(from_occurrences))]
|
||||
/// Increase verbosity (-vvv supported)
|
||||
@@ -66,25 +71,20 @@ async_daemonize!(realmain);
|
||||
async fn realmain(args: Args, _ex: Arc<smol::Executor<'_>>) -> Result<()> {
|
||||
info!("Initializing DarkFi node...");
|
||||
|
||||
// We use this handler to block this function after detaching all
|
||||
// tasks, and to catch a shutdown signal, where we can clean up and
|
||||
// exit gracefully.
|
||||
let (signal, shutdown) = smol::channel::bounded::<()>(1);
|
||||
ctrlc::set_handler(move || {
|
||||
async_std::task::block_on(signal.send(())).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
// Signal handling for graceful termination.
|
||||
let (signals_handler, signals_task) = SignalHandler::new()?;
|
||||
|
||||
// NOTE: everything is dummy for now
|
||||
// Initialize or open sled database
|
||||
let sled_db = sled::Config::new().temporary(true).open()?;
|
||||
vks::inject(&sled_db)?;
|
||||
|
||||
// Initialize validator configuration
|
||||
let genesis_block = BlockInfo::default();
|
||||
let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
|
||||
let config = ValidatorConfig::new(time_keeper, genesis_block, vec![], args.testing_node);
|
||||
let config = ValidatorConfig::new(time_keeper, genesis_block, vec![], args.testing_mode);
|
||||
|
||||
if args.testing_node {
|
||||
if args.testing_mode {
|
||||
info!("Node is configured to run in testing mode!");
|
||||
}
|
||||
|
||||
@@ -95,10 +95,8 @@ async fn realmain(args: Args, _ex: Arc<smol::Executor<'_>>) -> Result<()> {
|
||||
let _darkfid = Darkfid::new(validator).await;
|
||||
info!("Node initialized successfully!");
|
||||
|
||||
// Wait for SIGINT
|
||||
shutdown.recv().await?;
|
||||
print!("\r");
|
||||
// Wait for termination signal
|
||||
signals_handler.wait_termination(signals_task).await?;
|
||||
info!("Caught termination signal, cleaning up and exiting...");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
130
src/util/cli.rs
130
src/util/cli.rs
@@ -119,6 +119,8 @@ pub fn get_log_config(verbosity_level: u8) -> simplelog::Config {
|
||||
/// async-std = "1.12.0"
|
||||
/// darkfi = { path = "../../", features = ["util"] }
|
||||
/// easy-parallel = "3.2.0"
|
||||
/// signal-hook-async-std = "0.2.2"
|
||||
/// signal-hook = "0.3.15"
|
||||
/// simplelog = "0.12.0"
|
||||
/// smol = "1.2.5"
|
||||
///
|
||||
@@ -130,7 +132,7 @@ pub fn get_log_config(verbosity_level: u8) -> simplelog::Config {
|
||||
///
|
||||
/// Example usage:
|
||||
/// ```
|
||||
/// use async_std::sync::Arc;
|
||||
/// use async_std::{stream::StreamExt, sync::Arc};
|
||||
// use darkfi::{async_daemonize, cli_desc, Result};
|
||||
/// use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
|
||||
///
|
||||
@@ -145,6 +147,10 @@ pub fn get_log_config(verbosity_level: u8) -> simplelog::Config {
|
||||
/// /// Configuration file to use
|
||||
/// config: Option<String>,
|
||||
///
|
||||
/// #[structopt(short, long)]
|
||||
/// /// Set log file to ouput into
|
||||
/// log: Option<String>,
|
||||
///
|
||||
/// #[structopt(short, parse(from_occurrences))]
|
||||
/// /// Increase verbosity (-vvv supported)
|
||||
/// verbose: u8,
|
||||
@@ -169,33 +175,27 @@ macro_rules! async_daemonize {
|
||||
let log_level = darkfi::util::cli::get_log_level(args.verbose);
|
||||
let log_config = darkfi::util::cli::get_log_config(args.verbose);
|
||||
|
||||
/* FIXME: This is an issue. We should only log when explicitly told to
|
||||
let log_file_path = match std::env::var("DARKFI_LOG") {
|
||||
Ok(p) => p,
|
||||
Err(_) => {
|
||||
let bin_name = if let Some(bin_name) = option_env!("CARGO_BIN_NAME") {
|
||||
bin_name
|
||||
} else {
|
||||
"darkfi"
|
||||
};
|
||||
std::fs::create_dir_all(darkfi::util::path::expand_path("~/.local/darkfi")?)?;
|
||||
format!("~/.local/darkfi/{}.log", bin_name)
|
||||
// Setup terminal logger
|
||||
let term_logger = simplelog::TermLogger::new(
|
||||
log_level,
|
||||
log_config.clone(),
|
||||
simplelog::TerminalMode::Mixed,
|
||||
simplelog::ColorChoice::Auto,
|
||||
);
|
||||
|
||||
// If a log file has been configured, also create a write logger.
|
||||
// Otherwise, output to terminal logger only.
|
||||
match args.log {
|
||||
Some(ref log_path) => {
|
||||
let log_path = darkfi::util::path::expand_path(log_path)?;
|
||||
let log_file = std::fs::File::create(log_path)?;
|
||||
let write_logger = simplelog::WriteLogger::new(log_level, log_config, log_file);
|
||||
simplelog::CombinedLogger::init(vec![term_logger, write_logger])?;
|
||||
}
|
||||
};
|
||||
|
||||
let log_file_path = darkfi::util::path::expand_path(&log_file_path)?;
|
||||
let log_file = std::fs::File::create(log_file_path)?;
|
||||
*/
|
||||
|
||||
simplelog::CombinedLogger::init(vec![
|
||||
simplelog::TermLogger::new(
|
||||
log_level,
|
||||
log_config.clone(),
|
||||
simplelog::TerminalMode::Mixed,
|
||||
simplelog::ColorChoice::Auto,
|
||||
),
|
||||
//simplelog::WriteLogger::new(log_level, log_config, log_file),
|
||||
])?;
|
||||
None => {
|
||||
simplelog::CombinedLogger::init(vec![term_logger])?;
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.rs/smol/latest/smol/struct.Executor.html#examples
|
||||
let n_threads = std::thread::available_parallelism().unwrap().get();
|
||||
@@ -215,6 +215,82 @@ macro_rules! async_daemonize {
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Auxiliary structure used to keep track of signals
|
||||
struct SignalHandler {
|
||||
/// Termination signal channel receiver
|
||||
term_rx: smol::channel::Receiver<()>,
|
||||
/// Signals handle
|
||||
handle: signal_hook_async_std::Handle,
|
||||
/// SIGHUP subscriber to retrieve new configuration,
|
||||
sighup_sub: darkfi::system::SubscriberPtr<Args>,
|
||||
}
|
||||
|
||||
impl SignalHandler {
|
||||
fn new() -> Result<(Self, async_std::task::JoinHandle<Result<()>>)> {
|
||||
let (term_tx, term_rx) = smol::channel::bounded::<()>(1);
|
||||
let signals = signal_hook_async_std::Signals::new([
|
||||
signal_hook::consts::SIGHUP,
|
||||
signal_hook::consts::SIGTERM,
|
||||
signal_hook::consts::SIGINT,
|
||||
signal_hook::consts::SIGQUIT,
|
||||
])?;
|
||||
let handle = signals.handle();
|
||||
let sighup_sub = darkfi::system::Subscriber::new();
|
||||
let signals_task =
|
||||
async_std::task::spawn(handle_signals(signals, term_tx, sighup_sub.clone()));
|
||||
|
||||
Ok((Self { term_rx, handle, sighup_sub }, signals_task))
|
||||
}
|
||||
|
||||
/// Handler waits for termination signal
|
||||
async fn wait_termination(
|
||||
&self,
|
||||
signals_task: async_std::task::JoinHandle<Result<()>>,
|
||||
) -> Result<()> {
|
||||
self.term_rx.recv().await?;
|
||||
print!("\r");
|
||||
self.handle.close();
|
||||
signals_task.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Auxiliary task to handle SIGHUP, SIGTERM, SIGINT and SIGQUIT signals
|
||||
async fn handle_signals(
|
||||
mut signals: signal_hook_async_std::Signals,
|
||||
term_tx: smol::channel::Sender<()>,
|
||||
subscriber: darkfi::system::SubscriberPtr<Args>,
|
||||
) -> Result<()> {
|
||||
while let Some(signal) = signals.next().await {
|
||||
match signal {
|
||||
signal_hook::consts::SIGHUP => {
|
||||
let args = Args::from_args_with_toml("").unwrap();
|
||||
let cfg_path =
|
||||
darkfi::util::path::get_config_path(args.config, CONFIG_FILE)?;
|
||||
darkfi::util::cli::spawn_config(
|
||||
&cfg_path,
|
||||
CONFIG_FILE_CONTENTS.as_bytes(),
|
||||
)?;
|
||||
let args = Args::from_args_with_toml(&std::fs::read_to_string(cfg_path)?);
|
||||
if args.is_err() {
|
||||
println!("handle_signals():: Error parsing the config file");
|
||||
continue
|
||||
}
|
||||
subscriber.notify(args.unwrap()).await;
|
||||
}
|
||||
signal_hook::consts::SIGTERM |
|
||||
signal_hook::consts::SIGINT |
|
||||
signal_hook::consts::SIGQUIT => {
|
||||
term_tx.send(()).await?;
|
||||
}
|
||||
|
||||
_ => println!("handle_signals():: Unsupported signal"),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user