procfs_core/sys/kernel/
mod.rs

1//! Global kernel info / tuning miscellaneous stuff
2//!
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.
5
6use std::cmp;
7use std::collections::HashSet;
8use std::str::FromStr;
9
10use bitflags::bitflags;
11
12use crate::{ProcError, ProcResult};
13
14/// Represents a kernel version, in major.minor.release version.
15#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
16pub struct Version {
17    pub major: u8,
18    pub minor: u8,
19    pub patch: u16,
20}
21
22impl Version {
23    pub fn new(major: u8, minor: u8, patch: u16) -> Version {
24        Version { major, minor, patch }
25    }
26
27    /// Parses a kernel version string, in major.minor.release syntax.
28    ///
29    /// Note that any extra information (stuff after a dash) is ignored.
30    ///
31    /// # Example
32    ///
33    /// ```
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);
37    /// assert_eq!(a, b);
38    ///
39    /// ```
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);
45            s
46        } else {
47            s
48        };
49        let mut kernel_split = kernel.split('.');
50
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")?;
54
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")?;
58
59        Ok(Version { major, minor, patch })
60    }
61}
62
63impl FromStr for Version {
64    type Err = &'static str;
65
66    /// Parses a kernel version string, in major.minor.release syntax.
67    ///
68    /// Note that any extra information (stuff after a dash) is ignored.
69    ///
70    /// # Example
71    ///
72    /// ```
73    /// # use procfs_core::KernelVersion;
74    /// let a: KernelVersion = "3.16.0-6-amd64".parse().unwrap();
75    /// let b = KernelVersion::new(3, 16, 0);
76    /// assert_eq!(a, b);
77    ///
78    /// ```
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        Version::from_str(s)
81    }
82}
83
84impl 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),
89                x => x,
90            },
91            x => x,
92        }
93    }
94}
95
96impl cmp::PartialOrd for Version {
97    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
98        Some(self.cmp(other))
99    }
100}
101
102/// Represents a kernel type
103#[derive(Debug, Clone, Eq, PartialEq)]
104pub struct Type {
105    pub sysname: String,
106}
107
108impl Type {
109    pub fn new(sysname: String) -> Type {
110        Type { sysname }
111    }
112}
113
114impl FromStr for Type {
115    type Err = &'static str;
116
117    /// Parse a kernel type string
118    ///
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()))
122    }
123}
124
125/// Represents a kernel build information
126#[derive(Debug, Clone, Eq, PartialEq)]
127pub struct BuildInfo {
128    pub version: String,
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.
131    ///
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.
133    pub extra: String,
134}
135
136impl BuildInfo {
137    pub fn new(version: &str, flags: HashSet<String>, extra: String) -> BuildInfo {
138        BuildInfo {
139            version: version.to_string(),
140            flags,
141            extra,
142        }
143    }
144
145    /// Check if SMP is ON
146    pub fn smp(&self) -> bool {
147        self.flags.contains("SMP")
148    }
149
150    /// Check if PREEMPT is ON
151    pub fn preempt(&self) -> bool {
152        self.flags.contains("PREEMPT")
153    }
154
155    /// Check if PREEMPTRT is ON
156    pub fn preemptrt(&self) -> bool {
157        self.flags.contains("PREEMPTRT")
158    }
159
160    /// Return version number
161    ///
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() {
167                version_str.push(c);
168            } else {
169                break;
170            }
171        }
172        let version_number: u32 = version_str.parse().map_err(|_| "Failed to parse version number")?;
173        Ok(version_number)
174    }
175
176    /// Parse extra field to `DateTime` object
177    ///
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>> {
181        if let Ok(dt) =
182            chrono::DateTime::parse_from_str(&format!("{} +0000", &self.extra), "%a %b %d %H:%M:%S UTC %Y %z")
183        {
184            return Ok(dt.with_timezone(&chrono::Local));
185        }
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));
188        }
189        Err(ProcError::Other("Failed to parse extra field to date".to_string()))
190    }
191}
192
193impl FromStr for BuildInfo {
194    type Err = &'static str;
195
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();
201
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);
207            } else {
208                return Err("Failed to parse kernel build version");
209            }
210        } else {
211            return Err("Failed to parse kernel build version");
212        }
213
214        for s in &mut splited {
215            if s.chars().all(char::is_uppercase) {
216                flags.insert(s.to_string());
217            } else {
218                extra.push_str(s);
219                extra.push(' ');
220                break;
221            }
222        }
223        let remains: Vec<&str> = splited.collect();
224        extra.push_str(&remains.join(" "));
225
226        Ok(BuildInfo { version, flags, extra })
227    }
228}
229
230#[derive(Debug, PartialEq, Eq, Copy, Clone)]
231/// Represents the data from `/proc/sys/kernel/sem`
232pub struct SemaphoreLimits {
233    /// The maximum semaphores per semaphore set
234    pub semmsl: u64,
235    /// A system-wide limit on the number of semaphores in all semaphore sets
236    pub semmns: u64,
237    /// The maximum number of operations that may be specified in a semop(2) call
238    pub semopm: u64,
239    /// A system-wide limit on the maximum number of semaphore identifiers
240    pub semmni: u64,
241}
242
243impl SemaphoreLimits {
244    fn from_str(s: &str) -> Result<Self, &'static str> {
245        let mut s = s.split_ascii_whitespace();
246
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")?;
251
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")?;
256
257        Ok(SemaphoreLimits {
258            semmsl,
259            semmns,
260            semopm,
261            semmni,
262        })
263    }
264}
265
266impl FromStr for SemaphoreLimits {
267    type Err = &'static str;
268
269    fn from_str(s: &str) -> Result<Self, Self::Err> {
270        SemaphoreLimits::from_str(s)
271    }
272}
273
274bitflags! {
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;
294    }
295}
296
297/// Values controlling functions allowed to be invoked by the SysRq key
298#[derive(Copy, Clone, Debug)]
299pub enum SysRq {
300    /// Disable sysrq completely
301    Disable,
302    /// Enable all functions of sysrq
303    Enable,
304    /// Bitmask of allowed sysrq functions
305    AllowedFunctions(AllowedFunctions),
306}
307
308impl SysRq {
309    pub fn to_number(self) -> u16 {
310        match self {
311            SysRq::Disable => 0,
312            SysRq::Enable => 1,
313            SysRq::AllowedFunctions(allowed) => allowed.bits(),
314        }
315    }
316
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()),
324            },
325        }
326    }
327}
328
329impl FromStr for SysRq {
330    type Err = ProcError;
331
332    fn from_str(s: &str) -> Result<Self, Self::Err> {
333        SysRq::from_str(s)
334    }
335}
336
337/// The minimum value that can be written to `/proc/sys/kernel/threads-max` on Linux 4.1 or later
338pub 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
340pub const THREADS_MAX: u32 = 0x3fff_ffff;
341
342#[cfg(test)]
343mod tests {
344    use super::*;
345
346    #[test]
347    fn test_version() {
348        let a = Version::from_str("3.16.0-6-amd64").unwrap();
349        let b = Version::new(3, 16, 0);
350        assert_eq!(a, b);
351
352        let a = Version::from_str("3.16.0").unwrap();
353        let b = Version::new(3, 16, 0);
354        assert_eq!(a, b);
355
356        let a = Version::from_str("3.16.0_1").unwrap();
357        let b = Version::new(3, 16, 0);
358        assert_eq!(a, b);
359    }
360
361    #[test]
362    fn test_type() {
363        let a = Type::from_str("Linux").unwrap();
364        assert_eq!(a.sysname, "Linux");
365    }
366
367    #[test]
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);
377        assert!(a.smp());
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();
383
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");
390        assert!(b.smp());
391        assert!(b.preempt());
392        assert!(!b.preemptrt());
393        #[cfg(feature = "chrono")]
394        let _ = b.extra_date().unwrap();
395
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)");
404        assert!(c.smp());
405        assert!(!c.preempt());
406        assert!(!c.preemptrt());
407        // Skip the date parsing for now
408    }
409
410    #[test]
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 {
415            semmsl: 32_000,
416            semmns: 1_024_000_000,
417            semopm: 500,
418            semmni: 32_000,
419        };
420        assert_eq!(a, b);
421
422        let a = SemaphoreLimits::from_str("1");
423        assert!(a.is_err() && a.err().unwrap() == "Missing SEMMNS");
424
425        let a = SemaphoreLimits::from_str("1 string 500 3200");
426        assert!(a.is_err() && a.err().unwrap() == "Failed to parse SEMMNS");
427    }
428}