// Copyright 2023 The Grim Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use grin_config::ConfigError; use grin_core::global; use lazy_static::lazy_static; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use serde::de::DeserializeOwned; use serde::Serialize; use std::fs::{self, File}; use std::io::Write; use std::path::PathBuf; use std::sync::Arc; use crate::node::NodeConfig; use crate::settings::AppConfig; use crate::tor::TorConfig; use crate::wallet::ConnectionsConfig; lazy_static! { /// Static settings state to be accessible globally. static ref SETTINGS_STATE: Arc = Arc::new(Settings::init()); } /// Contains initialized configurations. pub struct Settings { /// Application configuration. app_config: Arc>, /// Integrated node configuration. node_config: Arc>, /// Wallet connections configuration. conn_config: Arc>, /// Tor server configuration. tor_config: Arc> } impl Settings { /// Main application directory name. pub const MAIN_DIR_NAME: &'static str = ".grim"; /// Application socket name. pub const SOCKET_NAME: &'static str = "grim.sock"; /// Log file name. pub const LOG_FILE_NAME: &'static str = "grim.log"; /// Initialize settings with app and node configs. fn init() -> Self { // Initialize app config. let app_config_path = Settings::config_path(AppConfig::FILE_NAME, None); let app_config = Self::init_config::(app_config_path); // Initialize tor config. let tor_config_path = Settings::config_path(TorConfig::FILE_NAME, None); let mut tor_config = Self::init_config::(tor_config_path); tor_config.migrate(); // Setup chain type. let chain_type = &app_config.chain_type; if !global::GLOBAL_CHAIN_TYPE.is_init() { global::init_global_chain_type(*chain_type); } else { global::set_global_chain_type(*chain_type); global::set_local_chain_type(*chain_type); } Self { node_config: Arc::new(RwLock::new(NodeConfig::for_chain_type(chain_type))), conn_config: Arc::new(RwLock::new(ConnectionsConfig::for_chain_type(chain_type))), app_config: Arc::new(RwLock::new(app_config)), tor_config: Arc::new(RwLock::new(tor_config)), } } /// Initialize configuration from provided file path or set [`Default`] if file not exists. pub fn init_config(path: PathBuf) -> T { let parsed = Self::read_from_file::(path.clone()); if !path.exists() || parsed.is_err() { let default_config = T::default(); Settings::write_to_file(&default_config, path); default_config } else { parsed.unwrap() } } /// Get node configuration to read values. pub fn node_config_to_read() -> RwLockReadGuard<'static, NodeConfig> { SETTINGS_STATE.node_config.read() } /// Get node configuration to update values. pub fn node_config_to_update() -> RwLockWriteGuard<'static, NodeConfig> { SETTINGS_STATE.node_config.write() } /// Get app configuration to read values. pub fn app_config_to_read() -> RwLockReadGuard<'static, AppConfig> { SETTINGS_STATE.app_config.read() } /// Get app configuration to update values. pub fn app_config_to_update() -> RwLockWriteGuard<'static, AppConfig> { SETTINGS_STATE.app_config.write() } /// Get connections configuration to read values. pub fn conn_config_to_read() -> RwLockReadGuard<'static, ConnectionsConfig> { SETTINGS_STATE.conn_config.read() } /// Get connections configuration to update values. pub fn conn_config_to_update() -> RwLockWriteGuard<'static, ConnectionsConfig> { SETTINGS_STATE.conn_config.write() } /// Get tor server configuration to read values. pub fn tor_config_to_read() -> RwLockReadGuard<'static, TorConfig> { SETTINGS_STATE.tor_config.read() } /// Get tor server configuration to update values. pub fn tor_config_to_update() -> RwLockWriteGuard<'static, TorConfig> { SETTINGS_STATE.tor_config.write() } /// Get base directory path for configuration. pub fn base_path(sub_dir: Option) -> PathBuf { // Check if dir exists. let mut path = dirs::home_dir().unwrap_or_else(|| PathBuf::new()); path.push(Self::MAIN_DIR_NAME); if sub_dir.is_some() { path.push(sub_dir.unwrap()); } // Create if the default path doesn't exist. if !path.exists() { let _ = fs::create_dir_all(path.clone()); } path } /// Get log file path. pub fn log_path() -> String { let mut log_path = Self::base_path(None); log_path.push(Self::LOG_FILE_NAME); log_path.to_str().unwrap().into() } /// Get desktop application socket path. pub fn socket_path() -> PathBuf { let mut socket_path = Self::base_path(None); socket_path.push(Self::SOCKET_NAME); socket_path } /// Get configuration file path from provided name and subdirectory if needed. pub fn config_path(config_name: &str, sub_dir: Option) -> PathBuf { let mut path = Self::base_path(sub_dir); path.push(config_name); path } /// Get path of file created when application crashed. pub fn crash_check_path() -> PathBuf { let mut path = Self::base_path(None); path.push("crashed"); path } /// Delete crash file. pub fn delete_crash_check() { let log = Self::crash_check_path(); if log.exists() { let _ = fs::remove_file(log.clone()); } } /// Read configuration from the file. pub fn read_from_file(config_path: PathBuf) -> Result { let file_content = fs::read_to_string(config_path.clone())?; let parsed = toml::from_str::(file_content.as_str()); match parsed { Ok(cfg) => Ok(cfg), Err(e) => { Err(ConfigError::ParseError( config_path.to_str().unwrap().to_string(), format!("{}", e), )) } } } /// Write configuration to the file. pub fn write_to_file(config: &T, path: PathBuf) { let conf_out = toml::to_string(config).unwrap(); let mut file = File::create(path.to_str().unwrap()).unwrap(); file.write_all(conf_out.as_bytes()).unwrap(); } }