feat(sysctl): support changing the value of parameters

This commit is contained in:
Orhun Parmaksız 2021-11-10 19:50:08 +03:00
parent e4507feb5c
commit ad024dc5c2
No known key found for this signature in database
GPG key ID: F83424824B3E4B90
3 changed files with 106 additions and 51 deletions

View file

@ -14,22 +14,25 @@ macro_rules! map {
/// General configuration.
#[derive(Debug, Default)]
pub struct Config {
/// Sysctl configuration.
pub sysctl: SysctlConfig,
/// Color configuration.
pub color: ColorConfig,
}
/// Sysctl configuration.
#[derive(Debug)]
pub struct SysctlConfig {
pub struct ColorConfig {
/// Whether if the colors are disabled.
pub no_color: bool,
/// Sections and the corresponding colors.
pub section_colors: HashMap<Section, Color>,
/// Default color for the output
pub default_color: Color,
}
impl Default for SysctlConfig {
impl Default for ColorConfig {
fn default() -> Self {
Self {
no_color: false,
section_colors: map! {
Section::Abi => Color::Red,
Section::Fs => Color::Green,

View file

@ -1,13 +1,14 @@
use crate::config::SysctlConfig;
use crate::config::ColorConfig;
use crate::error::Result;
use crate::parsers::parse_kernel_docs;
use colored::*;
use rayon::prelude::*;
use std::convert::TryFrom;
use std::fmt::{self, Display, Formatter};
use std::io::Write;
use std::path::Path;
use std::result::Result as StdResult;
use sysctl::{CtlFlags, CtlIter, Sysctl as SysctlImpl};
use sysctl::{Ctl, CtlFlags, CtlIter, Sysctl as SysctlImpl};
use systeroid_parser::document::Document;
/// Sections of the sysctl documentation.
@ -91,7 +92,7 @@ pub struct Parameter {
impl Parameter {
/// Returns the parameter name with corresponding section colors.
pub fn colored_name(&self, config: &SysctlConfig) -> String {
pub fn colored_name(&self, config: &ColorConfig) -> String {
let fields = self.name.split('.').collect::<Vec<&str>>();
fields
.iter()
@ -113,6 +114,48 @@ impl Parameter {
result
})
}
/// Prints the kernel parameter to given output.
pub fn display<W: Write>(&self, config: &ColorConfig, output: &mut W) -> Result<()> {
if !config.no_color {
writeln!(
output,
"{} {} {}",
self.colored_name(config),
"=".color(config.default_color),
self.value.bold(),
)?;
} else {
writeln!(output, "{} = {}", self.name, self.value)?;
}
Ok(())
}
/// Sets a new value for the kernel parameter.
pub fn update<W: Write>(
&mut self,
new_value: &str,
config: &ColorConfig,
output: &mut W,
) -> Result<()> {
let ctl = Ctl::new(&self.name)?;
let new_value = ctl.set_value_string(new_value)?;
self.value = new_value;
self.display(config, output)
}
}
impl<'a> TryFrom<&'a Ctl> for Parameter {
type Error = crate::error::Error;
fn try_from(ctl: &'a Ctl) -> Result<Self> {
Ok(Parameter {
name: ctl.name()?,
value: ctl.value_string()?,
description: ctl.description().ok(),
section: Section::from(ctl.name()?),
document: None,
})
}
}
/// Sysctl wrapper for managing the kernel parameters.
@ -120,28 +163,27 @@ impl Parameter {
pub struct Sysctl {
/// Available kernel parameters.
pub parameters: Vec<Parameter>,
/// Configuration.
pub config: SysctlConfig,
}
impl Sysctl {
/// Constructs a new instance by fetching the available kernel parameters.
pub fn init(config: SysctlConfig) -> Result<Self> {
pub fn init() -> Result<Self> {
let mut parameters = Vec::new();
for ctl in CtlIter::root().filter_map(StdResult::ok).filter(|ctl| {
ctl.flags()
.map(|flags| !flags.contains(CtlFlags::SKIP))
.unwrap_or(false)
}) {
parameters.push(Parameter {
name: ctl.name()?,
value: ctl.value_string()?,
description: ctl.description().ok(),
section: Section::from(ctl.name()?),
document: None,
});
match Parameter::try_from(&ctl) {
Ok(parameter) => {
parameters.push(parameter);
}
Err(e) => {
eprintln!("{} ({})", e, ctl.name()?);
}
}
}
Ok(Self { parameters, config })
Ok(Self { parameters })
}
/// Updates the descriptions of the kernel parameters.
@ -174,31 +216,4 @@ impl Sysctl {
});
Ok(())
}
/// Prints the available kernel parameters to the given output.
pub fn display<W: Write>(
&self,
output: &mut W,
names: Vec<String>,
colored: bool,
) -> Result<()> {
for parameter in self
.parameters
.iter()
.filter(|param| names.is_empty() || names.iter().any(|name| param.name == *name))
{
if colored {
writeln!(
output,
"{} {} {}",
parameter.colored_name(&self.config),
"=".color(self.config.default_color),
parameter.value.bold(),
)?;
} else {
writeln!(output, "{} = {}", parameter.name, parameter.value)?;
}
}
Ok(())
}
}

View file

@ -6,23 +6,60 @@
pub mod args;
use crate::args::Args;
use std::io;
use std::io::{self, Write};
use systeroid_core::config::Config;
use systeroid_core::error::Result;
use systeroid_core::sysctl::Sysctl;
/// Runs `systeroid`.
pub fn run(args: Args) -> Result<()> {
let config = Config::default();
let mut stdout = io::stdout();
let mut sysctl = Sysctl::init(config.sysctl)?;
let mut config = Config::default();
config.color.no_color = args.no_color;
let (mut stdout, mut stderr) = (io::stdout(), io::stderr());
let mut sysctl = Sysctl::init()?;
if let Some(kernel_docs) = args.kernel_docs {
sysctl.update_docs(&kernel_docs)?;
}
if args.display_all {
sysctl.display(&mut stdout, args.param_names, !args.no_color)?;
if args.param_names.is_empty() {
sysctl
.parameters
.iter()
.try_for_each(|parameter| parameter.display(&config.color, &mut stdout))?;
} else {
for mut param_name in args.param_names {
let new_value = if param_name.contains('=') {
let fields = param_name
.split('=')
.take(2)
.map(String::from)
.collect::<Vec<String>>();
param_name = fields[0].to_string();
Some(fields[1].to_string())
} else {
None
};
match sysctl
.parameters
.iter_mut()
.find(|param| param.name == *param_name)
{
Some(parameter) => {
if let Some(new_value) = new_value {
parameter.update(&new_value, &config.color, &mut stdout)?;
} else {
parameter.display(&config.color, &mut stdout)?;
}
}
None => writeln!(
stderr,
"{}: cannot stat /proc/{}: No such file or directory",
env!("CARGO_PKG_NAME"),
param_name.replace(".", "/")
)?,
}
}
}
Ok(())