Compare commits
2 Commits
3963bce64e
...
c9180033d1
Author | SHA1 | Date | |
---|---|---|---|
c9180033d1 | |||
a03a679f71 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -20,7 +20,7 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linkspeed"
|
name = "linkspeed"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"eyre",
|
"eyre",
|
||||||
]
|
]
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "linkspeed"
|
name = "linkspeed"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
unsafe_code = "forbid"
|
unsafe_code = "forbid"
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
|
redundant_closure_for_method_calls = "allow"
|
||||||
similar_names = "allow"
|
similar_names = "allow"
|
||||||
|
|
||||||
pedantic = { level = "warn", priority = -1 }
|
pedantic = { level = "warn", priority = -1 }
|
||||||
|
@ -6,6 +6,8 @@ mod sysfs;
|
|||||||
|
|
||||||
use std::{env, thread::sleep};
|
use std::{env, thread::sleep};
|
||||||
|
|
||||||
|
use sysfs::NetdevError;
|
||||||
|
|
||||||
use crate::{cfg::Config, measure::LinkSpeed};
|
use crate::{cfg::Config, measure::LinkSpeed};
|
||||||
|
|
||||||
fn main() -> eyre::Result<()> {
|
fn main() -> eyre::Result<()> {
|
||||||
@ -19,7 +21,12 @@ fn main() -> eyre::Result<()> {
|
|||||||
(Some(_), Some(na)) => na,
|
(Some(_), Some(na)) => na,
|
||||||
(Some(nc), None) => nc,
|
(Some(nc), None) => nc,
|
||||||
(None, Some(na)) => na,
|
(None, Some(na)) => na,
|
||||||
(None, None) => eyre::bail!("No network device specified"),
|
(None, None) => {
|
||||||
|
eyre::bail!(
|
||||||
|
"No network device specified. Please provide a network device name as an argument or in the config file.\n\n{}",
|
||||||
|
NetdevError::available_netdevs_msg()
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let link_speed = LinkSpeed::new(&netdev_name)?;
|
let link_speed = LinkSpeed::new(&netdev_name)?;
|
||||||
|
78
src/sysfs.rs
78
src/sysfs.rs
@ -3,24 +3,73 @@ use std::{
|
|||||||
io::{Read as _, Seek as _, SeekFrom},
|
io::{Read as _, Seek as _, SeekFrom},
|
||||||
};
|
};
|
||||||
|
|
||||||
use eyre::Context as _;
|
pub struct Netdev {
|
||||||
|
|
||||||
pub(crate) struct Netdev {
|
|
||||||
rx_bytes: File,
|
rx_bytes: File,
|
||||||
tx_bytes: File,
|
tx_bytes: File,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum NetdevError {
|
||||||
|
NotFound { name: String },
|
||||||
|
IoError { name: String, err: std::io::Error },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetdevError {
|
||||||
|
pub fn available_netdevs_msg() -> String {
|
||||||
|
let list = Netdev::list()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|dev| format!(" {dev}"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
format!("Available network devices:\n{list}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for NetdevError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
NetdevError::NotFound { name } => write!(
|
||||||
|
f,
|
||||||
|
"Netdev '{name}' not found.\n\n{}",
|
||||||
|
Self::available_netdevs_msg()
|
||||||
|
),
|
||||||
|
NetdevError::IoError { name, err } => {
|
||||||
|
write!(f, "I/O Error occurred for netdev '{name}': {err}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Netdev {
|
impl Netdev {
|
||||||
|
const SYS_CLASS_NET: &'static str = "/sys/class/net";
|
||||||
|
|
||||||
pub fn from_name(name: &str) -> eyre::Result<Self> {
|
pub fn from_name(name: &str) -> eyre::Result<Self> {
|
||||||
let rx_bytes = Self::open_netdev(name, "rx_bytes")?;
|
let rx_bytes = Self::open_netdev_stat(name, "rx_bytes")?;
|
||||||
let tx_bytes = Self::open_netdev(name, "tx_bytes")?;
|
let tx_bytes = Self::open_netdev_stat(name, "tx_bytes")?;
|
||||||
|
|
||||||
Ok(Self { rx_bytes, tx_bytes })
|
Ok(Self { rx_bytes, tx_bytes })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_netdev(dev: &str, stat: &str) -> eyre::Result<File> {
|
fn open_netdev_stat(name: &str, stat: &str) -> eyre::Result<File> {
|
||||||
let path = format!("/sys/class/net/{dev}/statistics/{stat}");
|
let sys_class_net = Self::SYS_CLASS_NET;
|
||||||
File::open(&path).wrap_err(format!("Failed to open netdev '{dev}'"))
|
let path = format!("{sys_class_net}/{name}/statistics/{stat}");
|
||||||
|
match File::open(&path) {
|
||||||
|
Ok(file) => Ok(file),
|
||||||
|
Err(e) => {
|
||||||
|
if e.kind() == std::io::ErrorKind::NotFound {
|
||||||
|
Err(eyre::eyre!(NetdevError::NotFound {
|
||||||
|
name: name.to_string(),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(eyre::eyre!(NetdevError::IoError {
|
||||||
|
name: name.to_string(),
|
||||||
|
err: e,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_bytes(file: &mut File) -> eyre::Result<u64> {
|
fn read_bytes(file: &mut File) -> eyre::Result<u64> {
|
||||||
@ -37,4 +86,17 @@ impl Netdev {
|
|||||||
pub fn tx_bytes(&mut self) -> eyre::Result<u64> {
|
pub fn tx_bytes(&mut self) -> eyre::Result<u64> {
|
||||||
Self::read_bytes(&mut self.tx_bytes)
|
Self::read_bytes(&mut self.tx_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn list() -> eyre::Result<Vec<String>> {
|
||||||
|
let mut entries = std::fs::read_dir(Self::SYS_CLASS_NET)?
|
||||||
|
.filter_map(|entry| {
|
||||||
|
entry
|
||||||
|
.ok()
|
||||||
|
.and_then(|entry| entry.file_name().into_string().ok())
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
entries.sort();
|
||||||
|
Ok(entries)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user