Bug 1689358 - Generate minidumps for child process crashes using the minidump-writer...
[gecko.git] / third_party / rust / procfs-core / src / sys / kernel / mod.rs
blob7d1b2493556ddbdf4fa47d4c93ef363f450e83a8
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.
6 use std::cmp;
7 use std::collections::HashSet;
8 use std::str::FromStr;
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)]
16 pub struct Version {
17     pub major: u8,
18     pub minor: u8,
19     pub patch: u16,
22 impl Version {
23     pub fn new(major: u8, minor: u8, patch: u16) -> Version {
24         Version { major, minor, patch }
25     }
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('.');
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 })
60     }
63 impl FromStr for Version {
64     type Err = &'static str;
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     }
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),
89                 x => x,
90             },
91             x => x,
92         }
93     }
96 impl cmp::PartialOrd for Version {
97     fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
98         Some(self.cmp(other))
99     }
102 /// Represents a kernel type
103 #[derive(Debug, Clone, Eq, PartialEq)]
104 pub struct Type {
105     pub sysname: String,
108 impl Type {
109     pub fn new(sysname: String) -> Type {
110         Type { sysname }
111     }
114 impl FromStr for Type {
115     type Err = &'static str;
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     }
125 /// Represents a kernel build information
126 #[derive(Debug, Clone, Eq, PartialEq)]
127 pub 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,
136 impl 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     }
145     /// Check if SMP is ON
146     pub fn smp(&self) -> bool {
147         self.flags.contains("SMP")
148     }
150     /// Check if PREEMPT is ON
151     pub fn preempt(&self) -> bool {
152         self.flags.contains("PREEMPT")
153     }
155     /// Check if PREEMPTRT is ON
156     pub fn preemptrt(&self) -> bool {
157         self.flags.contains("PREEMPTRT")
158     }
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     }
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     }
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);
207             } else {
208                 return Err("Failed to parse kernel build version");
209             }
210         } else {
211             return Err("Failed to parse kernel build version");
212         }
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(" "));
226         Ok(BuildInfo { version, flags, extra })
227     }
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
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,
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")?;
257         Ok(SemaphoreLimits {
258             semmsl,
259             semmns,
260             semopm,
261             semmni,
262         })
263     }
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)
271     }
274 bitflags! {
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     }
297 /// Values controlling functions allowed to be invoked by the SysRq key
298 #[derive(Copy, Clone, Debug)]
299 pub 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),
308 impl 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     }
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     }
329 impl FromStr for SysRq {
330     type Err = ProcError;
332     fn from_str(s: &str) -> Result<Self, Self::Err> {
333         SysRq::from_str(s)
334     }
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;
342 #[cfg(test)]
343 mod tests {
344     use super::*;
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);
352         let a = Version::from_str("3.16.0").unwrap();
353         let b = Version::new(3, 16, 0);
354         assert_eq!(a, b);
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     }
361     #[test]
362     fn test_type() {
363         let a = Type::from_str("Linux").unwrap();
364         assert_eq!(a.sysname, "Linux");
365     }
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();
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();
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     }
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);
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");
427     }