2022-07-09 13:53:01 +00:00
|
|
|
use std::fmt::Write;
|
2022-07-09 07:57:49 +00:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::prelude::*;
|
|
|
|
|
2022-07-09 13:31:01 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
2022-07-09 13:53:01 +00:00
|
|
|
IO(std::io::Error),
|
|
|
|
ParseInt(std::num::ParseIntError),
|
|
|
|
Format(std::fmt::Error),
|
|
|
|
Generic(String),
|
2022-07-09 13:31:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<std::io::Error> for Error {
|
|
|
|
fn from(io_error: std::io::Error) -> Self {
|
2022-07-09 13:53:01 +00:00
|
|
|
Self::IO(io_error)
|
2022-07-09 13:31:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<std::num::ParseIntError> for Error {
|
|
|
|
fn from(parse_error: std::num::ParseIntError) -> Self {
|
2022-07-09 13:53:01 +00:00
|
|
|
Self::ParseInt(parse_error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<std::fmt::Error> for Error {
|
|
|
|
fn from(fmt_error: std::fmt::Error) -> Self {
|
|
|
|
Self::Format(fmt_error)
|
2022-07-09 13:31:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<String> for Error {
|
|
|
|
fn from(string: String) -> Self {
|
2022-07-09 13:53:01 +00:00
|
|
|
Self::Generic(string)
|
2022-07-09 13:31:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match self {
|
2022-07-09 13:53:01 +00:00
|
|
|
Error::IO(e) => e.fmt(f),
|
|
|
|
Error::ParseInt(e) => e.fmt(f),
|
|
|
|
Error::Format(e) => e.fmt(f),
|
|
|
|
Error::Generic(s) => f.write_str(s),
|
2022-07-09 13:31:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for Error {
|
|
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
|
|
match self {
|
2022-07-09 13:53:01 +00:00
|
|
|
Error::IO(e) => e.source(),
|
|
|
|
Error::ParseInt(e) => e.source(),
|
2022-07-09 13:57:29 +00:00
|
|
|
Error::Format(e) => e.source(),
|
2022-07-10 04:05:12 +00:00
|
|
|
Error::Generic(_) => None,
|
2022-07-09 13:31:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-09 08:33:19 +00:00
|
|
|
pub struct NetInfo {
|
|
|
|
dev_name: String,
|
2022-10-18 12:04:28 +00:00
|
|
|
graph: String,
|
2022-12-11 12:49:51 +00:00
|
|
|
graph_history: [f64; 10],
|
2022-07-09 08:33:19 +00:00
|
|
|
down: u64,
|
|
|
|
prev_down: u64,
|
|
|
|
up: u64,
|
|
|
|
prev_up: u64,
|
2022-12-11 12:49:51 +00:00
|
|
|
first_iteration: bool,
|
2022-07-09 08:33:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl NetInfo {
|
|
|
|
pub fn new(dev_name: String) -> Self {
|
|
|
|
Self {
|
|
|
|
dev_name,
|
2022-10-18 12:04:28 +00:00
|
|
|
graph: String::from(" "),
|
2022-12-11 12:49:51 +00:00
|
|
|
graph_history: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
2022-07-09 08:33:19 +00:00
|
|
|
down: 0,
|
|
|
|
prev_down: 0,
|
|
|
|
up: 0,
|
|
|
|
prev_up: 0,
|
2022-12-11 12:49:51 +00:00
|
|
|
first_iteration: true,
|
2022-07-09 08:33:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-09 13:31:01 +00:00
|
|
|
pub fn update(&mut self) -> Result<(), Error> {
|
2022-07-09 08:33:19 +00:00
|
|
|
let mut netdev_string = String::new();
|
|
|
|
{
|
|
|
|
let mut netdev_file: File = File::open("/proc/net/dev")?;
|
|
|
|
netdev_file.read_to_string(&mut netdev_string)?;
|
|
|
|
}
|
|
|
|
|
2022-07-10 04:05:12 +00:00
|
|
|
let mut dev_line: Option<&str> = None;
|
2022-08-16 11:24:29 +00:00
|
|
|
for line in netdev_string.lines().map(|line| line.trim()) {
|
|
|
|
if line.starts_with(&self.dev_name) {
|
|
|
|
dev_line = Some(line);
|
2022-07-09 08:33:19 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(line) = dev_line {
|
|
|
|
let entries: Vec<&str> = line.split_whitespace().collect();
|
|
|
|
if entries.len() < 10 {
|
2022-07-09 13:31:01 +00:00
|
|
|
return Err(format!("NetInfo::update: Failed to parse /proc/net/dev, \"{}\" device line is too short", self.dev_name).into());
|
2022-07-09 08:33:19 +00:00
|
|
|
}
|
|
|
|
|
2022-12-11 12:49:51 +00:00
|
|
|
if !self.first_iteration {
|
|
|
|
self.down = entries[1].parse()?;
|
|
|
|
self.up = entries[9].parse()?;
|
|
|
|
} else {
|
|
|
|
self.prev_down = entries[1].parse()?;
|
|
|
|
self.prev_up = entries[9].parse()?;
|
|
|
|
}
|
2022-07-09 08:33:19 +00:00
|
|
|
} else {
|
2022-07-09 13:31:01 +00:00
|
|
|
return Err(format!(
|
|
|
|
"NetInfo::update: Failed to parse /proc/net/dev, can't find net device \"{}\"",
|
|
|
|
self.dev_name
|
|
|
|
)
|
|
|
|
.into());
|
2022-07-09 08:33:19 +00:00
|
|
|
}
|
|
|
|
|
2022-12-11 12:49:51 +00:00
|
|
|
self.first_iteration = false;
|
|
|
|
|
2022-07-09 08:33:19 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-12-11 12:49:51 +00:00
|
|
|
pub fn get_netstring(&mut self, graph_max_opt: Option<f64>) -> Result<(String, String), Error> {
|
|
|
|
let down_diff: f64 = if self.down > self.prev_down {
|
|
|
|
let value = (self.down - self.prev_down) as f64;
|
|
|
|
self.prev_down = self.down;
|
|
|
|
value
|
|
|
|
} else {
|
|
|
|
0.0
|
|
|
|
};
|
|
|
|
let up_diff: f64 = if self.up > self.prev_up {
|
|
|
|
let value = (self.up - self.prev_up) as f64;
|
|
|
|
self.prev_up = self.up;
|
|
|
|
value
|
|
|
|
} else {
|
|
|
|
0.0
|
|
|
|
};
|
2022-07-09 08:33:19 +00:00
|
|
|
|
|
|
|
let mut output = String::new();
|
2022-07-09 09:35:54 +00:00
|
|
|
if down_diff > 1024.0 * 1024.0 {
|
2022-07-09 13:53:01 +00:00
|
|
|
write!(&mut output, "{:.2} MiB ", down_diff / (1024.0 * 1024.0))?;
|
2022-07-09 09:35:54 +00:00
|
|
|
} else if down_diff > 1024.0 {
|
2022-07-09 13:53:01 +00:00
|
|
|
write!(&mut output, "{:.2} KiB ", down_diff / 1024.0)?;
|
2022-07-09 08:33:19 +00:00
|
|
|
} else {
|
2022-07-09 13:53:01 +00:00
|
|
|
write!(&mut output, "{:.0} B ", down_diff)?;
|
2022-07-09 08:33:19 +00:00
|
|
|
}
|
|
|
|
|
2022-07-09 09:35:54 +00:00
|
|
|
if up_diff > 1024.0 * 1024.0 {
|
2022-07-09 13:53:01 +00:00
|
|
|
write!(&mut output, "{:.2} MiB", up_diff / (1024.0 * 1024.0))?;
|
2022-07-09 09:35:54 +00:00
|
|
|
} else if up_diff > 1024.0 {
|
2022-07-09 13:53:01 +00:00
|
|
|
write!(&mut output, "{:.2} KiB", up_diff / 1024.0)?;
|
2022-07-09 08:33:19 +00:00
|
|
|
} else {
|
2022-07-09 13:53:01 +00:00
|
|
|
write!(&mut output, "{:.0} B", up_diff)?;
|
2022-07-09 08:33:19 +00:00
|
|
|
}
|
|
|
|
|
2022-12-11 12:49:51 +00:00
|
|
|
let diff_max = if down_diff > up_diff {
|
|
|
|
down_diff
|
|
|
|
} else {
|
|
|
|
up_diff
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(graph_max) = graph_max_opt {
|
2022-10-18 12:04:28 +00:00
|
|
|
let graph_value: u8 = if diff_max > graph_max {
|
|
|
|
8
|
|
|
|
} else {
|
2022-10-19 02:11:24 +00:00
|
|
|
(diff_max / graph_max * 8.0f64).round() as u8
|
2022-10-18 12:04:28 +00:00
|
|
|
};
|
2022-10-18 12:14:45 +00:00
|
|
|
|
|
|
|
self.graph.remove(0);
|
2022-10-18 12:04:28 +00:00
|
|
|
match graph_value {
|
2022-10-18 12:14:45 +00:00
|
|
|
0 => self.graph.push(' '),
|
|
|
|
1 => self.graph.push('▁'),
|
|
|
|
2 => self.graph.push('▂'),
|
|
|
|
3 => self.graph.push('▃'),
|
|
|
|
4 => self.graph.push('▄'),
|
|
|
|
5 => self.graph.push('▅'),
|
|
|
|
6 => self.graph.push('▆'),
|
|
|
|
7 => self.graph.push('▇'),
|
|
|
|
_ => self.graph.push('█'),
|
2022-10-18 12:04:28 +00:00
|
|
|
}
|
2022-12-11 12:49:51 +00:00
|
|
|
} else {
|
|
|
|
self.graph_history.rotate_left(1);
|
|
|
|
self.graph_history[9] = diff_max;
|
|
|
|
|
|
|
|
let mut history_max: f64 = 0.0;
|
|
|
|
for value in &self.graph_history {
|
|
|
|
if history_max < *value {
|
|
|
|
history_max = *value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.graph.clear();
|
|
|
|
if history_max == 0.0 {
|
|
|
|
self.graph = String::from(" ");
|
|
|
|
} else {
|
|
|
|
for value in &self.graph_history {
|
|
|
|
match (8.0 * value / history_max).round() as u8 {
|
|
|
|
0 => self.graph.push(' '),
|
|
|
|
1 => self.graph.push('▁'),
|
|
|
|
2 => self.graph.push('▂'),
|
|
|
|
3 => self.graph.push('▃'),
|
|
|
|
4 => self.graph.push('▄'),
|
|
|
|
5 => self.graph.push('▅'),
|
|
|
|
6 => self.graph.push('▆'),
|
|
|
|
7 => self.graph.push('▇'),
|
|
|
|
_ => self.graph.push('█'),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert_eq!(self.graph_history.len(), 10);
|
2022-10-18 12:04:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok((output, self.graph.clone()))
|
2022-07-09 08:33:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-09 13:31:01 +00:00
|
|
|
pub fn get_meminfo() -> Result<String, Error> {
|
2022-07-09 07:57:49 +00:00
|
|
|
let mut meminfo_string = String::new();
|
|
|
|
{
|
|
|
|
let mut meminfo: File = File::open("/proc/meminfo")?;
|
|
|
|
meminfo.read_to_string(&mut meminfo_string)?;
|
|
|
|
}
|
|
|
|
|
2022-07-09 09:21:28 +00:00
|
|
|
let mut is_total_mega = false;
|
2022-07-09 07:57:49 +00:00
|
|
|
let mut total: u32 = 0;
|
|
|
|
let mut available: u32 = 0;
|
|
|
|
for line in meminfo_string.lines() {
|
|
|
|
if line.starts_with("MemTotal:") {
|
|
|
|
let line_parts = line
|
|
|
|
.split_whitespace()
|
|
|
|
.map(|s| s.to_owned())
|
|
|
|
.collect::<Vec<String>>();
|
2022-07-10 04:13:57 +00:00
|
|
|
total = line_parts[1].parse()?;
|
2022-07-09 07:57:49 +00:00
|
|
|
} else if line.starts_with("MemAvailable:") {
|
|
|
|
let line_parts = line
|
|
|
|
.split_whitespace()
|
|
|
|
.map(|s| s.to_owned())
|
|
|
|
.collect::<Vec<String>>();
|
2022-07-10 04:13:57 +00:00
|
|
|
available = line_parts[1].parse()?;
|
2022-07-09 07:57:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-10 08:02:54 +00:00
|
|
|
let mut used: f64 = (total - available) as f64;
|
2022-07-09 09:21:28 +00:00
|
|
|
let mut is_used_mega = false;
|
2022-07-09 07:57:49 +00:00
|
|
|
|
|
|
|
if total == 0 {
|
|
|
|
Ok("0".into())
|
|
|
|
} else {
|
2022-07-10 08:02:54 +00:00
|
|
|
let mut total = total as f64;
|
|
|
|
|
|
|
|
if total > 1024.0 {
|
|
|
|
total /= 1024.0;
|
2022-07-09 09:21:28 +00:00
|
|
|
is_total_mega = true;
|
2022-07-09 07:57:49 +00:00
|
|
|
}
|
|
|
|
|
2022-07-10 08:02:54 +00:00
|
|
|
if used > 1024.0 {
|
|
|
|
used /= 1024.0;
|
2022-07-09 09:21:28 +00:00
|
|
|
is_used_mega = true;
|
2022-07-09 07:57:49 +00:00
|
|
|
}
|
|
|
|
|
2022-07-10 08:02:54 +00:00
|
|
|
let mut output: String;
|
2022-07-09 09:21:28 +00:00
|
|
|
if is_used_mega {
|
2022-07-10 08:02:54 +00:00
|
|
|
output = format!("{:.2} ", used);
|
2022-07-09 09:21:28 +00:00
|
|
|
output.push_str("MiB / ");
|
2022-07-09 07:57:49 +00:00
|
|
|
} else {
|
2022-07-10 08:02:54 +00:00
|
|
|
output = format!("{:.0} ", used);
|
2022-07-09 07:57:49 +00:00
|
|
|
output.push_str("KiB / ");
|
|
|
|
}
|
|
|
|
|
2022-07-09 09:21:28 +00:00
|
|
|
if is_total_mega {
|
2022-07-10 08:02:54 +00:00
|
|
|
write!(&mut output, "{:.2} ", total)?;
|
2022-07-09 09:21:28 +00:00
|
|
|
output.push_str("MiB");
|
2022-07-09 07:57:49 +00:00
|
|
|
} else {
|
2022-07-10 08:02:54 +00:00
|
|
|
write!(&mut output, "{:.0} ", total)?;
|
2022-07-09 07:57:49 +00:00
|
|
|
output.push_str("KiB");
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(output)
|
|
|
|
}
|
|
|
|
}
|
2022-07-09 08:04:49 +00:00
|
|
|
|
2022-07-09 13:31:01 +00:00
|
|
|
pub fn get_loadavg() -> Result<String, Error> {
|
2022-07-09 08:04:49 +00:00
|
|
|
let mut loadavg_string = String::new();
|
|
|
|
{
|
|
|
|
let mut loadavg_file: File = File::open("/proc/loadavg")?;
|
|
|
|
loadavg_file.read_to_string(&mut loadavg_string)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let loadavg_parts: Vec<&str> = loadavg_string.split_whitespace().collect();
|
|
|
|
if loadavg_parts.len() < 3 {
|
2022-07-09 13:31:01 +00:00
|
|
|
return Err("loadavg: failed to parse".to_owned().into());
|
2022-07-09 08:04:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(format!(
|
|
|
|
"{} {} {}",
|
|
|
|
loadavg_parts[0], loadavg_parts[1], loadavg_parts[2]
|
|
|
|
))
|
|
|
|
}
|