util/cli/async_daemonize: use log_file to explicitly write to, impl signal handler using async signal hooks

This commit is contained in:
aggstam
2023-07-04 13:17:26 +03:00
parent 6c0b5ea2bb
commit a63a808992
5 changed files with 127 additions and 49 deletions

3
Cargo.lock generated
View File

@@ -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",

View File

@@ -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

View File

@@ -7,4 +7,4 @@
## uncommenting, or by using the command-line.
# Enable testing mode for local testing
testing_node = false
testing_mode = false

View File

@@ -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(())
}

View File

@@ -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(())
}
};
}