#![allow(unknown_lints)] // The suggested fix with `str::parse` removes support for Rust 1.48 #![allow(clippy::from_str_radix_10)] #![deny(broken_intra_doc_links, invalid_html_tags)] //! This crate provides to an interface into the linux `procfs` filesystem, usually mounted at //! `/proc`. //! //! This is a pseudo-filesystem which is available on most every linux system and provides an //! interface to kernel data structures. //! //! # `procfs-core` //! //! The `procfs-core` crate is a fully platform-independent crate that contains most of the data-structures and //! parsing code. Most people should first look at the `procfs` crate instead.
use bitflags::bitflags;
use std::fmt; use std::io::{BufRead, BufReader, Read}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{collections::HashMap, time::Duration};
#[cfg(feature = "serde1")] use serde::{Deserialize, Serialize};
/// Types which can be parsed from a Read implementation. pubtrait FromRead: Sized { /// Read the type from a Read. fn from_read<R: Read>(r: R) -> ProcResult<Self>;
/// Read the type from a file. fn from_file<P: AsRef<Path>>(path: P) -> ProcResult<Self> {
std::fs::File::open(path.as_ref())
.map_err(|e| e.into())
.and_then(|f| Self::from_read(f))
.map_err(|e| e.error_path(path.as_ref()))
}
}
/// Types which can be parsed from a BufRead implementation. pubtrait FromBufRead: Sized { fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self>;
}
impl<T: FromBufRead> FromRead for T { fn from_read<R: Read>(r: R) -> ProcResult<Self> { Self::from_buf_read(BufReader::new(r))
}
}
/// Types which can be parsed from a Read implementation and system info. pubtrait FromReadSI: Sized { /// Parse the type from a Read and system info. fn from_read<R: Read>(r: R, system_info: &SystemInfo) -> ProcResult<Self>;
/// Parse the type from a file. fn from_file<P: AsRef<Path>>(path: P, system_info: &SystemInfo) -> ProcResult<Self> {
std::fs::File::open(path.as_ref())
.map_err(|e| e.into())
.and_then(|f| Self::from_read(f, system_info))
.map_err(|e| e.error_path(path.as_ref()))
}
}
/// Types which can be parsed from a BufRead implementation and system info. pubtrait FromBufReadSI: Sized { fn from_buf_read<R: BufRead>(r: R, system_info: &SystemInfo) -> ProcResult<Self>;
}
impl<T: FromBufReadSI> FromReadSI for T { fn from_read<R: Read>(r: R, system_info: &SystemInfo) -> ProcResult<Self> { Self::from_buf_read(BufReader::new(r), system_info)
}
}
#[macro_export] #[doc(hidden)]
macro_rules! from_str {
($t:tt, $e:expr) => {{ let e = $e; crate::expect!(
$t::from_str_radix(e, 10),
format!("Failed to parse {} ({:?}) as a {}", stringify!($e), e, stringify!($t),)
)
}};
($t:tt, $e:expr, $radix:expr) => {{ let e = $e; crate::expect!(
$t::from_str_radix(e, $radix),
format!("Failed to parse {} ({:?}) as a {}", stringify!($e), e, stringify!($t))
)
}};
($t:tt, $e:expr, $radix:expr, pid:$pid:expr) => {{ let e = $e; crate::expect!(
$t::from_str_radix(e, $radix),
format!( "Failed to parse {} ({:?}) as a {} (pid {})",
stringify!($e),
e,
stringify!($t),
$pid
)
)
}};
}
/// Auxiliary system information interface. /// /// A few function in this crate require some extra system info to compute their results. For example, /// the [crate::process::Stat::rss_bytes()] function needs to know the page size. Since `procfs-core` only parses /// data and never interacts with a real system, this `SystemInfoInterface` is what allows real system info to be used. /// /// If you are a user of the `procfs` crate, you'll normally use the `[procfs::WithCurrentSystemInfo]` trait. /// For example: /// /// ```rust,ignore /// use procfs::WithCurrentSystemInfo; /// /// let me = procfs::process::Process::myself().unwrap(); /// let stat = me.stat().unwrap(); /// let bytes = stat.rss_bytes().get(); /// ``` /// /// However, imagine that you captured a process's stat info, along with page size: /// ```rust /// # use procfs_core::{FromRead, WithSystemInfo}; /// # let stat_data = std::io::Cursor::new(b"475071 (cat) R 323893 475071 323893 34826 475071 4194304 94 0 0 0 0 0 0 0 20 0 1 0 201288208 5738496 225 18446744073709551615 94881179934720 94881179954601 140722831478832 0 0 0 0 0 0 0 0 0 17 4 0 0 0 0 0 94881179970608 94881179972224 94881184485376 140722831483757 140722831483777 140722831483777 140722831486955 0"); /// let stat = procfs_core::process::Stat::from_read(stat_data).unwrap(); /// /// let system_info = procfs_core::ExplicitSystemInfo { /// boot_time_secs: 1692972606, /// ticks_per_second: 100, /// page_size: 4096, /// is_little_endian: true, /// }; /// /// let rss_bytes = stat.rss_bytes().with_system_info(&system_info); /// ``` pubtrait SystemInfoInterface { fn boot_time_secs(&self) -> ProcResult<u64>; fn ticks_per_second(&self) -> u64; fn page_size(&self) -> u64; /// Whether the system is little endian (true) or big endian (false). fn is_little_endian(&self) -> bool;
#[cfg(feature = "chrono")] fn boot_time(&self) -> ProcResult<chrono::DateTime<chrono::Local>> { use chrono::TimeZone; let date_time = expect!(chrono::Local.timestamp_opt(self.boot_time_secs()? as i64, 0).single());
Ok(date_time)
}
}
/// Auxiliary system information. pubtype SystemInfo = dyn SystemInfoInterface;
fn split_into_num<T: FromStrRadix>(s: &str, sep: char, radix: u32) -> ProcResult<(T, T)> { letmut s = s.split(sep); let a = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix)); let b = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
Ok((a, b))
}
/// This is used to hold both an IO error as well as the path of the file that originated the error #[derive(Debug)] #[doc(hidden)] pubstruct IoErrorWrapper { pub path: PathBuf, pub inner: std::io::Error,
}
/// The main error type for the procfs crate. /// /// For more info, see the [ProcError] type. pubtype ProcResult<T> = Result<T, ProcError>;
/// The various error conditions in the procfs crate. /// /// Most of the variants have an `Option<PathBuf>` component. If the error root cause was related /// to some operation on a file, the path of this file will be stored in this component. #[derive(Debug)] pubenum ProcError { /// A standard permission denied error. /// /// This will be a common error, since some files in the procfs filesystem are only readable by /// the root user.
PermissionDenied(Option<PathBuf>), /// This might mean that the process no longer exists, or that your kernel doesn't support the /// feature you are trying to use.
NotFound(Option<PathBuf>), /// This might mean that a procfs file has incomplete contents. /// /// If you encounter this error, consider retrying the operation.
Incomplete(Option<PathBuf>), /// Any other IO error (rare).
Io(std::io::Error, Option<PathBuf>), /// Any other non-IO error (very rare).
Other(String), /// This error indicates that some unexpected error occurred. This is a bug. The inner /// [InternalError] struct will contain some more info. /// /// If you ever encounter this error, consider it a bug in the procfs crate and please report /// it on github.
InternalError(InternalError),
}
/// Extensions for dealing with ProcErrors. pubtrait ProcErrorExt { /// Add path information to the error. fn error_path(self, path: &Path) -> Self;
}
impl ProcErrorExt for ProcError { fn error_path(mutself, path: &Path) -> Self { use ProcError::*; match &mutself {
PermissionDenied(p) | NotFound(p) | Incomplete(p) | Io(_, p) if p.is_none() => {
*p = Some(path.to_owned());
}
_ => (),
} self
}
}
/// An internal error in the procfs crate /// /// If you encounter this error, consider it a bug and please report it on /// [github](https://github.com/eminence/procfs). /// /// If you compile with the optional `backtrace` feature (disabled by default), /// you can gain access to a stack trace of where the error happened. #[cfg_attr(feature = "serde1", derive(Serialize))] pubstruct InternalError { pub msg: String, pub file: &'static str, pub line: u32, #[cfg(feature = "backtrace")] #[cfg_attr(feature = "serde1", serde(skip))] pub backtrace: backtrace::Backtrace,
}
impl std::fmt::Debug for InternalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f, "bug at {}:{} (please report this procfs bug)\n{}", self.file, self.line, self.msg
)
}
}
impl std::fmt::Display for InternalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f, "bug at {}:{} (please report this procfs bug)\n{}", self.file, self.line, self.msg
)
}
}
impl From<std::io::Error> for ProcError { fn from(io: std::io::Error) -> Self { use std::io::ErrorKind; let kind = io.kind(); // the only way we'll have a path for the IO error is if this IO error // has a inner type if io.get_ref().is_some() { let inner = io.into_inner().unwrap();
// is this inner type a IoErrorWrapper? match inner.downcast::<IoErrorWrapper>() {
Ok(wrapper) => { let path = wrapper.path; match kind {
ErrorKind::PermissionDenied => ProcError::PermissionDenied(Some(path)),
ErrorKind::NotFound => ProcError::NotFound(Some(path)),
_other => { // All platforms happen to have ESRCH=3, and windows actually // translates it to a `NotFound` anyway. const ESRCH: i32 = 3; if matches!(wrapper.inner.raw_os_error(), Some(raw) if raw == ESRCH) { // This "No such process" error gets mapped into a NotFound error return ProcError::NotFound(Some(path));
} else {
ProcError::Io(wrapper.inner, Some(path))
}
}
}
}
Err(io) => { // reconstruct the original error
ProcError::Io(std::io::Error::new(kind, io), None)
}
}
} else { match kind {
ErrorKind::PermissionDenied => ProcError::PermissionDenied(None),
ErrorKind::NotFound => ProcError::NotFound(None),
_other => ProcError::Io(io, None),
}
}
}
}
/// Load average figures. /// /// Load averages are calculated as the number of jobs in the run queue (state R) or waiting for /// disk I/O (state D) averaged over 1, 5, and 15 minutes. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct LoadAverage { /// The one-minute load average pub one: f32, /// The five-minute load average pub five: f32, /// The fifteen-minute load average pub fifteen: f32, /// The number of currently runnable kernel scheduling entities (processes, threads). pub cur: u32, /// The number of kernel scheduling entities that currently exist on the system. pub max: u32, /// The fifth field is the PID of the process that was most recently created on the system. pub latest_pid: u32,
}
impl FromRead for LoadAverage { fn from_read<R: Read>(mut reader: R) -> ProcResult<Self> { letmut line = String::new();
reader.read_to_string(&mut line)?; letmut s = line.split_whitespace();
let one = expect!(f32::from_str(expect!(s.next()))); let five = expect!(f32::from_str(expect!(s.next()))); let fifteen = expect!(f32::from_str(expect!(s.next()))); let curmax = expect!(s.next()); let latest_pid = expect!(u32::from_str(expect!(s.next())));
letmut s = curmax.split('/'); let cur = expect!(u32::from_str(expect!(s.next()))); let max = expect!(u32::from_str(expect!(s.next())));
for line in r.lines() { let line = line?; if line.starts_with('#') { continue;
} if line.contains('=') { letmut s = line.splitn(2, '='); let name = expect!(s.next()).to_owned(); let value = match expect!(s.next()) { "y" => ConfigSetting::Yes, "m" => ConfigSetting::Module,
s => ConfigSetting::Value(s.to_owned()),
};
map.insert(name, value);
}
}
Ok(KernelConfig(map))
}
}
/// The amount of time, measured in ticks, the CPU has been in specific states /// /// These fields are measured in ticks because the underlying data from the kernel is measured in ticks. /// The number of ticks per second is generally 100 on most systems. /// /// To convert this value to seconds, you can divide by the tps. There are also convenience methods /// that you can use too. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct CpuTime { /// Ticks spent in user mode pub user: u64, /// Ticks spent in user mode with low priority (nice) pub nice: u64, /// Ticks spent in system mode pub system: u64, /// Ticks spent in the idle state pub idle: u64, /// Ticks waiting for I/O to complete /// /// This value is not reliable, for the following reasons: /// /// 1. The CPU will not wait for I/O to complete; iowait is the time that a /// task is waiting for I/O to complete. When a CPU goes into idle state /// for outstanding task I/O, another task will be scheduled on this CPU. /// /// 2. On a multi-core CPU, this task waiting for I/O to complete is not running /// on any CPU, so the iowait for each CPU is difficult to calculate. /// /// 3. The value in this field may *decrease* in certain conditions. /// /// (Since Linux 2.5.41) pub iowait: Option<u64>, /// Ticks servicing interrupts /// /// (Since Linux 2.6.0) pub irq: Option<u64>, /// Ticks servicing softirqs /// /// (Since Linux 2.6.0) pub softirq: Option<u64>, /// Ticks of stolen time. /// /// Stolen time is the time spent in other operating systems when running in /// a virtualized environment. /// /// (Since Linux 2.6.11) pub steal: Option<u64>, /// Ticks spent running a virtual CPU for guest operating systems under control /// of the linux kernel /// /// (Since Linux 2.6.24) pub guest: Option<u64>, /// Ticks spent running a niced guest /// /// (Since Linux 2.6.33) pub guest_nice: Option<u64>,
// Store this field in the struct so we don't have to attempt to unwrap ticks_per_second() when we convert // from ticks into other time units let tps = ticks_per_second;
s.next(); let user = from_str!(u64, expect!(s.next())); let nice = from_str!(u64, expect!(s.next())); let system = from_str!(u64, expect!(s.next())); let idle = from_str!(u64, expect!(s.next()));
let iowait = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?; let irq = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?; let softirq = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?; let steal = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?; let guest = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?; let guest_nice = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
/// Milliseconds of stolen time pubfn steal_ms(&self) -> Option<u64> { let ms_per_tick = 1000 / self.tps; self.steal.map(|ms| ms * ms_per_tick)
}
/// Amount of stolen time pubfn steal_duration(&self) -> Option<Duration> { self.steal_ms().map(Duration::from_millis)
}
/// Milliseconds spent running a virtual CPU for guest operating systems under control of the linux kernel pubfn guest_ms(&self) -> Option<u64> { let ms_per_tick = 1000 / self.tps; self.guest.map(|ms| ms * ms_per_tick)
}
/// Time spent running a virtual CPU for guest operating systems under control of the linux kernel pubfn guest_duration(&self) -> Option<Duration> { self.guest_ms().map(Duration::from_millis)
}
/// Milliseconds spent running a niced guest pubfn guest_nice_ms(&self) -> Option<u64> { let ms_per_tick = 1000 / self.tps; self.guest_nice.map(|ms| ms * ms_per_tick)
}
/// Time spent running a niced guest pubfn guest_nice_duration(&self) -> Option<Duration> { self.guest_nice_ms().map(Duration::from_millis)
}
}
/// Kernel/system statistics, from `/proc/stat` #[derive(Debug, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct KernelStats { /// The amount of time the system spent in various states pub total: CpuTime, /// The amount of time that specific CPUs spent in various states pub cpu_time: Vec<CpuTime>,
/// The number of context switches that the system underwent pub ctxt: u64,
/// Boot time, in number of seconds since the Epoch pub btime: u64,
/// Number of forks since boot pub processes: u64,
/// Number of processes in runnable state /// /// (Since Linux 2.5.45) pub procs_running: Option<u32>,
/// Number of processes blocked waiting for I/O /// /// (Since Linux 2.5.45) pub procs_blocked: Option<u32>,
}
impl FromBufReadSI for KernelStats { fn from_buf_read<R: BufRead>(r: R, system_info: &SystemInfo) -> ProcResult<Self> { let lines = r.lines();
/// Various virtual memory statistics /// /// Since the exact set of statistics will vary from kernel to kernel, and because most of them are /// not well documented, this struct contains a HashMap instead of specific members. Consult the /// kernel source code for more details of this data. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct VmStat(pub HashMap<String, i64>);
impl FromBufRead for VmStat { fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> { letmut map = HashMap::new(); for line in r.lines() { let line = line?; letmut split = line.split_whitespace(); let name = expect!(split.next()); let val = from_str!(i64, expect!(split.next()));
map.insert(name.to_owned(), val);
}
Ok(VmStat(map))
}
}
/// Details about a loaded kernel module /// /// For an example, see the [lsmod.rs](https://github.com/eminence/procfs/tree/master/examples) /// example in the source repo. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct KernelModule { /// The name of the module pub name: String,
/// The size of the module pub size: u32,
/// The number of references in the kernel to this module. This can be -1 if the module is unloading pub refcount: i32,
/// A list of modules that depend on this module. pub used_by: Vec<String>,
/// The module state /// /// This will probably always be "Live", but it could also be either "Unloading" or "Loading" pub state: String,
}
/// A set of loaded kernel modules #[derive(Debug, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct KernelModules(pub HashMap<String, KernelModule>);
impl FromBufRead for KernelModules { /// This should correspond to the data in `/proc/modules`. fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> { // kernel reference: kernel/module.c m_show() letmut map = HashMap::new(); for line in r.lines() { let line: String = line?; letmut s = line.split_whitespace(); let name = expect!(s.next()); let size = from_str!(u32, expect!(s.next())); let refcount = from_str!(i32, expect!(s.next())); let used_by: &str = expect!(s.next()); let state = expect!(s.next());
/// A list of the arguments passed to the Linux kernel at boot time. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct KernelCmdline(pub Vec<String>);
impl FromRead for KernelCmdline { /// This should correspond to the data in `/proc/cmdline`. fn from_read<R: Read>(mut r: R) -> ProcResult<Self> { letmut buf = String::new();
r.read_to_string(&mut buf)?;
Ok(KernelCmdline(
buf.split(' ')
.filter_map(|s| if !s.is_empty() { Some(s.to_string()) } else { None })
.collect(),
))
}
}
#[cfg(test)] mod tests { usesuper::*;
#[test] fn test_kernel_from_str() { let k = KernelVersion::from_str("1.2.3").unwrap();
assert_eq!(k.major, 1);
assert_eq!(k.minor, 2);
assert_eq!(k.patch, 3);
let k = KernelVersion::from_str("4.9.16-gentoo").unwrap();
assert_eq!(k.major, 4);
assert_eq!(k.minor, 9);
assert_eq!(k.patch, 16);
let k = KernelVersion::from_str("4.9.266-0.1.ac.225.84.332.metal1.x86_64").unwrap();
assert_eq!(k.major, 4);
assert_eq!(k.minor, 9);
assert_eq!(k.patch, 266);
}
#[test] fn test_kernel_cmp() { let a = KernelVersion::from_str("1.2.3").unwrap(); let b = KernelVersion::from_str("1.2.3").unwrap(); let c = KernelVersion::from_str("1.2.4").unwrap(); let d = KernelVersion::from_str("1.5.4").unwrap(); let e = KernelVersion::from_str("2.5.4").unwrap();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.