From c9d850287cb8516158cfc7d9d9f9f5a27933e15f Mon Sep 17 00:00:00 2001 From: Navid Yaghoobi Date: Sun, 18 Aug 2024 18:41:30 +1000 Subject: [PATCH] added /proc//ns Signed-off-by: Navid Yaghoobi --- FEATURES.md | 1 + examples/process_ns.rs | 14 ++++++ src/lib.rs | 1 + src/process_ns.rs | 102 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 examples/process_ns.rs create mode 100644 src/process_ns.rs diff --git a/FEATURES.md b/FEATURES.md index 92ebe21..aa40b10 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -9,6 +9,7 @@ Supported Features * io * limits * root + * ns * ✅ `/proc/buddyinfo` diff --git a/examples/process_ns.rs b/examples/process_ns.rs new file mode 100644 index 0000000..95eb1e6 --- /dev/null +++ b/examples/process_ns.rs @@ -0,0 +1,14 @@ +use procsys::process; + +fn main() { + let sys_proc = process::collect(2).expect("system proc 2"); + let sys_proc_ns = sys_proc.namespaces().expect("system proc 2 namespaces"); + + match serde_json::to_string_pretty(&sys_proc_ns) { + Ok(output) => println!("{}", output), + Err(err) => { + log::error!("{}", err); + std::process::exit(1); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index fc3cbcc..4d06aa0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ pub mod process; pub mod process_cgroup; pub mod process_io; pub mod process_limits; +pub mod process_ns; pub mod swaps; pub mod sysfs; pub mod utils; diff --git a/src/process_ns.rs b/src/process_ns.rs new file mode 100644 index 0000000..353dde4 --- /dev/null +++ b/src/process_ns.rs @@ -0,0 +1,102 @@ +use std::{collections::HashMap, fs::read_link}; + +use serde::Serialize; + +use crate::{ + error::{CollectResult, MetricError}, + process::Process, + utils, +}; + +/// ProcessNamespace represents a single namespace of a process +#[derive(Debug, Serialize, Clone, Default)] +pub struct ProcessNamespace { + pub ns_type: String, + pub inode: u32, +} + +impl ProcessNamespace { + pub fn new() -> Self { + Default::default() + } +} + +impl Process { + /// Namespaces reads from /proc/\/ns/* to get the namespaces of which the process is a member + pub fn namespaces(&self) -> CollectResult> { + let mut proc_namespaces: HashMap = HashMap::new(); + + let mut proc_ns_path = self.path(); + proc_ns_path.push("ns"); + + for ns_item in utils::list_dir_content(&proc_ns_path, "", "ns") { + let mut ns_item_path = proc_ns_path.clone(); + ns_item_path.push(&ns_item); + + match read_link(&ns_item_path) { + Ok(c) => { + let item_fields: Vec<&str> = c + .to_str() + .unwrap_or_default() + .trim() + .split(':') + .filter(|s| !s.is_empty()) + .collect(); + + if item_fields.len() != 2 { + return Err(MetricError::InvalidFieldNumberError( + "process ns item".to_string(), + item_fields.len(), + c.to_str().unwrap_or_default().to_string(), + )); + } + + let mut proc_ns = ProcessNamespace::new(); + let proc_ns_inode = item_fields[1].trim().trim_matches('[').trim_matches(']'); + + proc_ns.ns_type = item_fields[0].trim().to_string(); + proc_ns.inode = proc_ns_inode.parse::().unwrap_or_default(); + + proc_namespaces.insert(ns_item, proc_ns); + } + Err(e) => return Err(MetricError::IOError(ns_item_path, e)), + } + } + + Ok(proc_namespaces) + } +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + use crate::process::*; + + #[test] + fn proc_ns() { + let proc_path = Path::new("test_data/fixtures/proc"); + let sys_proc = collect_from(proc_path, 26231).expect("running proc 26231"); + let sys_proc_ns = sys_proc + .namespaces() + .expect("running proc 26231 namespaces"); + + assert_eq!(sys_proc_ns.len(), 2); + assert_eq!(sys_proc_ns.contains_key("mnt"), true); + assert_eq!(sys_proc_ns.contains_key("net"), true); + + for (key, proc_ns) in &sys_proc_ns { + match key.as_str() { + "mnt" => { + assert_eq!(proc_ns.ns_type, "mnt"); + assert_eq!(proc_ns.inode, 4026531840); + } + "net" => { + assert_eq!(proc_ns.ns_type, "net"); + assert_eq!(proc_ns.inode, 4026531993); + } + _ => panic!("invalid namespace key: {}", key), + } + } + } +}