Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support drops column in /proc/net/udp #316

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ categories = ["os::unix-apis", "filesystem"]
license = "MIT OR Apache-2.0"
edition = "2018"
rust-version = "1.48"

[workspace.lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tarpaulin)'] }
10 changes: 8 additions & 2 deletions procfs-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,20 @@ rust-version.workspace = true

[features]
default = ["chrono"]
serde1 = ["serde", "bitflags/serde"]
serde1 = ["serde", "bitflags/serde", "serde_with"]

[dependencies]
backtrace = { version = "0.3", optional = true }
bitflags = { version = "2" }
chrono = { version = "0.4.20", optional = true, features = ["clock"], default-features = false }
chrono = { version = "0.4.20", optional = true, features = [
"clock",
], default-features = false }
hex = "0.4"
serde = { version = "1.0", features = ["derive"], optional = true }
serde_with = { version = "3.11", optional = true }

[dev-dependencies]
serde_json = { version = "1.0" }

[package.metadata.docs.rs]
all-features = true
7 changes: 7 additions & 0 deletions procfs-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ macro_rules! from_str {
/// ticks_per_second: 100,
/// page_size: 4096,
/// is_little_endian: true,
/// kernel_version: "6.11.0".parse().unwrap(),
/// };
///
/// let rss_bytes = stat.rss_bytes().with_system_info(&system_info);
Expand All @@ -257,6 +258,7 @@ pub trait SystemInfoInterface {
fn page_size(&self) -> u64;
/// Whether the system is little endian (true) or big endian (false).
fn is_little_endian(&self) -> bool;
fn kernel_version(&self) -> ProcResult<KernelVersion>;

#[cfg(feature = "chrono")]
fn boot_time(&self) -> ProcResult<chrono::DateTime<chrono::Local>> {
Expand All @@ -277,6 +279,7 @@ pub struct ExplicitSystemInfo {
pub ticks_per_second: u64,
pub page_size: u64,
pub is_little_endian: bool,
pub kernel_version: KernelVersion,
}

impl SystemInfoInterface for ExplicitSystemInfo {
Expand All @@ -295,6 +298,10 @@ impl SystemInfoInterface for ExplicitSystemInfo {
fn is_little_endian(&self) -> bool {
self.is_little_endian
}

fn kernel_version(&self) -> ProcResult<KernelVersion> {
Ok(self.kernel_version)
}
}

/// Values which can provide an output given the [SystemInfo].
Expand Down
65 changes: 62 additions & 3 deletions procfs-core/src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
//!
//! This module corresponds to the `/proc/net` directory and contains various information about the
//! networking layer.
use crate::ProcResult;
use crate::{build_internal_error, expect, from_iter, from_str};
use std::collections::HashMap;

use crate::{KernelVersion, ProcResult};
use bitflags::bitflags;
use std::collections::HashMap;
use std::io::BufRead;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::{path::PathBuf, str::FromStr};
Expand Down Expand Up @@ -152,6 +151,7 @@ pub struct UdpNetEntry {
pub tx_queue: u32,
pub uid: u32,
pub inode: u64,
pub drops: u64,
}

/// An entry in the Unix socket table
Expand Down Expand Up @@ -296,6 +296,15 @@ impl super::FromBufReadSI for UdpNetEntries {
s.next(); // skip timeout
let inode = expect!(s.next(), "udp::inode");

let drops = match system_info.kernel_version() {
Ok(version) if version >= KernelVersion::new(2, 6, 27) => {
s.next(); // skip ref
s.next(); // skip pointer
expect!(s.next(), "udp::drops")
}
_ => "0", // Fallback if the kernel version does not support the drops column or the kernel version could not be read
};

vec.push(UdpNetEntry {
local_address: parse_addressport_str(local_address, system_info.is_little_endian())?,
remote_address: parse_addressport_str(rem_address, system_info.is_little_endian())?,
Expand All @@ -304,6 +313,7 @@ impl super::FromBufReadSI for UdpNetEntries {
state: expect!(UdpState::from_u8(from_str!(u8, state, 16))),
uid,
inode: from_str!(u64, inode),
drops: from_str!(u64, drops),
});
}

Expand Down Expand Up @@ -1728,4 +1738,53 @@ UdpLite: 0 0 0 0 0 0 0 0 0
let res = Snmp::from_read(r).unwrap();
println!("{res:?}");
}

#[test]
fn test_udp_drops_debian_kernel_version_greater_2_6_27() {
let expected_drops = 42;
let data = format!(
r#" sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
1270: 00000000:0202 00000000:0000 07 00000000:00000000 00:00000000 00000000 104 0 3719665 2 00000000357eb7c3 {expected_drops}"#
);
let r = std::io::Cursor::new(data.as_bytes());
use crate::FromBufReadSI;
let entries = UdpNetEntries::from_buf_read(
r,
&crate::ExplicitSystemInfo {
boot_time_secs: 0,
ticks_per_second: 0,
page_size: 0,
is_little_endian: true,
kernel_version: "2.6.27".parse().unwrap(),
},
)
.unwrap();
let entry = entries.0.first().unwrap();

assert_eq!(entry.drops, expected_drops)
}

#[test]
fn test_udp_drops_debian_kernel_version_smaller_2_6_27() {
let data = format!(
r#" sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
1270: 00000000:0202 00000000:0000 07 00000000:00000000 00:00000000 00000000 104 0 3719665"#
);
let r = std::io::Cursor::new(data.as_bytes());
use crate::FromBufReadSI;
let entries = UdpNetEntries::from_buf_read(
r,
&crate::ExplicitSystemInfo {
boot_time_secs: 0,
ticks_per_second: 0,
page_size: 0,
is_little_endian: true,
kernel_version: "2.6.26".parse().unwrap(),
},
)
.unwrap();
let entry = entries.0.first().unwrap();

assert_eq!(entry.drops, 0)
}
}
43 changes: 41 additions & 2 deletions procfs-core/src/sys/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@
//! The files in this directory can be used to tune and monitor miscellaneous
//! and general things in the operation of the Linux kernel.

use std::cmp;
use std::collections::HashSet;
use std::str::FromStr;
use std::{cmp, fmt::Display};

#[cfg(feature = "serde1")]
use serde_with::{DeserializeFromStr, SerializeDisplay};

use bitflags::bitflags;

use crate::{ProcError, ProcResult};

/// Represents a kernel version, in major.minor.release version.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
#[cfg_attr(feature = "serde1", derive(SerializeDisplay, DeserializeFromStr))]
pub struct Version {
pub major: u8,
pub minor: u8,
Expand Down Expand Up @@ -99,6 +103,12 @@ impl cmp::PartialOrd for Version {
}
}

impl Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}

/// Represents a kernel type
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Type {
Expand Down Expand Up @@ -425,4 +435,33 @@ mod tests {
let a = SemaphoreLimits::from_str("1 string 500 3200");
assert!(a.is_err() && a.err().unwrap() == "Failed to parse SEMMNS");
}

#[cfg(feature = "serde1")]
mod serde_kernel_version {
#[test]
fn should_serialize_kernel_version() {
let version = Version {
major: 42,
minor: 0,
patch: 1,
};
let version = serde_json::to_string(&version).unwrap();

// NOTE: The double quote is necessary because of the JSON format.
assert_eq!(r#""42.0.1""#, &version);
}

#[test]
fn should_deserialize_kernel_version() {
let expected = Version {
major: 21,
minor: 0,
patch: 2,
};
// NOTE: The double quote is necessary because of the JSON format.
let version: Version = serde_json::from_str(r#""21.0.2""#).unwrap();

assert_eq!(version, expected);
}
}
}
3 changes: 3 additions & 0 deletions procfs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ all-features = true
[[bench]]
name = "cpuinfo"
harness = false

[lints]
workspace = true
7 changes: 5 additions & 2 deletions procfs/benches/cpuinfo.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use procfs::CpuInfo;
use procfs::Current;

fn bench_cpuinfo(c: &mut Criterion) {
c.bench_function("CpuInfo::new", |b| b.iter(|| black_box(CpuInfo::new().unwrap())));
c.bench_function("CpuInfo::current", |b| {
b.iter(|| black_box(CpuInfo::current().unwrap()))
});

let cpuinfo = black_box(CpuInfo::new().unwrap());
let cpuinfo = black_box(CpuInfo::current().unwrap());
c.bench_function("CpuInfo::get_info", |b| b.iter(|| black_box(cpuinfo.get_info(0))));
c.bench_function("CpuInfo::model_name", |b| b.iter(|| cpuinfo.model_name(0)));
c.bench_function("CpuInfo::vendor_id", |b| b.iter(|| cpuinfo.vendor_id(0)));
Expand Down
5 changes: 4 additions & 1 deletion procfs/examples/lslocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use std::path::Path;
fn main() {
let myself = Process::myself().unwrap();
let mountinfo = myself.mountinfo().unwrap();
println!("{:18}{:13}{:13}{:13}{:12} Path", "Process", "PID", "Lock Type", "Mode", "Kind");
println!(
"{:18}{:13}{:13}{:13}{:12} Path",
"Process", "PID", "Lock Type", "Mode", "Kind"
);
println!("{}", "=".repeat(74));
for lock in procfs::locks().unwrap() {
lock.pid
Expand Down
4 changes: 2 additions & 2 deletions procfs/examples/mounts.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// List mountpoints listed in /proc/mounts

fn main() {
let width = 15;
let width = 15;
for mount_entry in procfs::mounts().unwrap() {
println!("Device: {}", mount_entry.fs_spec);
println!("{:>width$}: {}", "Mount point", mount_entry.fs_file);
println!("{:>width$}: {}","FS type", mount_entry.fs_vfstype);
println!("{:>width$}: {}", "FS type", mount_entry.fs_vfstype);
println!("{:>width$}: {}", "Dump", mount_entry.fs_freq);
println!("{:>width$}: {}", "Check", mount_entry.fs_passno);
print!("{:>width$}: ", "Options");
Expand Down
6 changes: 2 additions & 4 deletions procfs/src/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

use procfs_core::ProcResult;
pub use procfs_core::CryptoTable;
use procfs_core::ProcResult;

use crate::Current;

Expand All @@ -12,7 +11,6 @@ pub fn crypto() -> ProcResult<CryptoTable> {
CryptoTable::current()
}


#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -23,4 +21,4 @@ mod tests {
let table = table.expect("CrytoTable should have been read");
assert!(!table.crypto_blocks.is_empty(), "Crypto table was empty");
}
}
}
4 changes: 4 additions & 0 deletions procfs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ impl SystemInfoInterface for LocalSystemInfo {
fn is_little_endian(&self) -> bool {
u16::from_ne_bytes([0, 1]).to_le_bytes() == [0, 1]
}

fn kernel_version(&self) -> ProcResult<procfs_core::KernelVersion> {
KernelVersion::cached().map(Into::into)
}
}

const LOCAL_SYSTEM_INFO: LocalSystemInfo = LocalSystemInfo;
Expand Down
1 change: 0 additions & 1 deletion procfs/src/process/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ use std::path::PathBuf;
use std::str::FromStr;

mod namespaces;
pub use namespaces::*;

mod task;
pub use task::*;
Expand Down
8 changes: 7 additions & 1 deletion procfs/src/sys/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl Version {
Self {
major,
minor,
patch: u16::from_ne_bytes([lo, hi])
patch: u16::from_ne_bytes([lo, hi]),
}
}

Expand Down Expand Up @@ -145,6 +145,12 @@ impl cmp::PartialOrd for Version {
}
}

impl From<Version> for procfs_core::KernelVersion {
fn from(value: Version) -> Self {
Self::new(value.major, value.minor, value.patch)
}
}

/// Represents a kernel type
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Type {
Expand Down
2 changes: 1 addition & 1 deletion procfs/src/sys/kernel/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use crate::{read_value, write_value, ProcError, ProcResult};
use std::path::Path;

const RANDOM_ROOT: &str = "/proc/sys/kernel/random";
const RANDOM_ROOT: &str = "/proc/sys/kernel/random";

/// This read-only file gives the available entropy, in bits. This will be a number in the range
/// 0 to 4096
Expand Down