Newer
Older
* Copyright (c) 2021-2022, Luca Fulchir <luker@fenrirproject.org>
*
* This file is part of dfim.
*
* dfim is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dfim is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dfim. If not, see <https://www.gnu.org/licenses/>.
*/
pub mod device;
pub mod errors;
pub mod include;
use crate::config::trgt::TargetCommon;
fn standardize(&mut self);
fn check_consistency(
&self,
logger: &slog::Logger,
) -> Result<(), errors::ConfigError>;
}
#[derive(::serde::Deserialize, ::serde::Serialize, Copy, Clone)]
#[serde(deny_unknown_fields)]
pub enum LogTime {
UTC,
Local,
}
#[derive(::serde::Deserialize, ::serde::Serialize, Copy, Clone)]
#[serde(deny_unknown_fields)]
pub enum LogFacility {
Kern,
User,
Mail,
Daemon,
Auth,
Syslog,
Lpr,
News,
Uucp,
Cron,
Authpriv,
Ftp,
Local0,
Local1,
Local2,
Local3,
Local4,
Local5,
Local6,
Local7,
}
#[derive(::serde::Deserialize, ::serde::Serialize, Copy, Clone)]
#[serde(deny_unknown_fields)]
pub enum LogLevel {
Critical,
Error,
Warning,
Info,
Debug,
Trace,
}
#[derive(::serde::Deserialize, ::serde::Serialize, Copy, Clone)]
#[serde(deny_unknown_fields)]
pub enum LogTermStream {
StdErr,
StdOut,
}
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "lowercase")]
pub enum LogSyslog {
Socket(::std::path::PathBuf),
Udp {
from: ::std::net::SocketAddr,
to: ::std::net::SocketAddr,
hostname_id: String,
},
Tcp {
to: ::std::net::SocketAddr,
hostname_id: String,
},
}
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
#[serde(rename_all = "lowercase")]
#[serde(deny_unknown_fields)]
pub enum LogDest {
Terminal(Option<LogTermStream>),
Syslog {
dest: LogSyslog,
facility: LogFacility,
level: LogLevel,
},
}
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Logging {
pub level: LogLevel,
pub time: LogTime,
#[serde(flatten)]
pub dest: LogDest,
}
#[derive(::serde::Deserialize, ::serde::Serialize, Clone)]
// don't provide a default, be explicit
pub dry_run: bool,
pub logging: Logging,
pub include: ::std::path::PathBuf,
/* Todo: only for notifications
pub user: String,
pub group: String,
*/
}
impl CfgVerify for FullConfig {
fn standardize(&mut self) {
for d in self.devices.iter_mut() {
d.standardize();
}
for t in self.targets.iter_mut() {
t.standardize();
}
}
fn check_consistency(
&self,
logger: &slog::Logger,
) -> Result<(), errors::ConfigError> {
for d in self.devices.iter() {
let id_re = ::regex::Regex::new(r"^[\w\d_-]+$").unwrap();
if !id_re.is_match(&d.id) {
::slog::error!(
logger,
"device name \"{}\" is invalid: must not contain \
whitespace, only letters,numbers, and -_",
&d.id
);
return Err(errors::ConfigError::Consistency(
"Invalid device name".to_owned(),
));
}
d.check_consistency(logger)?;
}
for t in self.targets.iter() {
t.check_consistency(logger)?;
}
Ok(())
}
}
conf_path: &::std::path::Path,
) -> std::result::Result<FullConfig, errors::ConfigError> {
let main: Main = {
// parse file in this block so that the fopen is
// closed as soon as possible
let conf_device = ::std::fs::File::open(conf_path)?;
let mut extensions = ::ron::extensions::Extensions::default();
extensions.insert(::ron::extensions::Extensions::IMPLICIT_SOME);
extensions.insert(::ron::extensions::Extensions::UNWRAP_NEWTYPES);
// TODO: wait for new RON release
//match ::ron::Options::default().
// with_default_extensions(extensions).from_reader(&conf_device) {
match ::ron::de::from_reader(&conf_device) {
Ok(cfg) => cfg,
Err(repr) => {
::slog::error!(logger, "on file: {:?}", &conf_path);
::slog::error!(logger, "{}", repr);
return Err(errors::ConfigError::Parsing(repr));
}
};
let mut cfg = FullConfig {
main: main,
devices: Vec::new(),
targets: Vec::new(),
};
// now parse all the included device confs
if !cfg.main.include.is_dir() {
::std::eprintln!("Error: include conf directory not found");
return Err(errors::ConfigError::IO(::std::io::Error::new(
::std::io::ErrorKind::NotFound,
"include conf directory not found",
)));
let re = ::regex::Regex::new(r"^.*\.ron$").unwrap();
for filename in ::std::fs::read_dir(&cfg.main.include)? {
let filename = filename?.path();
if !re.is_match(filename.to_str().unwrap()) {
continue;
}
let mut include = match include::parse_include(logger, &filename) {
Ok(include) => include,
Err(e) => {
return Err(e);
}
};
cfg.targets.append(&mut include.targets);
cfg.devices.append(&mut include.devices);
// We have a few places in the config where we let the user inline the
// configuration for ease of use
// Uninline them so that we have a standard way of accessing stuff
let mut queue = Vec::<trgt::Target>::new();
let mut next_queue = Vec::<trgt::Target>::new();
for dev in &mut self.devices {
match &mut dev.target {
trgt::TargetId::Id(_) => {}
trgt::TargetId::Inline(t) => {
let tmp_id = t.id().to_string();
queue.push(t.clone());
dev.target = trgt::TargetId::Id(tmp_id);
}
}
}
queue.append(&mut self.targets);
while queue.len() > 0 {
for target in &mut queue {
match target {
for p in &mut g.partitions {
match &mut p.target {
trgt::TargetId::Id(_) => {}
trgt::TargetId::Inline(target) => {
let tmp_id = target.id().to_string();
next_queue.push(target.clone());
p.target = trgt::TargetId::Id(tmp_id);