feat(app): add caching mechanism for kernel parameters

This commit is contained in:
Orhun Parmaksız 2021-12-03 00:09:39 +03:00
parent 81e3fc8fa7
commit 6bbf04a3c1
No known key found for this signature in database
GPG key ID: F83424824B3E4B90
8 changed files with 211 additions and 11 deletions

103
Cargo.lock generated
View file

@ -110,6 +110,27 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "either"
version = "1.6.1"
@ -131,6 +152,17 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "globset"
version = "0.4.8"
@ -182,6 +214,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -277,6 +315,25 @@ dependencies = [
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom",
"redox_syscall",
]
[[package]]
name = "regex"
version = "1.5.4"
@ -294,6 +351,12 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "ryu"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
[[package]]
name = "same-file"
version = "1.0.6"
@ -309,6 +372,37 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.78"
@ -347,8 +441,11 @@ name = "systeroid-core"
version = "0.1.0"
dependencies = [
"colored",
"dirs-next",
"lazy_static",
"rayon",
"serde",
"serde_json",
"sysctl",
"systeroid-parser",
"thiserror",
@ -415,6 +512,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "winapi"
version = "0.3.9"

View file

@ -10,6 +10,9 @@ thiserror = "1.0.29"
lazy_static = "1.4.0"
rayon = "1.5.1"
colored = "2.0.0"
serde = { version = "1.0.130", features = ["derive"] }
serde_json = "1.0.69"
dirs-next = "2.0.0"
[dependencies.systeroid-parser]
version = "0.1.0"

View file

@ -0,0 +1,55 @@
use crate::error::{Error, Result};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
use systeroid_parser::reader;
/// Cache manager for handling the R/W operations of labeled data.
#[derive(Debug)]
pub struct Cache {
/// Cache directory.
cache_dir: PathBuf,
}
impl Cache {
/// Initializes the cache storage.
pub fn init() -> Result<Self> {
Ok(Self {
cache_dir: dirs_next::cache_dir().ok_or_else(|| {
Error::CacheError(String::from("cannot access the cache directory"))
})?,
})
}
/// Returns the path of given labeled data.
fn get_cache_path(&self, label: &str) -> PathBuf {
self.cache_dir
.join(env!("CARGO_PKG_NAME"))
.join(label)
.with_extension("json")
}
/// Returns `true` if the labeled data is present in the cache.
pub fn exists(&self, label: &str) -> bool {
self.get_cache_path(label).exists()
}
/// Reads the given labeled data from the cache.
pub fn read<Data: DeserializeOwned>(&self, label: &str) -> Result<Data> {
let raw_data = reader::read_to_string(self.get_cache_path(label))?;
Ok(serde_json::from_str(&raw_data)?)
}
/// Writes the given data to the cache.
pub fn write<Data: ?Sized + Serialize>(&self, data: &Data, label: &str) -> Result<()> {
let cache_path = self.get_cache_path(label);
if !cache_path.exists() {
fs::create_dir_all(self.cache_dir.join(env!("CARGO_PKG_NAME")))?;
};
let mut file = File::create(&cache_path)?;
file.write_all(serde_json::to_string(data)?.as_bytes())?;
Ok(())
}
}

View file

@ -9,6 +9,12 @@ pub enum Error {
/// Error that may occur whenever a lock is acquired.
#[error("thread lock error: `{0}`")]
ThreadLockError(String),
/// Error that may occur when accessing the cached data.
#[error("cache error: `{0}`")]
CacheError(String),
/// Error that may occur while de/serializing JSON data.
#[error("JSON de/serialization error: `{0}`")]
SerdeJsonError(#[from] serde_json::Error),
/// Error that may occur while parsing documents.
#[error("parser error: `{0}`")]
ParseError(#[from] systeroid_parser::error::Error),

View file

@ -19,3 +19,6 @@ pub mod config;
/// Display options.
pub mod display;
/// Cache manager.
pub mod cache;

View file

@ -4,6 +4,7 @@ use crate::error::Result;
use crate::parsers::parse_kernel_docs;
use colored::*;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::fmt::{self, Display, Formatter};
use std::io::Write;
@ -12,7 +13,7 @@ use std::result::Result as StdResult;
use sysctl::{Ctl, CtlFlags, CtlIter, Sysctl as SysctlImpl};
/// Sections of the sysctl documentation.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum Section {
/// Documentation for `/proc/sys/abi/*`
Abi,
@ -76,7 +77,7 @@ impl Section {
}
/// Representation of a kernel parameter.
#[derive(Debug)]
#[derive(Serialize, Deserialize, Debug)]
pub struct Parameter {
/// Name of the kernel parameter.
pub name: String,
@ -260,6 +261,22 @@ impl Sysctl {
parameter
}
/// Updates the parameters using the given list.
///
/// Keeps the original values.
pub fn update_params(&mut self, mut parameters: Vec<Parameter>) {
parameters.par_iter_mut().for_each(|parameter| {
if let Some(param) = self
.parameters
.par_iter()
.find_any(|param| param.name == parameter.name)
{
parameter.value = param.value.to_string();
}
});
self.parameters = parameters;
}
/// Updates the descriptions of the kernel parameters.
pub fn update_docs(&mut self, kernel_docs: &Path) -> Result<()> {
let documents = parse_kernel_docs(kernel_docs)?;

View file

@ -2,10 +2,14 @@ use std::env;
use std::io::{self, Stdout};
use std::path::PathBuf;
use std::process::{Command, Stdio};
use systeroid_core::cache::Cache;
use systeroid_core::config::AppConfig;
use systeroid_core::error::Result;
use systeroid_core::parsers::KERNEL_DOCS_PATH;
use systeroid_core::sysctl::Sysctl;
use systeroid_core::sysctl::{Parameter, Sysctl};
/// Label for caching the kernel parameters.
const PARAMETERS_CACHE_LABEL: &str = "parameters";
/// Application controller.
#[derive(Debug)]
@ -14,19 +18,21 @@ pub struct App<'a> {
sysctl: &'a mut Sysctl,
/// Configuration.
config: &'a AppConfig,
/// Cache.
cache: Cache,
/// Standard output.
stdout: Stdout,
}
impl<'a> App<'a> {
/// Constructs a new instance.
pub fn new(sysctl: &'a mut Sysctl, config: &'a AppConfig) -> Self {
let stdout = io::stdout();
Self {
pub fn new(sysctl: &'a mut Sysctl, config: &'a AppConfig) -> Result<Self> {
Ok(Self {
sysctl,
config,
stdout,
}
cache: Cache::init()?,
stdout: io::stdout(),
})
}
/// Displays all of the available kernel modules.
@ -39,16 +45,23 @@ impl<'a> App<'a> {
/// Updates the documentation for kernel parameters.
pub fn update_documentation(&mut self, kernel_docs: Option<&PathBuf>) -> Result<()> {
if self.cache.exists(PARAMETERS_CACHE_LABEL) && kernel_docs.is_none() {
self.sysctl
.update_params(self.cache.read::<Vec<Parameter>>(PARAMETERS_CACHE_LABEL)?);
return Ok(());
}
let mut kernel_docs_path = KERNEL_DOCS_PATH.clone();
if let Some(path) = kernel_docs {
kernel_docs_path.insert(0, path);
}
if let Some(path) = kernel_docs_path.iter().find(|path| path.exists()) {
self.sysctl.update_docs(path)
self.sysctl.update_docs(path)?;
self.cache
.write(&self.sysctl.parameters, PARAMETERS_CACHE_LABEL)?;
} else {
eprintln!("warning: `Linux kernel documentation cannot be found. Please specify a path via '-d' argument`",);
Ok(())
}
Ok(())
}
/// Displays the documentation of a parameter.

View file

@ -21,7 +21,7 @@ pub fn run(args: Args) -> Result<()> {
config.app.display_type = args.display_type;
config.app.no_color = env::var("NO_COLOR").is_ok();
let mut sysctl = Sysctl::init(config.sysctl)?;
let mut app = App::new(&mut sysctl, &config.app);
let mut app = App::new(&mut sysctl, &config.app)?;
if let Some(param) = args.param_to_explain {
app.update_documentation(args.kernel_docs.as_ref())?;