cli.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{
  19. env, fs,
  20. io::Write,
  21. path::Path,
  22. str,
  23. sync::{Arc, Mutex},
  24. time::Instant,
  25. };
  26. use simplelog::ConfigBuilder;
  27. use crate::Result;
  28. /*
  29. #[derive(Clone, Default)]
  30. pub struct Config<T> {
  31. config: PhantomData<T>,
  32. }
  33. impl<T: Serialize + DeserializeOwned> Config<T> {
  34. pub fn load(path: PathBuf) -> Result<T> {
  35. if Path::new(&path).exists() {
  36. let toml = fs::read(&path)?;
  37. let str_buff = str::from_utf8(&toml)?;
  38. let config: T = toml::from_str(str_buff)?;
  39. Ok(config)
  40. } else {
  41. let path = path.to_str();
  42. if path.is_some() {
  43. println!("Could not find/parse configuration file in: {}", path.unwrap());
  44. } else {
  45. println!("Could not find/parse configuration file");
  46. }
  47. println!("Please follow the instructions in the README");
  48. Err(Error::ConfigNotFound)
  49. }
  50. }
  51. }
  52. */
  53. pub fn spawn_config(path: &Path, contents: &[u8]) -> Result<()> {
  54. if !path.exists() {
  55. if let Some(parent) = path.parent() {
  56. fs::create_dir_all(parent)?;
  57. }
  58. let mut file = fs::File::create(path)?;
  59. file.write_all(contents)?;
  60. println!("Config file created in {:?}. Please review it and try again.", path);
  61. std::process::exit(2);
  62. }
  63. Ok(())
  64. }
  65. pub fn get_log_level(verbosity_level: u8) -> simplelog::LevelFilter {
  66. match verbosity_level {
  67. 0 => simplelog::LevelFilter::Info,
  68. 1 => simplelog::LevelFilter::Info,
  69. 2 => simplelog::LevelFilter::Debug,
  70. _ => simplelog::LevelFilter::Trace,
  71. }
  72. }
  73. pub fn get_log_config(verbosity_level: u8) -> simplelog::Config {
  74. match env::var("LOG_TARGETS") {
  75. Ok(x) => {
  76. let targets: Vec<String> = x.split(',').map(|x| x.to_string()).collect();
  77. let mut cfgbuilder = ConfigBuilder::new();
  78. match verbosity_level {
  79. 0 => cfgbuilder.set_target_level(simplelog::LevelFilter::Debug),
  80. _ => cfgbuilder.set_target_level(simplelog::LevelFilter::Error),
  81. };
  82. for i in targets {
  83. if i.starts_with('!') {
  84. cfgbuilder.add_filter_ignore(i.trim_start_matches('!').to_string());
  85. } else {
  86. cfgbuilder.add_filter_allow(i);
  87. }
  88. }
  89. cfgbuilder.build()
  90. }
  91. Err(_) => {
  92. let mut cfgbuilder = ConfigBuilder::new();
  93. match verbosity_level {
  94. 0 => cfgbuilder.set_target_level(simplelog::LevelFilter::Debug),
  95. _ => cfgbuilder.set_target_level(simplelog::LevelFilter::Error),
  96. };
  97. cfgbuilder.build()
  98. }
  99. }
  100. }
  101. /// This macro is used for a standard way of daemonizing darkfi binaries
  102. /// with TOML config file configuration, and argument parsing. It also
  103. /// spawns a multithreaded async executor and passes it into the given
  104. /// function.
  105. ///
  106. /// The Cargo.toml dependencies needed for this are:
  107. /// ```text
  108. /// darkfi = { path = "../../", features = ["util"] }
  109. /// easy-parallel = "3.2.0"
  110. /// signal-hook-async-std = "0.2.2"
  111. /// signal-hook = "0.3.15"
  112. /// simplelog = "0.12.0"
  113. /// smol = "1.2.5"
  114. ///
  115. /// # Argument parsing
  116. /// serde = {version = "1.0.135", features = ["derive"]}
  117. /// structopt = "0.3.26"
  118. /// structopt-toml = "0.5.1"
  119. /// ```
  120. ///
  121. /// Example usage:
  122. /// ```
  123. /// use darkfi::{async_daemonize, cli_desc, Result};
  124. /// use smol::stream::StreamExt;
  125. /// use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
  126. ///
  127. /// const CONFIG_FILE: &str = "daemond_config.toml";
  128. /// const CONFIG_FILE_CONTENTS: &str = include_str!("../daemond_config.toml");
  129. ///
  130. /// #[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
  131. /// #[serde(default)]
  132. /// #[structopt(name = "daemond", about = cli_desc!())]
  133. /// struct Args {
  134. /// #[structopt(short, long)]
  135. /// /// Configuration file to use
  136. /// config: Option<String>,
  137. ///
  138. /// #[structopt(short, long)]
  139. /// /// Set log file to ouput into
  140. /// log: Option<String>,
  141. ///
  142. /// #[structopt(short, parse(from_occurrences))]
  143. /// /// Increase verbosity (-vvv supported)
  144. /// verbose: u8,
  145. /// }
  146. ///
  147. /// async_daemonize!(realmain);
  148. /// async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
  149. /// println!("Hello, world!");
  150. /// Ok(())
  151. /// }
  152. /// ```
  153. #[cfg(feature = "async-daemonize")]
  154. #[macro_export]
  155. macro_rules! async_daemonize {
  156. ($realmain:ident) => {
  157. fn main() -> Result<()> {
  158. let args = Args::from_args_with_toml("").unwrap();
  159. let cfg_path = darkfi::util::path::get_config_path(args.config, CONFIG_FILE)?;
  160. darkfi::util::cli::spawn_config(&cfg_path, CONFIG_FILE_CONTENTS.as_bytes())?;
  161. let args = Args::from_args_with_toml(&std::fs::read_to_string(cfg_path)?).unwrap();
  162. let log_level = darkfi::util::cli::get_log_level(args.verbose);
  163. let log_config = darkfi::util::cli::get_log_config(args.verbose);
  164. // Setup terminal logger
  165. let term_logger = simplelog::TermLogger::new(
  166. log_level,
  167. log_config.clone(),
  168. simplelog::TerminalMode::Mixed,
  169. simplelog::ColorChoice::Auto,
  170. );
  171. // If a log file has been configured, also create a write logger.
  172. // Otherwise, output to terminal logger only.
  173. match args.log {
  174. Some(ref log_path) => {
  175. let log_path = darkfi::util::path::expand_path(log_path)?;
  176. let log_file = std::fs::File::create(log_path)?;
  177. let write_logger = simplelog::WriteLogger::new(log_level, log_config, log_file);
  178. simplelog::CombinedLogger::init(vec![term_logger, write_logger])?;
  179. }
  180. None => {
  181. simplelog::CombinedLogger::init(vec![term_logger])?;
  182. }
  183. }
  184. // https://docs.rs/smol/latest/smol/struct.Executor.html#examples
  185. let n_threads = std::thread::available_parallelism().unwrap().get();
  186. let ex = std::sync::Arc::new(smol::Executor::new());
  187. let (signal, shutdown) = smol::channel::unbounded::<()>();
  188. let (_, result) = easy_parallel::Parallel::new()
  189. // Run four executor threads
  190. .each(0..n_threads, |_| smol::future::block_on(ex.run(shutdown.recv())))
  191. // Run the main future on the current thread.
  192. .finish(|| {
  193. smol::future::block_on(async {
  194. $realmain(args, ex.clone()).await?;
  195. drop(signal);
  196. Ok::<(), darkfi::Error>(())
  197. })
  198. });
  199. result
  200. }
  201. /// Auxiliary structure used to keep track of signals
  202. struct SignalHandler {
  203. /// Termination signal channel receiver
  204. term_rx: smol::channel::Receiver<()>,
  205. /// Signals handle
  206. handle: signal_hook_async_std::Handle,
  207. /// SIGHUP subscriber to retrieve new configuration,
  208. sighup_sub: darkfi::system::SubscriberPtr<Args>,
  209. }
  210. impl SignalHandler {
  211. fn new(
  212. ex: std::sync::Arc<smol::Executor<'static>>,
  213. ) -> Result<(Self, smol::Task<Result<()>>)> {
  214. let (term_tx, term_rx) = smol::channel::bounded::<()>(1);
  215. let signals = signal_hook_async_std::Signals::new([
  216. signal_hook::consts::SIGHUP,
  217. signal_hook::consts::SIGTERM,
  218. signal_hook::consts::SIGINT,
  219. signal_hook::consts::SIGQUIT,
  220. ])?;
  221. let handle = signals.handle();
  222. let sighup_sub = darkfi::system::Subscriber::new();
  223. let signals_task = ex.spawn(handle_signals(signals, term_tx, sighup_sub.clone()));
  224. Ok((Self { term_rx, handle, sighup_sub }, signals_task))
  225. }
  226. /// Handler waits for termination signal
  227. async fn wait_termination(&self, signals_task: smol::Task<Result<()>>) -> Result<()> {
  228. self.term_rx.recv().await?;
  229. print!("\r");
  230. self.handle.close();
  231. signals_task.await?;
  232. Ok(())
  233. }
  234. }
  235. /// Auxiliary task to handle SIGHUP, SIGTERM, SIGINT and SIGQUIT signals
  236. async fn handle_signals(
  237. mut signals: signal_hook_async_std::Signals,
  238. term_tx: smol::channel::Sender<()>,
  239. subscriber: darkfi::system::SubscriberPtr<Args>,
  240. ) -> Result<()> {
  241. while let Some(signal) = signals.next().await {
  242. match signal {
  243. signal_hook::consts::SIGHUP => {
  244. let args = Args::from_args_with_toml("").unwrap();
  245. let cfg_path =
  246. darkfi::util::path::get_config_path(args.config, CONFIG_FILE)?;
  247. darkfi::util::cli::spawn_config(
  248. &cfg_path,
  249. CONFIG_FILE_CONTENTS.as_bytes(),
  250. )?;
  251. let args = Args::from_args_with_toml(&std::fs::read_to_string(cfg_path)?);
  252. if args.is_err() {
  253. println!("handle_signals():: Error parsing the config file");
  254. continue
  255. }
  256. subscriber.notify(args.unwrap()).await;
  257. }
  258. signal_hook::consts::SIGTERM |
  259. signal_hook::consts::SIGINT |
  260. signal_hook::consts::SIGQUIT => {
  261. term_tx.send(()).await?;
  262. }
  263. _ => println!("handle_signals():: Unsupported signal"),
  264. }
  265. }
  266. Ok(())
  267. }
  268. };
  269. }
  270. pub fn fg_red(message: &str) -> String {
  271. format!("\x1b[31m{}\x1b[0m", message)
  272. }
  273. pub fn fg_green(message: &str) -> String {
  274. format!("\x1b[32m{}\x1b[0m", message)
  275. }
  276. pub fn fg_reset() -> String {
  277. "\x1b[0m".to_string()
  278. }
  279. pub struct ProgressInc {
  280. position: Arc<Mutex<u64>>,
  281. timer: Arc<Mutex<Option<Instant>>>,
  282. }
  283. impl Default for ProgressInc {
  284. fn default() -> Self {
  285. Self::new()
  286. }
  287. }
  288. impl ProgressInc {
  289. pub fn new() -> Self {
  290. eprint!("\x1b[?25l");
  291. Self { position: Arc::new(Mutex::new(0)), timer: Arc::new(Mutex::new(None)) }
  292. }
  293. pub fn inc(&self, n: u64) {
  294. let mut position = self.position.lock().unwrap();
  295. if *position == 0 {
  296. *self.timer.lock().unwrap() = Some(Instant::now());
  297. }
  298. *position += n;
  299. let binding = self.timer.lock().unwrap();
  300. let Some(elapsed) = binding.as_ref() else { return };
  301. let elapsed = elapsed.elapsed();
  302. let pos = *position;
  303. eprint!("\r[{elapsed:?}] {pos} attempts");
  304. }
  305. pub fn position(&self) -> u64 {
  306. *self.position.lock().unwrap()
  307. }
  308. pub fn finish_and_clear(&self) {
  309. *self.timer.lock().unwrap() = None;
  310. eprint!("\r\x1b[2K\x1b[?25h");
  311. }
  312. }