diff --git a/README.md b/README.md index d6d3eacfc..958b58f30 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ println!("NB processors: {}", sys.processors().len()); // Display processes ID, name na disk usage: for (pid, process) in sys.processes() { - println!("[{}] {} {:?}", pid, process.name(), process.disk_usage()); + println!("[{}] {} {:?} {:?}", pid, process.name(), process.disk_usage(), process.network_usage()); } ``` diff --git a/src/common.rs b/src/common.rs index d2a1b2904..3e99a20d1 100644 --- a/src/common.rs +++ b/src/common.rs @@ -169,6 +169,7 @@ assert_eq!(r.", stringify!($name), "(), false); pub struct ProcessRefreshKind { cpu: bool, disk_usage: bool, + network_usage: bool, } impl ProcessRefreshKind { @@ -195,11 +196,13 @@ impl ProcessRefreshKind { /// /// assert_eq!(r.cpu(), true); /// assert_eq!(r.disk_usage(), true); + /// assert_eq!(r.network_usage(), true); /// ``` pub fn everything() -> Self { Self { cpu: true, disk_usage: true, + network_usage: true, } } @@ -210,6 +213,12 @@ impl ProcessRefreshKind { with_disk_usage, without_disk_usage ); + impl_get_set!( + ProcessRefreshKind, + network_usage, + with_network_usage, + without_network_usage + ); } /// Used to determine what you want to refresh specifically on the [`System`] type. @@ -694,6 +703,40 @@ pub struct DiskUsage { pub read_bytes: u64, } +/// Type containing received and trasmitted bytes on all interface. +/// +/// It is returned by [`ProcessExt::network_usage`][crate::ProcessExt::network_usage]. +/// +/// ```no_run +/// use sysinfo::{ProcessExt, System, SystemExt}; +/// +/// let s = System::new_all(); +/// for (pid, process) in s.processes() { +/// let network_usage = process.network_usage(); +/// println!("[{}] received bytes : new/total => {}/{} B", +/// pid, +/// network_usage.received_bytes, +/// network_usage.total_received_bytes, +/// ); +/// println!("[{}] transmitted bytes: new/total => {}/{} B", +/// pid, +/// network_usage.transmitted_bytes, +/// network_usage.total_transmitted_bytes, +/// ); +/// } +/// ``` +#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)] +pub struct NetworkUsage { + /// Total number of transmit bytes. + pub total_transmitted_bytes: u64, + /// Number of transmit bytes since the last refresh. + pub transmitted_bytes: u64, + /// Total number of receive bytes. + pub total_received_bytes: u64, + /// Number of receive bytes since the last refresh. + pub received_bytes: u64, +} + /// Enum describing the different status of a process. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ProcessStatus { diff --git a/src/debug.rs b/src/debug.rs index b2ce91c96..feb74348b 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -75,6 +75,7 @@ impl fmt::Debug for Process { .field("status", &self.status()) .field("root", &self.root()) .field("disk_usage", &self.disk_usage()) + .field("network_usage", &self.network_usage()) .finish() } } diff --git a/src/lib.rs b/src/lib.rs index 34d27323f..57d8c5a49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,7 +59,7 @@ cfg_if::cfg_if! { } pub use common::{ - get_current_pid, DiskType, DiskUsage, Gid, LoadAvg, NetworksIter, Pid, PidExt, + get_current_pid, DiskType, DiskUsage, Gid, LoadAvg, NetworkUsage, NetworksIter, Pid, PidExt, ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, Uid, User, }; pub use sys::{Component, Disk, NetworkData, Networks, Process, Processor, System}; diff --git a/src/linux/process.rs b/src/linux/process.rs index 10c0b14b5..16bc81675 100644 --- a/src/linux/process.rs +++ b/src/linux/process.rs @@ -14,7 +14,7 @@ use libc::{gid_t, kill, uid_t}; use crate::sys::system::{SystemInfo, REMAINING_FILES}; use crate::sys::utils::{get_all_data, get_all_data_from_file, realpath}; use crate::utils::into_iter; -use crate::{DiskUsage, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal}; +use crate::{DiskUsage, NetworkUsage, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal}; #[doc(hidden)] impl From for ProcessStatus { @@ -100,6 +100,10 @@ pub struct Process { old_written_bytes: u64, read_bytes: u64, written_bytes: u64, + transmitted_bytes: u64, + old_transmitted_bytes: u64, + received_bytes: u64, + old_received_bytes: u64, } impl Process { @@ -142,6 +146,10 @@ impl Process { old_written_bytes: 0, read_bytes: 0, written_bytes: 0, + transmitted_bytes: 0, + old_transmitted_bytes: 0, + received_bytes: 0, + old_received_bytes: 0, } } } @@ -216,6 +224,17 @@ impl ProcessExt for Process { total_read_bytes: self.read_bytes, } } + + fn network_usage(&self) -> NetworkUsage { + NetworkUsage { + transmitted_bytes: self + .transmitted_bytes + .saturating_sub(self.old_transmitted_bytes), + total_transmitted_bytes: self.transmitted_bytes, + received_bytes: self.received_bytes.saturating_sub(self.old_received_bytes), + total_received_bytes: self.received_bytes, + } + } } impl Drop for Process { @@ -253,6 +272,40 @@ pub(crate) fn set_time(p: &mut Process, utime: u64, stime: u64) { p.updated = true; } +pub(crate) fn update_process_network_activity(p: &mut Process, path: &Path) { + let mut path = PathBuf::from(path); + path.push("net/dev"); + let data = match get_all_data(&path, 16_384) { + Ok(d) => d, + Err(_) => return, + }; + p.old_received_bytes = p.received_bytes; + p.old_transmitted_bytes = p.transmitted_bytes; + + let mut received_bytes = 0; + let mut transmitted_bytes = 0; + + let mut lines = data.split('\n'); + + // Skip headers. + lines.next(); + lines.next(); + + for line in lines { + let values = line.split_whitespace().collect::>(); + if values.len() < 17 { + continue; + } + + // received and transmitted bytes are contained in the first and ninth column. + received_bytes += values[1].parse::().ok().unwrap(); + transmitted_bytes += values[9].parse::().ok().unwrap(); + } + + p.received_bytes = received_bytes; + p.transmitted_bytes = transmitted_bytes; +} + pub(crate) fn update_process_disk_activity(p: &mut Process, path: &Path) { let mut path = PathBuf::from(path); path.push("io"); @@ -348,6 +401,9 @@ pub(crate) fn _get_process_data( if refresh_kind.disk_usage() { update_process_disk_activity(entry, path); } + if refresh_kind.network_usage() { + update_process_network_activity(entry, path); + } return Ok((None, pid)); } @@ -435,6 +491,9 @@ pub(crate) fn _get_process_data( if refresh_kind.disk_usage() { update_process_disk_activity(&mut p, path); } + if refresh_kind.network_usage() { + update_process_network_activity(&mut p, path); + } Ok((Some(p), pid)) } diff --git a/src/traits.rs b/src/traits.rs index 64d8c322c..f4ad33115 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -5,8 +5,8 @@ use crate::{ sys::{Component, Disk, Networks, Process, Processor}, }; use crate::{ - DiskType, DiskUsage, LoadAvg, NetworksIter, Pid, ProcessRefreshKind, ProcessStatus, - RefreshKind, Signal, User, + DiskType, DiskUsage, LoadAvg, NetworkUsage, NetworksIter, Pid, ProcessRefreshKind, + ProcessStatus, RefreshKind, Signal, User, }; use std::collections::HashMap; @@ -370,6 +370,28 @@ pub trait ProcessExt: Debug { /// } /// ``` fn disk_usage(&self) -> DiskUsage; + + /// Returns number of bytes received and transmitted to all network. + /// + /// ⚠️ This is currently only implemented on linux. + /// + /// ```no_run + /// use sysinfo::{Pid, ProcessExt, System, SystemExt}; + /// + /// let s = System::new(); + /// if let Some(process) = s.process(Pid::from(1337)) { + /// let network_usage = process.network_usage(); + /// println!("received bytes : new/total => {}/{}", + /// network_usage.received_bytes, + /// network_usage.total_received_bytes, + /// ); + /// println!("transmitted bytes: new/total => {}/{}", + /// network_usage.transmitted_bytes, + /// network_usage.total_transmitted_bytes, + /// ); + /// } + /// ``` + fn network_usage(&self) -> NetworkUsage; } /// Contains all the methods of the [`Processor`][crate::Processor] struct. diff --git a/src/unknown/process.rs b/src/unknown/process.rs index 29215e23e..7991afcf8 100644 --- a/src/unknown/process.rs +++ b/src/unknown/process.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::{DiskUsage, Pid, ProcessExt, ProcessStatus, Signal}; +use crate::{DiskUsage, NetworkUsage, Pid, ProcessExt, ProcessStatus, Signal}; use std::fmt; use std::path::Path; @@ -81,4 +81,8 @@ impl ProcessExt for Process { fn disk_usage(&self) -> DiskUsage { DiskUsage::default() } + + fn network_usage(&self) -> NetworkUsage { + NetworkUsage::default() + } }