diff --git a/CHANGELOG.md b/CHANGELOG.md index ad859aac4760..b24a57108a50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ ### Added - [#6012](https://github.com/ChainSafe/forest/issues/6012): Stricter validation of address arguments in `forest-wallet` subcommands. +- [#6008](https://github.com/ChainSafe/forest/issues/6008): `FOREST_PATH` environment variable to override the data directory for `forest`, `forest-cli`, `forest-tool` and `forest-wallet`. Takes precedence over `client.data_dir` in the config file. The daemon also logs the resolved data directory on startup. ### Changed diff --git a/docs/docs/users/reference/env_variables.md b/docs/docs/users/reference/env_variables.md index eca507fb771b..c191638f2193 100644 --- a/docs/docs/users/reference/env_variables.md +++ b/docs/docs/users/reference/env_variables.md @@ -31,6 +31,7 @@ process. | `FOREST_MAX_FILTER_HEIGHT_RANGE` | positive integer | 2880 | 2880 | The maximum filter height range allowed, a conservative limit of one day | | `FOREST_STATE_MIGRATION_THREADS` | integer | Depends on the machine. | 3 | The number of threads for state migration thread-pool. Advanced users only. | | `FOREST_CONFIG_PATH` | string | /$FOREST_HOME/com.ChainSafe.Forest/config.toml | `/path/to/config.toml` | Forest configuration path. Alternatively supplied via `--config` cli parameter. | +| `FOREST_PATH` | directory path | platform-specific (`directories::ProjectDirs`) | `/var/lib/forest` | Override the Forest data directory. Honored by `forest`, `forest-cli`, `forest-tool` and `forest-wallet`. Takes precedence over the `client.data_dir` setting in the config file. | | `FOREST_TEST_RNG_FIXED_SEED` | non-negative integer | empty | 0 | Override RNG with a reproducible one seeded by the value. This should never be used out of test context for security. | | `RUST_LOG` | string | empty | `debug,forest_libp2p::service=info` | Allows for log level customization. | | `FOREST_IGNORE_DRAND` | 1 or true | empty | 1 | Ignore Drand validation. | diff --git a/src/cli_shared/mod.rs b/src/cli_shared/mod.rs index 170c032f1b6d..0f63ee601263 100644 --- a/src/cli_shared/mod.rs +++ b/src/cli_shared/mod.rs @@ -9,6 +9,17 @@ use crate::networks::NetworkChain; use crate::utils::io::read_toml; use std::path::PathBuf; +/// Environment variable that overrides the Forest data directory. +pub const FOREST_PATH_ENV: &str = "FOREST_PATH"; + +/// Returns the value of [`FOREST_PATH_ENV`] when set to a non-empty string. +pub fn forest_path_from_env() -> Option { + std::env::var(FOREST_PATH_ENV) + .ok() + .filter(|s| !s.is_empty()) + .map(PathBuf::from) +} + cfg_if::cfg_if! { if #[cfg(feature = "rustalloc")] { } else if #[cfg(feature = "jemalloc")] { @@ -37,6 +48,9 @@ pub fn read_config( if let Some(chain) = chain_opt { config.chain = chain; } + if let Some(data_dir) = forest_path_from_env() { + config.client.data_dir = data_dir; + } Ok((path, config)) } @@ -68,6 +82,22 @@ mod tests { assert_eq!(config.chain(), &NetworkChain::Butterflynet); } + #[test] + #[serial_test::serial] + fn read_config_forest_path_env_override() { + let temp_dir = tempfile::tempdir().expect("couldn't create temp dir"); + // SAFETY: tests touching the process environment are gated by `#[serial]`. + unsafe { + std::env::set_var(FOREST_PATH_ENV, temp_dir.path()); + } + let (_, config) = read_config(None, None).unwrap(); + // SAFETY: tests touching the process environment are gated by `#[serial]`. + unsafe { + std::env::remove_var(FOREST_PATH_ENV); + } + assert_eq!(config.client.data_dir, temp_dir.path()); + } + #[test] fn read_config_with_path() { let default_config = Config::default(); diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs index b5a4ec866039..9a80644da766 100644 --- a/src/daemon/mod.rs +++ b/src/daemon/mod.rs @@ -113,6 +113,7 @@ fn startup_init(config: &Config) -> anyhow::Result<()> { "Starting Forest daemon, version {}", FOREST_VERSION_STRING.as_str() ); + info!("Data directory: {}", config.client.data_dir.display()); Ok(()) } diff --git a/src/wallet/subcommands/wallet_cmd.rs b/src/wallet/subcommands/wallet_cmd.rs index 9796573a3c69..2a4a562012b2 100644 --- a/src/wallet/subcommands/wallet_cmd.rs +++ b/src/wallet/subcommands/wallet_cmd.rs @@ -62,12 +62,15 @@ impl WalletBackend { } fn new_local(client: rpc::Client, want_encryption: bool) -> anyhow::Result { - let Some(dir) = ProjectDirs::from("com", "ChainSafe", "Forest-Wallet") else { - bail!("Failed to find wallet directory"); + let wallet_dir = if let Some(forest_path) = crate::cli_shared::forest_path_from_env() { + forest_path + } else { + let Some(dir) = ProjectDirs::from("com", "ChainSafe", "Forest-Wallet") else { + bail!("Failed to find wallet directory"); + }; + dir.data_dir().to_path_buf() }; - let wallet_dir = dir.data_dir().to_path_buf(); - let is_encrypted = wallet_dir.join(ENCRYPTED_KEYSTORE_NAME).exists(); // Always use the encrypted keystore if it exists. It it does not exist,