diff --git a/procfs/Cargo.toml b/procfs/Cargo.toml index 13fd705a..d5280062 100644 --- a/procfs/Cargo.toml +++ b/procfs/Cargo.toml @@ -20,7 +20,6 @@ serde1 = ["serde", "procfs-core/serde1"] procfs-core = { path = "../procfs-core", version = "0.16.0-RC1", default-features = false } rustix = { version = "0.38.19", features = ["fs", "process", "param", "system", "thread"] } bitflags = { version = "2.0", default-features = false } -lazy_static = "1.0.2" chrono = {version = "0.4.20", optional = true, features = ["clock"], default-features = false } hex = "0.4" flate2 = { version = "1.0.3", optional = true } diff --git a/procfs/src/lib.rs b/procfs/src/lib.rs index 068b38c9..4d9baba8 100644 --- a/procfs/src/lib.rs +++ b/procfs/src/lib.rs @@ -49,7 +49,6 @@ pub use procfs_core::*; use bitflags::bitflags; -use lazy_static::lazy_static; use rustix::fd::AsFd; use std::collections::HashMap; @@ -202,28 +201,6 @@ pub use crate::sys::kernel::BuildInfo as KernelBuildInfo; pub use crate::sys::kernel::Type as KernelType; pub use crate::sys::kernel::Version as KernelVersion; -lazy_static! { - /// The number of clock ticks per second. - /// - /// This is calculated from `sysconf(_SC_CLK_TCK)`. - static ref TICKS_PER_SECOND: u64 = { - ticks_per_second() - }; - /// The version of the currently running kernel. - /// - /// This is a lazily constructed static. You can also get this information via - /// [KernelVersion::new()]. - static ref KERNEL: ProcResult = { - KernelVersion::current() - }; - /// Memory page size, in bytes. - /// - /// This is calculated from `sysconf(_SC_PAGESIZE)`. - static ref PAGESIZE: u64 = { - page_size() - }; -} - /// A wrapper around a `File` that remembers the name of the path struct FileWrapper { inner: File, @@ -524,9 +501,7 @@ mod tests { #[test] fn test_statics() { - println!("{:?}", *TICKS_PER_SECOND); - println!("{:?}", *KERNEL); - println!("{:?}", *PAGESIZE); + println!("{:?}", crate::sys::kernel::Version::cached()); } #[test] diff --git a/procfs/src/process/mod.rs b/procfs/src/process/mod.rs index 201198e7..f3ae7593 100644 --- a/procfs/src/process/mod.rs +++ b/procfs/src/process/mod.rs @@ -66,6 +66,7 @@ use super::*; use crate::net::{TcpNetEntry, UdpNetEntry}; +use crate::sys::kernel::Version; pub use procfs_core::process::*; use rustix::fd::{AsFd, BorrowedFd, OwnedFd, RawFd}; @@ -154,7 +155,7 @@ impl FDInfo { let p = path.as_ref(); let root = base.as_ref().join(p); // for 2.6.39 <= kernel < 3.6 fstat doesn't support O_PATH see https://github.com/eminence/procfs/issues/265 - let flags = match *crate::KERNEL { + let flags = match Version::cached() { Ok(v) if v < KernelVersion::new(3, 6, 0) => OFlags::NOFOLLOW | OFlags::CLOEXEC, Ok(_) => OFlags::NOFOLLOW | OFlags::PATH | OFlags::CLOEXEC, Err(_) => OFlags::NOFOLLOW | OFlags::PATH | OFlags::CLOEXEC, @@ -224,7 +225,7 @@ impl Process { /// Returns a `Process` based on a specified `/proc/` path. pub fn new_with_root(root: PathBuf) -> ProcResult { // for 2.6.39 <= kernel < 3.6 fstat doesn't support O_PATH see https://github.com/eminence/procfs/issues/265 - let flags = match *crate::KERNEL { + let flags = match Version::cached() { Ok(v) if v < KernelVersion::new(3, 6, 0) => OFlags::DIRECTORY | OFlags::CLOEXEC, Ok(_) => OFlags::PATH | OFlags::DIRECTORY | OFlags::CLOEXEC, Err(_) => OFlags::PATH | OFlags::DIRECTORY | OFlags::CLOEXEC, diff --git a/procfs/src/sys/kernel/mod.rs b/procfs/src/sys/kernel/mod.rs index 7e1465a9..e069d5e6 100644 --- a/procfs/src/sys/kernel/mod.rs +++ b/procfs/src/sys/kernel/mod.rs @@ -6,10 +6,11 @@ use std::cmp; use std::collections::HashSet; use std::str::FromStr; +use std::sync::atomic::{AtomicU32, Ordering}; use bitflags::bitflags; -use crate::{read_value, write_value, ProcError, ProcResult, KERNEL}; +use crate::{read_value, write_value, ProcError, ProcResult}; pub mod keys; pub mod random; @@ -34,6 +35,41 @@ impl Version { read_value("/proc/sys/kernel/osrelease") } + /// Cached version of the kernel version. + pub(crate) fn cached() -> ProcResult { + const SENTINEL: u32 = 0; + static KERNEL: AtomicU32 = AtomicU32::new(SENTINEL); + + // Try to load the kernel version. + let mut kernel = KERNEL.load(Ordering::Relaxed); + + // If we haven't loaded the kernel version yet, try to here. + if kernel == SENTINEL { + kernel = Self::current()?.to_u32(); + + // Try to store it in the cache. + KERNEL.store(kernel, Ordering::Release); + } + + Ok(Self::from_u32(kernel)) + } + + /// Convert kernel version to an arbitrary `u32` value. + fn to_u32(self) -> u32 { + let [lo, hi] = u16::to_ne_bytes(self.patch); + u32::from_ne_bytes([self.major, self.minor, lo, hi]) + } + + /// Convert kernel version from an arbitrary `u32` value. + fn from_u32(val: u32) -> Self { + let [major, minor, lo, hi] = u32::to_ne_bytes(val); + Self { + major, + minor, + patch: u16::from_ne_bytes([lo, hi]) + } + } + /// Parses a kernel version string, in major.minor.release syntax. /// /// Note that any extra information (stuff after a dash) is ignored. @@ -447,7 +483,7 @@ pub fn threads_max() -> ProcResult { /// Since Linux 4.1, this value is bounded, and must be in the range [THREADS_MIN]..=[THREADS_MAX]. /// This function will return an error if that is not the case. pub fn set_threads_max(new_limit: u32) -> ProcResult<()> { - if let Ok(kernel) = *KERNEL { + if let Ok(kernel) = Version::cached() { if kernel.major >= 4 && kernel.minor >= 1 && !(THREADS_MIN..=THREADS_MAX).contains(&new_limit) { return Err(ProcError::Other(format!( "{} is outside the THREADS_MIN..=THREADS_MAX range", diff --git a/procfs/src/sys/kernel/random.rs b/procfs/src/sys/kernel/random.rs index aade14e6..5c33a382 100644 --- a/procfs/src/sys/kernel/random.rs +++ b/procfs/src/sys/kernel/random.rs @@ -3,16 +3,14 @@ //! Note that some of these entries are only documented in random(4), while some are also documented under proc(5) use crate::{read_value, write_value, ProcError, ProcResult}; -use lazy_static::lazy_static; +use std::path::Path; -lazy_static! { - static ref RANDOM_ROOT: std::path::PathBuf = std::path::PathBuf::from("/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 pub fn entropy_avail() -> ProcResult { - read_value(RANDOM_ROOT.join("entropy_avail")) + read_value(Path::new(RANDOM_ROOT).join("entropy_avail")) } /// This file gives the size of the entropy pool @@ -22,7 +20,7 @@ pub fn entropy_avail() -> ProcResult { /// /// See `man random(4)` for more information pub fn poolsize() -> ProcResult { - read_value(RANDOM_ROOT.join("poolsize")) + read_value(Path::new(RANDOM_ROOT).join("poolsize")) } /// This file contains the number of bits of entropy required for waking up processes that sleep waiting @@ -33,10 +31,10 @@ pub fn poolsize() -> ProcResult { /// This will first attempt to read from `/proc/sys/kernel/random/read_wakeup_threshold` but it /// will fallback to `/proc/sys/kernel/random/write_wakeup_threshold` if the former file is not found. pub fn read_wakeup_threshold() -> ProcResult { - match read_value(RANDOM_ROOT.join("read_wakeup_threshold")) { + match read_value(Path::new(RANDOM_ROOT).join("read_wakeup_threshold")) { Ok(val) => Ok(val), Err(err) => match err { - ProcError::NotFound(_) => read_value(RANDOM_ROOT.join("write_wakeup_threshold")), + ProcError::NotFound(_) => read_value(Path::new(RANDOM_ROOT).join("write_wakeup_threshold")), err => Err(err), }, } @@ -45,17 +43,17 @@ pub fn read_wakeup_threshold() -> ProcResult { /// This file contains the number of bits of entropy below which we wake up processes that do a /// select(2) or poll(2) for write access to /dev/random. These values can be changed by writing to the file. pub fn write_wakeup_threshold(new_value: u32) -> ProcResult<()> { - write_value(RANDOM_ROOT.join("write_wakeup_threshold"), new_value) + write_value(Path::new(RANDOM_ROOT).join("write_wakeup_threshold"), new_value) } /// This read-only file randomly generates a fresh 128-bit UUID on each read pub fn uuid() -> ProcResult { - read_value(RANDOM_ROOT.join("uuid")) + read_value(Path::new(RANDOM_ROOT).join("uuid")) } /// This is a read-only file containing a 128-bit UUID generated at boot pub fn boot_id() -> ProcResult { - read_value(RANDOM_ROOT.join("boot_id")) + read_value(Path::new(RANDOM_ROOT).join("boot_id")) } #[cfg(test)]