1 //! Global kernel info / tuning miscellaneous stuff
3 //! The files in this directory can be used to tune and monitor miscellaneous
4 //! and general things in the operation of the Linux kernel.
7 use std::collections::HashSet;
10 use bitflags::bitflags;
12 use crate::{ProcError, ProcResult};
14 /// Represents a kernel version, in major.minor.release version.
15 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
23 pub fn new(major: u8, minor: u8, patch: u16) -> Version {
24 Version { major, minor, patch }
27 /// Parses a kernel version string, in major.minor.release syntax.
29 /// Note that any extra information (stuff after a dash) is ignored.
34 /// # use procfs_core::KernelVersion;
35 /// let a = KernelVersion::from_str("3.16.0-6-amd64").unwrap();
36 /// let b = KernelVersion::new(3, 16, 0);
40 #[allow(clippy::should_implement_trait)]
41 pub fn from_str(s: &str) -> Result<Self, &'static str> {
42 let pos = s.find(|c: char| c != '.' && !c.is_ascii_digit());
43 let kernel = if let Some(pos) = pos {
44 let (s, _) = s.split_at(pos);
49 let mut kernel_split = kernel.split('.');
51 let major = kernel_split.next().ok_or("Missing major version component")?;
52 let minor = kernel_split.next().ok_or("Missing minor version component")?;
53 let patch = kernel_split.next().ok_or("Missing patch version component")?;
55 let major = major.parse().map_err(|_| "Failed to parse major version")?;
56 let minor = minor.parse().map_err(|_| "Failed to parse minor version")?;
57 let patch = patch.parse().map_err(|_| "Failed to parse patch version")?;
59 Ok(Version { major, minor, patch })
63 impl FromStr for Version {
64 type Err = &'static str;
66 /// Parses a kernel version string, in major.minor.release syntax.
68 /// Note that any extra information (stuff after a dash) is ignored.
73 /// # use procfs_core::KernelVersion;
74 /// let a: KernelVersion = "3.16.0-6-amd64".parse().unwrap();
75 /// let b = KernelVersion::new(3, 16, 0);
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
84 impl cmp::Ord for Version {
85 fn cmp(&self, other: &Self) -> cmp::Ordering {
86 match self.major.cmp(&other.major) {
87 cmp::Ordering::Equal => match self.minor.cmp(&other.minor) {
88 cmp::Ordering::Equal => self.patch.cmp(&other.patch),
96 impl cmp::PartialOrd for Version {
97 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
102 /// Represents a kernel type
103 #[derive(Debug, Clone, Eq, PartialEq)]
109 pub fn new(sysname: String) -> Type {
114 impl FromStr for Type {
115 type Err = &'static str;
117 /// Parse a kernel type string
119 /// Notice that in Linux source code, it is defined as a single string.
120 fn from_str(s: &str) -> Result<Self, Self::Err> {
121 Ok(Type::new(s.to_string()))
125 /// Represents a kernel build information
126 #[derive(Debug, Clone, Eq, PartialEq)]
127 pub struct BuildInfo {
129 pub flags: HashSet<String>,
130 /// This field contains any extra data from the /proc/sys/kernel/version file. It generally contains the build date of the kernel, but the format of the date can vary.
132 /// A method named `extra_date` is provided which would try to parse some date formats. When the date format is not supported, an error will be returned. It depends on chrono feature.
137 pub fn new(version: &str, flags: HashSet<String>, extra: String) -> BuildInfo {
139 version: version.to_string(),
145 /// Check if SMP is ON
146 pub fn smp(&self) -> bool {
147 self.flags.contains("SMP")
150 /// Check if PREEMPT is ON
151 pub fn preempt(&self) -> bool {
152 self.flags.contains("PREEMPT")
155 /// Check if PREEMPTRT is ON
156 pub fn preemptrt(&self) -> bool {
157 self.flags.contains("PREEMPTRT")
160 /// Return version number
162 /// This would parse number from first digits of version string. For example, #21~1 to 21.
163 pub fn version_number(&self) -> ProcResult<u32> {
164 let mut version_str = String::new();
165 for c in self.version.chars() {
166 if c.is_ascii_digit() {
172 let version_number: u32 = version_str.parse().map_err(|_| "Failed to parse version number")?;
176 /// Parse extra field to `DateTime` object
178 /// This function may fail as TIMESTAMP can be various formats.
179 #[cfg(feature = "chrono")]
180 pub fn extra_date(&self) -> ProcResult<chrono::DateTime<chrono::Local>> {
182 chrono::DateTime::parse_from_str(&format!("{} +0000", &self.extra), "%a %b %d %H:%M:%S UTC %Y %z")
184 return Ok(dt.with_timezone(&chrono::Local));
186 if let Ok(dt) = chrono::DateTime::parse_from_str(&self.extra, "%a, %d %b %Y %H:%M:%S %z") {
187 return Ok(dt.with_timezone(&chrono::Local));
189 Err(ProcError::Other("Failed to parse extra field to date".to_string()))
193 impl FromStr for BuildInfo {
194 type Err = &'static str;
196 /// Parse a kernel build information string
197 fn from_str(s: &str) -> Result<Self, Self::Err> {
198 let mut version = String::new();
199 let mut flags: HashSet<String> = HashSet::new();
200 let mut extra: String = String::new();
202 let mut splited = s.split(' ');
203 let version_str = splited.next();
204 if let Some(version_str) = version_str {
205 if let Some(stripped) = version_str.strip_prefix('#') {
206 version.push_str(stripped);
208 return Err("Failed to parse kernel build version");
211 return Err("Failed to parse kernel build version");
214 for s in &mut splited {
215 if s.chars().all(char::is_uppercase) {
216 flags.insert(s.to_string());
223 let remains: Vec<&str> = splited.collect();
224 extra.push_str(&remains.join(" "));
226 Ok(BuildInfo { version, flags, extra })
230 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
231 /// Represents the data from `/proc/sys/kernel/sem`
232 pub struct SemaphoreLimits {
233 /// The maximum semaphores per semaphore set
235 /// A system-wide limit on the number of semaphores in all semaphore sets
237 /// The maximum number of operations that may be specified in a semop(2) call
239 /// A system-wide limit on the maximum number of semaphore identifiers
243 impl SemaphoreLimits {
244 fn from_str(s: &str) -> Result<Self, &'static str> {
245 let mut s = s.split_ascii_whitespace();
247 let semmsl = s.next().ok_or("Missing SEMMSL")?;
248 let semmns = s.next().ok_or("Missing SEMMNS")?;
249 let semopm = s.next().ok_or("Missing SEMOPM")?;
250 let semmni = s.next().ok_or("Missing SEMMNI")?;
252 let semmsl = semmsl.parse().map_err(|_| "Failed to parse SEMMSL")?;
253 let semmns = semmns.parse().map_err(|_| "Failed to parse SEMMNS")?;
254 let semopm = semopm.parse().map_err(|_| "Failed to parse SEMOPM")?;
255 let semmni = semmni.parse().map_err(|_| "Failed to parse SEMMNI")?;
266 impl FromStr for SemaphoreLimits {
267 type Err = &'static str;
269 fn from_str(s: &str) -> Result<Self, Self::Err> {
270 SemaphoreLimits::from_str(s)
275 /// Flags representing allowed sysrq functions
276 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
277 pub struct AllowedFunctions : u16 {
278 /// Enable control of console log level
279 const ENABLE_CONTROL_LOG_LEVEL = 2;
280 /// Enable control of keyboard (SAK, unraw)
281 const ENABLE_CONTROL_KEYBOARD = 4;
282 /// Enable debugging dumps of processes etc
283 const ENABLE_DEBUGGING_DUMPS = 8;
284 /// Enable sync command
285 const ENABLE_SYNC_COMMAND = 16;
286 /// Enable remound read-only
287 const ENABLE_REMOUNT_READ_ONLY = 32;
288 /// Enable signaling of processes (term, kill, oom-kill)
289 const ENABLE_SIGNALING_PROCESSES = 64;
290 /// Allow reboot/poweroff
291 const ALLOW_REBOOT_POWEROFF = 128;
292 /// Allow nicing of all real-time tasks
293 const ALLOW_NICING_REAL_TIME_TASKS = 256;
297 /// Values controlling functions allowed to be invoked by the SysRq key
298 #[derive(Copy, Clone, Debug)]
300 /// Disable sysrq completely
302 /// Enable all functions of sysrq
304 /// Bitmask of allowed sysrq functions
305 AllowedFunctions(AllowedFunctions),
309 pub fn to_number(self) -> u16 {
313 SysRq::AllowedFunctions(allowed) => allowed.bits(),
317 fn from_str(s: &str) -> ProcResult<Self> {
318 match s.parse::<u16>()? {
319 0 => Ok(SysRq::Disable),
320 1 => Ok(SysRq::Enable),
321 x => match AllowedFunctions::from_bits(x) {
322 Some(allowed) => Ok(SysRq::AllowedFunctions(allowed)),
323 None => Err("Invalid value".into()),
329 impl FromStr for SysRq {
330 type Err = ProcError;
332 fn from_str(s: &str) -> Result<Self, Self::Err> {
337 /// The minimum value that can be written to `/proc/sys/kernel/threads-max` on Linux 4.1 or later
338 pub const THREADS_MIN: u32 = 20;
339 /// The maximum value that can be written to `/proc/sys/kernel/threads-max` on Linux 4.1 or later
340 pub const THREADS_MAX: u32 = 0x3fff_ffff;
348 let a = Version::from_str("3.16.0-6-amd64").unwrap();
349 let b = Version::new(3, 16, 0);
352 let a = Version::from_str("3.16.0").unwrap();
353 let b = Version::new(3, 16, 0);
356 let a = Version::from_str("3.16.0_1").unwrap();
357 let b = Version::new(3, 16, 0);
363 let a = Type::from_str("Linux").unwrap();
364 assert_eq!(a.sysname, "Linux");
368 fn test_build_info() {
369 // For Ubuntu, Manjaro, CentOS and others:
370 let a = BuildInfo::from_str("#1 SMP PREEMPT Thu Sep 30 15:29:01 UTC 2021").unwrap();
371 let mut flags: HashSet<String> = HashSet::new();
372 flags.insert("SMP".to_string());
373 flags.insert("PREEMPT".to_string());
374 assert_eq!(a.version, "1");
375 assert_eq!(a.version_number().unwrap(), 1);
376 assert_eq!(a.flags, flags);
378 assert!(a.preempt());
379 assert!(!a.preemptrt());
380 assert_eq!(a.extra, "Thu Sep 30 15:29:01 UTC 2021");
381 #[cfg(feature = "chrono")]
382 let _ = a.extra_date().unwrap();
384 // For Arch and others:
385 let b = BuildInfo::from_str("#1 SMP PREEMPT Fri, 12 Nov 2021 19:22:10 +0000").unwrap();
386 assert_eq!(b.version, "1");
387 assert_eq!(b.version_number().unwrap(), 1);
388 assert_eq!(b.flags, flags);
389 assert_eq!(b.extra, "Fri, 12 Nov 2021 19:22:10 +0000");
391 assert!(b.preempt());
392 assert!(!b.preemptrt());
393 #[cfg(feature = "chrono")]
394 let _ = b.extra_date().unwrap();
396 // For Debian and others:
397 let c = BuildInfo::from_str("#1 SMP Debian 5.10.46-4 (2021-08-03)").unwrap();
398 let mut flags: HashSet<String> = HashSet::new();
399 flags.insert("SMP".to_string());
400 assert_eq!(c.version, "1");
401 assert_eq!(c.version_number().unwrap(), 1);
402 assert_eq!(c.flags, flags);
403 assert_eq!(c.extra, "Debian 5.10.46-4 (2021-08-03)");
405 assert!(!c.preempt());
406 assert!(!c.preemptrt());
407 // Skip the date parsing for now
411 fn test_semaphore_limits() {
412 // Note that the below string has tab characters in it. Make sure to not remove them.
413 let a = SemaphoreLimits::from_str("32000 1024000000 500 32000").unwrap();
414 let b = SemaphoreLimits {
416 semmns: 1_024_000_000,
422 let a = SemaphoreLimits::from_str("1");
423 assert!(a.is_err() && a.err().unwrap() == "Missing SEMMNS");
425 let a = SemaphoreLimits::from_str("1 string 500 3200");
426 assert!(a.is_err() && a.err().unwrap() == "Failed to parse SEMMNS");