1#![deny(clippy::all)]
16
17use std::fs;
18use std::path::Path;
19use std::path::PathBuf;
20use std::sync::OnceLock;
21
22use anyhow::bail;
23use anyhow::Result;
24use serde::Deserialize;
25use serde::Serialize;
26
27#[cfg(test)]
28mod test;
29
30pub const BELOW_DEFAULT_CONF: &str = "/etc/below/below.conf";
31const BELOW_DEFAULT_LOG: &str = "/var/log/below";
32const BELOW_DEFAULT_STORE: &str = "/var/log/below/store";
33
34pub static BELOW_CONFIG: OnceLock<BelowConfig> = OnceLock::new();
36
37#[derive(Serialize, Deserialize, Debug)]
38#[serde(default)]
40pub struct BelowConfig {
41 pub log_dir: PathBuf,
42 pub store_dir: PathBuf,
43 pub cgroup_root: PathBuf,
44 pub cgroup_filter_out: String,
45 pub enable_gpu_stats: bool,
46 pub use_rgpu_for_gpu_stats: bool,
47 pub enable_btrfs_stats: bool,
48 pub btrfs_samples: u64,
49 pub btrfs_min_pct: f64,
50 pub enable_ethtool_stats: bool,
51 pub enable_ksm_stats: bool,
52 pub enable_resctrl_stats: bool,
53 pub enable_tc_stats: bool,
54}
55
56impl Default for BelowConfig {
57 fn default() -> Self {
58 BelowConfig {
59 log_dir: std::env::var_os("LOGS_DIRECTORY")
60 .map_or(BELOW_DEFAULT_LOG.into(), PathBuf::from),
61 store_dir: std::env::var_os("LOGS_DIRECTORY").map_or(BELOW_DEFAULT_STORE.into(), |d| {
62 PathBuf::from(d).join("store")
63 }),
64 cgroup_root: cgroupfs::DEFAULT_CG_ROOT.into(),
65 cgroup_filter_out: String::new(),
66 enable_gpu_stats: false,
67 use_rgpu_for_gpu_stats: true,
68 enable_btrfs_stats: false,
69 btrfs_samples: btrfs::DEFAULT_SAMPLES,
70 btrfs_min_pct: btrfs::DEFAULT_MIN_PCT,
71 enable_ethtool_stats: false,
72 enable_ksm_stats: false,
73 enable_resctrl_stats: false,
74 enable_tc_stats: false,
75 }
76 }
77}
78
79impl BelowConfig {
80 pub fn load(path: &Path) -> Result<Self> {
81 match path.exists() {
82 true if !path.is_file() => bail!("{} exists and is not a file", path.to_string_lossy()),
83 true => BelowConfig::load_exists(path),
84 false if path.to_string_lossy() == BELOW_DEFAULT_CONF => Ok(Default::default()),
85 false => bail!("No such file or directory: {}", path.to_string_lossy()),
86 }
87 }
88
89 fn load_exists(path: &Path) -> Result<Self> {
90 let string_config = match fs::read_to_string(path) {
91 Ok(sc) => sc,
92 Err(e) => {
93 bail!(
94 "Failed to read from config file {}: {}",
95 path.to_string_lossy(),
96 e
97 );
98 }
99 };
100
101 match toml::from_str(string_config.as_str()) {
102 Ok(bc) => Ok(bc),
103 Err(e) => {
104 bail!(
105 "Failed to parse config file {}: {}\n{}",
106 path.to_string_lossy(),
107 e,
108 string_config
109 );
110 }
111 }
112 }
113}