From 7eed2114349a4ca2008b3f9417e7103e3701e0ea Mon Sep 17 00:00:00 2001 From: Rodolphe Breard Date: Wed, 8 May 2019 21:48:15 +0200 Subject: [PATCH] Allow the config file to include other files --- CHANGELOG.md | 1 + acmed/src/config.rs | 64 ++++++++++++++++++++++++++++++++++++++++----- man/en/acmed.toml.5 | 11 ++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de2564b..19443fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - ACMEd now displays a warning when the server indicates an error in an order or an authorization. +- A configuration file can now include several other files. ## [0.4.0] - 2019-05-08 diff --git a/acmed/src/config.rs b/acmed/src/config.rs index e7046f1..a1e85f0 100644 --- a/acmed/src/config.rs +++ b/acmed/src/config.rs @@ -6,19 +6,32 @@ use serde::Deserialize; use std::fmt; use std::fs::{self, File}; use std::io::prelude::*; -use std::path::Path; +use std::path::{Path, PathBuf}; + +macro_rules! set_cfg_attr { + ($to: expr, $from: expr) => { + if let Some(v) = $from { + $to = Some(v); + }; + }; +} #[derive(Deserialize)] #[serde(deny_unknown_fields)] pub struct Config { pub global: Option, + #[serde(default)] pub endpoint: Vec, #[serde(default)] pub hook: Vec, #[serde(default)] pub group: Vec, + #[serde(default)] pub account: Vec, + #[serde(default)] pub certificate: Vec, + #[serde(default)] + pub include: Vec, } impl Config { @@ -110,7 +123,7 @@ impl Config { } } -#[derive(Deserialize)] +#[derive(Clone, Deserialize)] #[serde(deny_unknown_fields)] pub struct GlobalOptions { pub accounts_directory: Option, @@ -307,12 +320,51 @@ fn init_directories(config: &Config) -> Result<(), Error> { Ok(()) } -pub fn from_file(file: &str) -> Result { - info!("Loading configuration file: {}", file); - let mut file = File::open(file)?; +fn get_cnf_path(from: &PathBuf, file: &str) -> PathBuf { + let mut path = from.clone(); + path.pop(); + path.push(file); + path +} + +fn read_cnf(path: &PathBuf) -> Result { + info!("Loading configuration file: {}", path.display()); + let mut file = File::open(path)?; let mut contents = String::new(); file.read_to_string(&mut contents)?; - let config: Config = toml::from_str(&contents)?; + let mut config: Config = toml::from_str(&contents)?; + for cnf_name in config.include.iter() { + let cnf_path = get_cnf_path(path, cnf_name); + let mut add_cnf = read_cnf(&cnf_path)?; + config.endpoint.append(&mut add_cnf.endpoint); + config.hook.append(&mut add_cnf.hook); + config.group.append(&mut add_cnf.group); + config.account.append(&mut add_cnf.account); + config.certificate.append(&mut add_cnf.certificate); + if config.global.is_none() { + config.global = add_cnf.global; + } else if let Some(new_glob) = add_cnf.global { + let mut tmp_glob = config.global.clone().unwrap(); + set_cfg_attr!(tmp_glob.accounts_directory, new_glob.accounts_directory); + set_cfg_attr!( + tmp_glob.certificates_directory, + new_glob.certificates_directory + ); + set_cfg_attr!(tmp_glob.cert_file_mode, new_glob.cert_file_mode); + set_cfg_attr!(tmp_glob.cert_file_user, new_glob.cert_file_user); + set_cfg_attr!(tmp_glob.cert_file_group, new_glob.cert_file_group); + set_cfg_attr!(tmp_glob.pk_file_mode, new_glob.pk_file_mode); + set_cfg_attr!(tmp_glob.pk_file_user, new_glob.pk_file_user); + set_cfg_attr!(tmp_glob.pk_file_group, new_glob.pk_file_group); + config.global = Some(tmp_glob); + } + } + Ok(config) +} + +pub fn from_file(file_name: &str) -> Result { + let path = PathBuf::from(file_name); + let config = read_cnf(&path)?; init_directories(&config)?; Ok(config) } diff --git a/man/en/acmed.toml.5 b/man/en/acmed.toml.5 index 1eae031..98bfefc 100644 --- a/man/en/acmed.toml.5 +++ b/man/en/acmed.toml.5 @@ -18,6 +18,17 @@ It is written in the .Em TOML format. The allowed elements are described below. .Bl -tag +.It Ic include +Array containing the path to configuration file to include. The path can be either relative or absolute. If relative, it is relative to the configuration file which included it. +.Pp +In case or overlapping global option definition, the one of the last included file will be used. For example, if a file +.Em A +includes files +.Em B +and +.Em C +and all three defines the same global option, the final value will be the one defined in file +.Em C . .It Ic global Table containing the global configuration options. .Bl -tag