below_config/
lib.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![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
34/// Global below config
35pub static BELOW_CONFIG: OnceLock<BelowConfig> = OnceLock::new();
36
37#[derive(Serialize, Deserialize, Debug)]
38// If value is missing during deserialization, use the Default::default()
39#[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}