Skip to content

Commit

Permalink
Create .nimiq dir when trying to create example config files
Browse files Browse the repository at this point in the history
Also fixed a couple of TOCTOUs (probably unnecessary) and state the
error if an example config file could not be created.

Fixes #2846.
  • Loading branch information
hrxi authored and jsdanielh committed Oct 2, 2024
1 parent 6ce5807 commit bd8cb3d
Showing 1 changed file with 60 additions and 26 deletions.
86 changes: 60 additions & 26 deletions lib/src/config/config_file/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{
collections::HashMap, fmt::Debug, fs::read_to_string, num::NonZeroU8, path::Path, str::FromStr,
collections::HashMap, fmt::Debug, fs, io, io::Write as _, num::NonZeroU8, path::Path,
str::FromStr,
};

use log::level_filters::LevelFilter;
Expand Down Expand Up @@ -60,7 +61,35 @@ impl ConfigFile {

/// Parse config file from file
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<ConfigFile, Error> {
Self::from_str(&read_to_string(path)?)
Self::from_str(&fs::read_to_string(path)?)
}

fn create_example(path: &Path) -> io::Result<()> {
if let Some(parent) = path.parent() {
if parent != Path::new("") {
if let Err(error) = fs::create_dir_all(parent) {
log::warn!(
%error,
"Failed to create Nimiq home directory {}",
parent.display(),
);
}
}
}

log::info!("Creating example config at: {}", path.display());
let mut file = match fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(path)
{
Err(error) if error.kind() == io::ErrorKind::AlreadyExists => {
return Ok(());
}
result => result?,
};

file.write_all(Self::EXAMPLE_CONFIG.as_bytes())
}

/// Find config file.
Expand All @@ -82,33 +111,38 @@ impl ConfigFile {
}
}

// If example doesn't exist, create it.
let path_example = paths::home().join("client.toml.example");
if !path_example.exists() {
log::info!("Creating example config at: {}", path_example.display());
if let Err(e) = std::fs::write(&path_example, Self::EXAMPLE_CONFIG) {
log::warn!(
"Failed to create example config file: {}: {}",
e,
path_example.display()
);
}
}

// Check if config exists, otherwise tell user to create one.
// Load config.
let path = paths::home().join("client.toml");
if !path.exists() {
let msg = format!(
"Config file not found. Please create one at {} or specify a location using -c path/to/config.toml, see the example config file at {}",
path.display(),
path_example.display(),
);
log::warn!("{}", msg);
return Err(Error::config_error(&msg));
match Self::from_file(&path) {
Err(Error::Io(err)) if err.kind() == io::ErrorKind::NotFound => {
// Fall through.
}
result => return result,
}

// Load config.
Self::from_file(&path)
// The config doesn't exist. Create an example config file and tell
// the user to create a config.
let example = paths::home().join("client.toml.example");
let example_message = Self::create_example(&example)
.map(|()| format!("see example config file at {}", example.display()))
.unwrap_or_else(|error| {
log::warn!(
%error,
"Failed to create example config file at {}",
example.display(),
);
format!(
"failed to create config file at {}: {}",
example.display(),
error,
)
});

Err(Error::config_error(format!(
"Config file not found. Please create one at {} or specify a location using -c path/to/config.toml ({})",
path.display(),
example_message,
)))
}
}

Expand Down

0 comments on commit bd8cb3d

Please sign in to comment.