1#![allow(unknown_lints)]
2#![deny(rustdoc::broken_intra_doc_links, rustdoc::invalid_html_tags)]
3use bitflags::bitflags;
15
16use std::fmt;
17use std::io::{BufRead, BufReader, Read};
18use std::path::{Path, PathBuf};
19use std::str::FromStr;
20use std::{collections::HashMap, time::Duration};
21
22#[cfg(feature = "serde1")]
23use serde::{Deserialize, Serialize};
24
25pub trait FromRead: Sized {
27    fn from_read<R: Read>(r: R) -> ProcResult<Self>;
29
30    fn from_file<P: AsRef<Path>>(path: P) -> ProcResult<Self> {
32        std::fs::File::open(path.as_ref())
33            .map_err(|e| e.into())
34            .and_then(|f| Self::from_read(f))
35            .map_err(|e| e.error_path(path.as_ref()))
36    }
37}
38
39pub trait FromBufRead: Sized {
41    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self>;
42}
43
44impl<T: FromBufRead> FromRead for T {
45    fn from_read<R: Read>(r: R) -> ProcResult<Self> {
46        Self::from_buf_read(BufReader::new(r))
47    }
48}
49
50pub trait FromReadSI: Sized {
52    fn from_read<R: Read>(r: R, system_info: &SystemInfo) -> ProcResult<Self>;
54
55    fn from_file<P: AsRef<Path>>(path: P, system_info: &SystemInfo) -> ProcResult<Self> {
57        std::fs::File::open(path.as_ref())
58            .map_err(|e| e.into())
59            .and_then(|f| Self::from_read(f, system_info))
60            .map_err(|e| e.error_path(path.as_ref()))
61    }
62}
63
64pub trait FromBufReadSI: Sized {
66    fn from_buf_read<R: BufRead>(r: R, system_info: &SystemInfo) -> ProcResult<Self>;
67}
68
69impl<T: FromBufReadSI> FromReadSI for T {
70    fn from_read<R: Read>(r: R, system_info: &SystemInfo) -> ProcResult<Self> {
71        Self::from_buf_read(BufReader::new(r), system_info)
72    }
73}
74
75pub mod prelude {
77    pub use super::{FromBufRead, FromBufReadSI, FromRead, FromReadSI};
78}
79
80#[doc(hidden)]
81pub trait IntoOption<T> {
82    fn into_option(t: Self) -> Option<T>;
83}
84
85impl<T> IntoOption<T> for Option<T> {
86    fn into_option(t: Option<T>) -> Option<T> {
87        t
88    }
89}
90
91impl<T, R> IntoOption<T> for Result<T, R> {
92    fn into_option(t: Result<T, R>) -> Option<T> {
93        t.ok()
94    }
95}
96
97#[doc(hidden)]
98pub trait IntoResult<T, E> {
99    fn into(t: Self) -> Result<T, E>;
100}
101
102#[macro_export]
103#[doc(hidden)]
104macro_rules! build_internal_error {
105    ($err: expr) => {
106        $crate::ProcError::InternalError($crate::InternalError {
107            msg: format!("Internal Unwrap Error: {}", $err),
108            file: file!(),
109            line: line!(),
110            #[cfg(feature = "backtrace")]
111            backtrace: backtrace::Backtrace::new(),
112        })
113    };
114    ($err: expr, $msg: expr) => {
115        $crate::ProcError::InternalError($crate::InternalError {
116            msg: format!("Internal Unwrap Error: {}: {}", $msg, $err),
117            file: file!(),
118            line: line!(),
119            #[cfg(feature = "backtrace")]
120            backtrace: backtrace::Backtrace::new(),
121        })
122    };
123}
124
125#[doc(hidden)]
128pub struct NoneError;
129
130impl std::fmt::Display for NoneError {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        write!(f, "NoneError")
133    }
134}
135
136impl<T> IntoResult<T, NoneError> for Option<T> {
137    fn into(t: Option<T>) -> Result<T, NoneError> {
138        t.ok_or(NoneError)
139    }
140}
141
142impl<T, E> IntoResult<T, E> for Result<T, E> {
143    fn into(t: Result<T, E>) -> Result<T, E> {
144        t
145    }
146}
147
148#[allow(unused_macros)]
149#[macro_export]
150#[doc(hidden)]
151macro_rules! proc_panic {
152    ($e:expr) => {
153        $crate::IntoOption::into_option($e).unwrap_or_else(|| {
154            panic!(
155                "Failed to unwrap {}. Please report this as a procfs bug.",
156                stringify!($e)
157            )
158        })
159    };
160    ($e:expr, $msg:expr) => {
161        $crate::IntoOption::into_option($e).unwrap_or_else(|| {
162            panic!(
163                "Failed to unwrap {} ({}). Please report this as a procfs bug.",
164                stringify!($e),
165                $msg
166            )
167        })
168    };
169}
170
171#[macro_export]
172#[doc(hidden)]
173macro_rules! expect {
174    ($e:expr) => {
175        match $crate::IntoResult::into($e) {
176            Ok(v) => v,
177            Err(e) => return Err($crate::build_internal_error!(e)),
178        }
179    };
180    ($e:expr, $msg:expr) => {
181        match $crate::IntoResult::into($e) {
182            Ok(v) => v,
183            Err(e) => return Err($crate::build_internal_error!(e, $msg)),
184        }
185    };
186}
187
188#[macro_export]
189#[doc(hidden)]
190macro_rules! from_str {
191    ($t:tt, $e:expr) => {{
192        let e = $e;
193        $crate::expect!(
194            $t::from_str_radix(e, 10),
195            format!("Failed to parse {} ({:?}) as a {}", stringify!($e), e, stringify!($t),)
196        )
197    }};
198    ($t:tt, $e:expr, $radix:expr) => {{
199        let e = $e;
200        $crate::expect!(
201            $t::from_str_radix(e, $radix),
202            format!("Failed to parse {} ({:?}) as a {}", stringify!($e), e, stringify!($t))
203        )
204    }};
205    ($t:tt, $e:expr, $radix:expr, pid:$pid:expr) => {{
206        let e = $e;
207        $crate::expect!(
208            $t::from_str_radix(e, $radix),
209            format!(
210                "Failed to parse {} ({:?}) as a {} (pid {})",
211                stringify!($e),
212                e,
213                stringify!($t),
214                $pid
215            )
216        )
217    }};
218}
219
220pub trait SystemInfoInterface {
253    fn boot_time_secs(&self) -> ProcResult<u64>;
254    fn ticks_per_second(&self) -> u64;
255    fn page_size(&self) -> u64;
256    fn is_little_endian(&self) -> bool;
258
259    #[cfg(feature = "chrono")]
260    fn boot_time(&self) -> ProcResult<chrono::DateTime<chrono::Local>> {
261        use chrono::TimeZone;
262        let date_time = expect!(chrono::Local.timestamp_opt(self.boot_time_secs()? as i64, 0).single());
263        Ok(date_time)
264    }
265}
266
267pub type SystemInfo = dyn SystemInfoInterface;
269
270#[derive(Debug, Clone, Copy)]
272#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
273pub struct ExplicitSystemInfo {
274    pub boot_time_secs: u64,
275    pub ticks_per_second: u64,
276    pub page_size: u64,
277    pub is_little_endian: bool,
278}
279
280impl SystemInfoInterface for ExplicitSystemInfo {
281    fn boot_time_secs(&self) -> ProcResult<u64> {
282        Ok(self.boot_time_secs)
283    }
284
285    fn ticks_per_second(&self) -> u64 {
286        self.ticks_per_second
287    }
288
289    fn page_size(&self) -> u64 {
290        self.page_size
291    }
292
293    fn is_little_endian(&self) -> bool {
294        self.is_little_endian
295    }
296}
297
298pub trait WithSystemInfo<'a>: 'a {
300    type Output: 'a;
301
302    fn with_system_info(self, info: &SystemInfo) -> Self::Output;
304}
305
306impl<'a, F: 'a, R: 'a> WithSystemInfo<'a> for F
307where
308    F: FnOnce(&SystemInfo) -> R,
309{
310    type Output = R;
311
312    fn with_system_info(self, info: &SystemInfo) -> Self::Output {
313        self(info)
314    }
315}
316
317#[doc(hidden)]
318pub fn from_iter<'a, I, U>(i: I) -> ProcResult<U>
319where
320    I: IntoIterator<Item = &'a str>,
321    U: FromStr,
322{
323    let mut iter = i.into_iter();
324    let val = expect!(iter.next());
325    match FromStr::from_str(val) {
326        Ok(u) => Ok(u),
327        Err(..) => Err(build_internal_error!("Failed to convert", val)),
328    }
329}
330
331fn from_iter_optional<'a, I, U>(i: I) -> ProcResult<Option<U>>
332where
333    I: IntoIterator<Item = &'a str>,
334    U: FromStr,
335{
336    let mut iter = i.into_iter();
337    let Some(val) = iter.next() else {
338        return Ok(None);
339    };
340    match FromStr::from_str(val) {
341        Ok(u) => Ok(Some(u)),
342        Err(..) => Err(build_internal_error!("Failed to convert (optional)", val)),
343    }
344}
345
346fn from_iter_radix<'a, I, U>(i: I, radix: u32) -> ProcResult<U>
347where
348    I: IntoIterator<Item = &'a str>,
349    U: FromStrRadix,
350{
351    let mut iter = i.into_iter();
352    let val = expect!(iter.next());
353
354    let val = match radix {
355        16 => {
356            if let Some(val) = val.strip_prefix("0x") {
357                val
358            } else if let Some(val) = val.strip_prefix("0X") {
359                val
360            } else {
361                val
362            }
363        }
364        _ => val,
365    };
366
367    match FromStrRadix::from_str_radix(val, radix) {
368        Ok(u) => Ok(u),
369        Err(..) => Err(build_internal_error!("Failed to convert (radix)", val)),
370    }
371}
372
373mod cgroups;
374pub use cgroups::*;
375
376mod cpuinfo;
377pub use cpuinfo::*;
378
379mod crypto;
380pub use crypto::*;
381
382mod devices;
383pub use devices::*;
384
385mod diskstats;
386pub use diskstats::*;
387
388mod iomem;
389pub use iomem::*;
390
391pub mod keyring;
392
393mod locks;
394pub use locks::*;
395
396mod mounts;
397pub use mounts::*;
398
399mod partitions;
400pub use partitions::*;
401
402mod meminfo;
403pub use meminfo::*;
404
405pub mod net;
406
407mod pressure;
408pub use pressure::*;
409
410pub mod process;
411
412mod kpageflags;
413pub use kpageflags::*;
414
415pub mod sys;
416pub use sys::kernel::Version as KernelVersion;
417
418mod sysvipc_shm;
419pub use sysvipc_shm::*;
420
421mod uptime;
422pub use uptime::*;
423
424pub trait FromStrRadix: Sized {
426    fn from_str_radix(t: &str, radix: u32) -> Result<Self, std::num::ParseIntError>;
427}
428
429impl FromStrRadix for u64 {
430    fn from_str_radix(s: &str, radix: u32) -> Result<u64, std::num::ParseIntError> {
431        u64::from_str_radix(s, radix)
432    }
433}
434
435impl FromStrRadix for u32 {
436    fn from_str_radix(s: &str, radix: u32) -> Result<u32, std::num::ParseIntError> {
437        u32::from_str_radix(s, radix)
438    }
439}
440
441impl FromStrRadix for i32 {
442    fn from_str_radix(s: &str, radix: u32) -> Result<i32, std::num::ParseIntError> {
443        i32::from_str_radix(s, radix)
444    }
445}
446
447fn split_into_num<T: FromStrRadix>(s: &str, sep: char, radix: u32) -> ProcResult<(T, T)> {
448    let mut s = s.split(sep);
449    let a = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
450    let b = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
451    Ok((a, b))
452}
453
454#[derive(Debug)]
456#[doc(hidden)]
457pub struct IoErrorWrapper {
458    pub path: PathBuf,
459    pub inner: std::io::Error,
460}
461
462impl std::error::Error for IoErrorWrapper {}
463impl fmt::Display for IoErrorWrapper {
464    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
465        write!(f, "IoErrorWrapper({}): {}", self.path.display(), self.inner)
466    }
467}
468
469pub type ProcResult<T> = Result<T, ProcError>;
473
474#[derive(Debug)]
479pub enum ProcError {
480    PermissionDenied(Option<PathBuf>),
485    NotFound(Option<PathBuf>),
488    Incomplete(Option<PathBuf>),
492    Io(std::io::Error, Option<PathBuf>),
494    Other(String),
496    InternalError(InternalError),
502}
503
504pub trait ProcErrorExt {
506    fn error_path(self, path: &Path) -> Self;
508}
509
510impl ProcErrorExt for ProcError {
511    fn error_path(mut self, path: &Path) -> Self {
512        use ProcError::*;
513        match &mut self {
514            PermissionDenied(p) | NotFound(p) | Incomplete(p) | Io(_, p) if p.is_none() => {
515                *p = Some(path.to_owned());
516            }
517            _ => (),
518        }
519        self
520    }
521}
522
523impl<T> ProcErrorExt for ProcResult<T> {
524    fn error_path(self, path: &Path) -> Self {
525        self.map_err(|e| e.error_path(path))
526    }
527}
528
529#[cfg_attr(feature = "serde1", derive(Serialize))]
537pub struct InternalError {
538    pub msg: String,
539    pub file: &'static str,
540    pub line: u32,
541    #[cfg(feature = "backtrace")]
542    #[cfg_attr(feature = "serde1", serde(skip))]
543    pub backtrace: backtrace::Backtrace,
544}
545
546impl std::fmt::Debug for InternalError {
547    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
548        write!(
549            f,
550            "bug at {}:{} (please report this procfs bug)\n{}",
551            self.file, self.line, self.msg
552        )
553    }
554}
555
556impl std::fmt::Display for InternalError {
557    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
558        write!(
559            f,
560            "bug at {}:{} (please report this procfs bug)\n{}",
561            self.file, self.line, self.msg
562        )
563    }
564}
565
566impl From<std::io::Error> for ProcError {
567    fn from(io: std::io::Error) -> Self {
568        use std::io::ErrorKind;
569        let kind = io.kind();
570        if io.get_ref().is_some() {
573            let inner = io.into_inner().unwrap();
574
575            match inner.downcast::<IoErrorWrapper>() {
577                Ok(wrapper) => {
578                    let path = wrapper.path;
579                    match kind {
580                        ErrorKind::PermissionDenied => ProcError::PermissionDenied(Some(path)),
581                        ErrorKind::NotFound => ProcError::NotFound(Some(path)),
582                        _other => {
583                            const ESRCH: i32 = 3;
586                            if matches!(wrapper.inner.raw_os_error(), Some(raw) if raw == ESRCH) {
587                                return ProcError::NotFound(Some(path));
589                            } else {
590                                ProcError::Io(wrapper.inner, Some(path))
591                            }
592                        }
593                    }
594                }
595                Err(io) => {
596                    ProcError::Io(std::io::Error::new(kind, io), None)
598                }
599            }
600        } else {
601            match kind {
602                ErrorKind::PermissionDenied => ProcError::PermissionDenied(None),
603                ErrorKind::NotFound => ProcError::NotFound(None),
604                _other => ProcError::Io(io, None),
605            }
606        }
607    }
608}
609
610impl From<&'static str> for ProcError {
611    fn from(val: &'static str) -> Self {
612        ProcError::Other(val.to_owned())
613    }
614}
615
616impl From<std::num::ParseIntError> for ProcError {
617    fn from(val: std::num::ParseIntError) -> Self {
618        ProcError::Other(format!("ParseIntError: {}", val))
619    }
620}
621
622impl From<std::string::ParseError> for ProcError {
623    fn from(e: std::string::ParseError) -> Self {
624        match e {}
625    }
626}
627
628impl std::fmt::Display for ProcError {
629    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
630        match self {
631            ProcError::PermissionDenied(Some(p)) => write!(f, "Permission Denied: {}", p.display()),
633            ProcError::NotFound(Some(p)) => write!(f, "File not found: {}", p.display()),
634            ProcError::Incomplete(Some(p)) => write!(f, "Data incomplete: {}", p.display()),
635            ProcError::Io(inner, Some(p)) => {
636                write!(f, "Unexpected IO error({}): {}", p.display(), inner)
637            }
638            ProcError::PermissionDenied(None) => write!(f, "Permission Denied"),
640            ProcError::NotFound(None) => write!(f, "File not found"),
641            ProcError::Incomplete(None) => write!(f, "Data incomplete"),
642            ProcError::Io(inner, None) => write!(f, "Unexpected IO error: {}", inner),
643
644            ProcError::Other(s) => write!(f, "Unknown error {}", s),
645            ProcError::InternalError(e) => write!(f, "Internal error: {}", e),
646        }
647    }
648}
649
650impl std::error::Error for ProcError {}
651
652#[derive(Debug, Clone)]
657#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
658pub struct LoadAverage {
659    pub one: f32,
661    pub five: f32,
663    pub fifteen: f32,
665    pub cur: u32,
667    pub max: u32,
669    pub latest_pid: u32,
671}
672
673impl FromRead for LoadAverage {
674    fn from_read<R: Read>(mut reader: R) -> ProcResult<Self> {
675        let mut line = String::new();
676
677        reader.read_to_string(&mut line)?;
678        let mut s = line.split_whitespace();
679
680        let one = expect!(f32::from_str(expect!(s.next())));
681        let five = expect!(f32::from_str(expect!(s.next())));
682        let fifteen = expect!(f32::from_str(expect!(s.next())));
683        let curmax = expect!(s.next());
684        let latest_pid = expect!(u32::from_str(expect!(s.next())));
685
686        let mut s = curmax.split('/');
687        let cur = expect!(u32::from_str(expect!(s.next())));
688        let max = expect!(u32::from_str(expect!(s.next())));
689
690        Ok(LoadAverage {
691            one,
692            five,
693            fifteen,
694            cur,
695            max,
696            latest_pid,
697        })
698    }
699}
700
701#[derive(Debug, Clone, PartialEq, Eq)]
703#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
704pub enum ConfigSetting {
705    Yes,
706    Module,
707    Value(String),
708}
709
710#[derive(Debug, Clone)]
712#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
713pub struct KernelConfig(pub HashMap<String, ConfigSetting>);
714
715impl FromBufRead for KernelConfig {
716    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
717        let mut map = HashMap::new();
718
719        for line in r.lines() {
720            let line = line?;
721            if line.starts_with('#') {
722                continue;
723            }
724            if line.contains('=') {
725                let mut s = line.splitn(2, '=');
726                let name = expect!(s.next()).to_owned();
727                let value = match expect!(s.next()) {
728                    "y" => ConfigSetting::Yes,
729                    "m" => ConfigSetting::Module,
730                    s => ConfigSetting::Value(s.to_owned()),
731                };
732                map.insert(name, value);
733            }
734        }
735
736        Ok(KernelConfig(map))
737    }
738}
739
740#[derive(Debug, Clone)]
748#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
749pub struct CpuTime {
750    pub user: u64,
752    pub nice: u64,
754    pub system: u64,
756    pub idle: u64,
758    pub iowait: Option<u64>,
773    pub irq: Option<u64>,
777    pub softirq: Option<u64>,
781    pub steal: Option<u64>,
788    pub guest: Option<u64>,
793    pub guest_nice: Option<u64>,
797
798    tps: u64,
799}
800
801impl CpuTime {
802    fn from_str(s: &str, ticks_per_second: u64) -> ProcResult<CpuTime> {
803        let mut s = s.split_whitespace();
804
805        let tps = ticks_per_second;
808
809        s.next();
810        let user = from_str!(u64, expect!(s.next()));
811        let nice = from_str!(u64, expect!(s.next()));
812        let system = from_str!(u64, expect!(s.next()));
813        let idle = from_str!(u64, expect!(s.next()));
814
815        let iowait = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
816        let irq = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
817        let softirq = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
818        let steal = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
819        let guest = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
820        let guest_nice = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
821
822        Ok(CpuTime {
823            user,
824            nice,
825            system,
826            idle,
827            iowait,
828            irq,
829            softirq,
830            steal,
831            guest,
832            guest_nice,
833            tps,
834        })
835    }
836
837    pub fn user_ms(&self) -> u64 {
839        let ms_per_tick = 1000 / self.tps;
840        self.user * ms_per_tick
841    }
842
843    pub fn user_duration(&self) -> Duration {
845        Duration::from_millis(self.user_ms())
846    }
847
848    pub fn nice_ms(&self) -> u64 {
850        let ms_per_tick = 1000 / self.tps;
851        self.nice * ms_per_tick
852    }
853
854    pub fn nice_duration(&self) -> Duration {
856        Duration::from_millis(self.nice_ms())
857    }
858
859    pub fn system_ms(&self) -> u64 {
861        let ms_per_tick = 1000 / self.tps;
862        self.system * ms_per_tick
863    }
864
865    pub fn system_duration(&self) -> Duration {
867        Duration::from_millis(self.system_ms())
868    }
869
870    pub fn idle_ms(&self) -> u64 {
872        let ms_per_tick = 1000 / self.tps;
873        self.idle * ms_per_tick
874    }
875
876    pub fn idle_duration(&self) -> Duration {
878        Duration::from_millis(self.idle_ms())
879    }
880
881    pub fn iowait_ms(&self) -> Option<u64> {
883        let ms_per_tick = 1000 / self.tps;
884        self.iowait.map(|io| io * ms_per_tick)
885    }
886
887    pub fn iowait_duration(&self) -> Option<Duration> {
889        self.iowait_ms().map(Duration::from_millis)
890    }
891
892    pub fn irq_ms(&self) -> Option<u64> {
894        let ms_per_tick = 1000 / self.tps;
895        self.irq.map(|ms| ms * ms_per_tick)
896    }
897
898    pub fn irq_duration(&self) -> Option<Duration> {
900        self.irq_ms().map(Duration::from_millis)
901    }
902
903    pub fn softirq_ms(&self) -> Option<u64> {
905        let ms_per_tick = 1000 / self.tps;
906        self.softirq.map(|ms| ms * ms_per_tick)
907    }
908
909    pub fn softirq_duration(&self) -> Option<Duration> {
911        self.softirq_ms().map(Duration::from_millis)
912    }
913
914    pub fn steal_ms(&self) -> Option<u64> {
916        let ms_per_tick = 1000 / self.tps;
917        self.steal.map(|ms| ms * ms_per_tick)
918    }
919
920    pub fn steal_duration(&self) -> Option<Duration> {
922        self.steal_ms().map(Duration::from_millis)
923    }
924
925    pub fn guest_ms(&self) -> Option<u64> {
927        let ms_per_tick = 1000 / self.tps;
928        self.guest.map(|ms| ms * ms_per_tick)
929    }
930
931    pub fn guest_duration(&self) -> Option<Duration> {
933        self.guest_ms().map(Duration::from_millis)
934    }
935
936    pub fn guest_nice_ms(&self) -> Option<u64> {
938        let ms_per_tick = 1000 / self.tps;
939        self.guest_nice.map(|ms| ms * ms_per_tick)
940    }
941
942    pub fn guest_nice_duration(&self) -> Option<Duration> {
944        self.guest_nice_ms().map(Duration::from_millis)
945    }
946}
947
948#[derive(Debug, Clone)]
950#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
951pub struct KernelStats {
952    pub total: CpuTime,
954    pub cpu_time: Vec<CpuTime>,
956
957    pub ctxt: u64,
959
960    pub btime: u64,
962
963    pub processes: u64,
965
966    pub procs_running: Option<u32>,
970
971    pub procs_blocked: Option<u32>,
975}
976
977impl FromBufReadSI for KernelStats {
978    fn from_buf_read<R: BufRead>(r: R, system_info: &SystemInfo) -> ProcResult<Self> {
979        let lines = r.lines();
980
981        let mut total_cpu = None;
982        let mut cpus = Vec::new();
983        let mut ctxt = None;
984        let mut btime = None;
985        let mut processes = None;
986        let mut procs_running = None;
987        let mut procs_blocked = None;
988
989        for line in lines {
990            let line = line?;
991            if line.starts_with("cpu ") {
992                total_cpu = Some(CpuTime::from_str(&line, system_info.ticks_per_second())?);
993            } else if line.starts_with("cpu") {
994                cpus.push(CpuTime::from_str(&line, system_info.ticks_per_second())?);
995            } else if let Some(stripped) = line.strip_prefix("ctxt ") {
996                ctxt = Some(from_str!(u64, stripped));
997            } else if let Some(stripped) = line.strip_prefix("btime ") {
998                btime = Some(from_str!(u64, stripped));
999            } else if let Some(stripped) = line.strip_prefix("processes ") {
1000                processes = Some(from_str!(u64, stripped));
1001            } else if let Some(stripped) = line.strip_prefix("procs_running ") {
1002                procs_running = Some(from_str!(u32, stripped));
1003            } else if let Some(stripped) = line.strip_prefix("procs_blocked ") {
1004                procs_blocked = Some(from_str!(u32, stripped));
1005            }
1006        }
1007
1008        Ok(KernelStats {
1009            total: expect!(total_cpu),
1010            cpu_time: cpus,
1011            ctxt: expect!(ctxt),
1012            btime: expect!(btime),
1013            processes: expect!(processes),
1014            procs_running,
1015            procs_blocked,
1016        })
1017    }
1018}
1019
1020#[derive(Debug, Clone)]
1026#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
1027pub struct VmStat(pub HashMap<String, i64>);
1028
1029impl FromBufRead for VmStat {
1030    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
1031        let mut map = HashMap::new();
1032        for line in r.lines() {
1033            let line = line?;
1034            let mut split = line.split_whitespace();
1035            let name = expect!(split.next());
1036            let val = from_str!(i64, expect!(split.next()));
1037            map.insert(name.to_owned(), val);
1038        }
1039
1040        Ok(VmStat(map))
1041    }
1042}
1043
1044#[derive(Debug, Clone)]
1049#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
1050pub struct KernelModule {
1051    pub name: String,
1053
1054    pub size: u32,
1056
1057    pub refcount: i32,
1059
1060    pub used_by: Vec<String>,
1062
1063    pub state: String,
1067}
1068
1069#[derive(Debug, Clone)]
1071#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
1072pub struct KernelModules(pub HashMap<String, KernelModule>);
1073
1074impl FromBufRead for KernelModules {
1075    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
1077        let mut map = HashMap::new();
1079        for line in r.lines() {
1080            let line: String = line?;
1081            let mut s = line.split_whitespace();
1082            let name = expect!(s.next());
1083            let size = from_str!(u32, expect!(s.next()));
1084            let refcount = from_str!(i32, expect!(s.next()));
1085            let used_by: &str = expect!(s.next());
1086            let state = expect!(s.next());
1087
1088            map.insert(
1089                name.to_string(),
1090                KernelModule {
1091                    name: name.to_string(),
1092                    size,
1093                    refcount,
1094                    used_by: if used_by == "-" {
1095                        Vec::new()
1096                    } else {
1097                        used_by
1098                            .split(',')
1099                            .filter(|s| !s.is_empty())
1100                            .map(|s| s.to_string())
1101                            .collect()
1102                    },
1103                    state: state.to_string(),
1104                },
1105            );
1106        }
1107
1108        Ok(KernelModules(map))
1109    }
1110}
1111
1112#[derive(Debug, Clone)]
1114#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
1115pub struct KernelCmdline(pub Vec<String>);
1116
1117impl FromRead for KernelCmdline {
1118    fn from_read<R: Read>(mut r: R) -> ProcResult<Self> {
1120        let mut buf = String::new();
1121        r.read_to_string(&mut buf)?;
1122        Ok(KernelCmdline(
1123            buf.split(' ')
1124                .filter_map(|s| if !s.is_empty() { Some(s.to_string()) } else { None })
1125                .collect(),
1126        ))
1127    }
1128}
1129
1130#[cfg(test)]
1131mod tests {
1132    use super::*;
1133
1134    #[test]
1135    fn test_kernel_from_str() {
1136        let k = KernelVersion::from_str("1.2.3").unwrap();
1137        assert_eq!(k.major, 1);
1138        assert_eq!(k.minor, 2);
1139        assert_eq!(k.patch, 3);
1140
1141        let k = KernelVersion::from_str("4.9.16-gentoo").unwrap();
1142        assert_eq!(k.major, 4);
1143        assert_eq!(k.minor, 9);
1144        assert_eq!(k.patch, 16);
1145
1146        let k = KernelVersion::from_str("4.9.266-0.1.ac.225.84.332.metal1.x86_64").unwrap();
1147        assert_eq!(k.major, 4);
1148        assert_eq!(k.minor, 9);
1149        assert_eq!(k.patch, 266);
1150    }
1151
1152    #[test]
1153    fn test_kernel_cmp() {
1154        let a = KernelVersion::from_str("1.2.3").unwrap();
1155        let b = KernelVersion::from_str("1.2.3").unwrap();
1156        let c = KernelVersion::from_str("1.2.4").unwrap();
1157        let d = KernelVersion::from_str("1.5.4").unwrap();
1158        let e = KernelVersion::from_str("2.5.4").unwrap();
1159
1160        assert_eq!(a, b);
1161        assert!(a < c);
1162        assert!(a < d);
1163        assert!(a < e);
1164        assert!(e > d);
1165        assert!(e > c);
1166        assert!(e > b);
1167    }
1168
1169    #[test]
1170    fn test_loadavg_from_reader() -> ProcResult<()> {
1171        let load_average = LoadAverage::from_read("2.63 1.00 1.42 3/4280 2496732".as_bytes())?;
1172
1173        assert_eq!(load_average.one, 2.63);
1174        assert_eq!(load_average.five, 1.00);
1175        assert_eq!(load_average.fifteen, 1.42);
1176        assert_eq!(load_average.max, 4280);
1177        assert_eq!(load_average.cur, 3);
1178        assert_eq!(load_average.latest_pid, 2496732);
1179        Ok(())
1180    }
1181
1182    #[test]
1183    fn test_from_str() -> ProcResult<()> {
1184        assert_eq!(from_str!(u8, "12"), 12);
1185        assert_eq!(from_str!(u8, "A", 16), 10);
1186        Ok(())
1187    }
1188
1189    #[test]
1190    fn test_from_str_fail() {
1191        fn inner() -> ProcResult<()> {
1192            let s = "four";
1193            from_str!(u8, s);
1194            unreachable!()
1195        }
1196
1197        assert!(inner().is_err())
1198    }
1199
1200    #[test]
1201    fn test_nopanic() {
1202        fn _inner() -> ProcResult<bool> {
1203            let x: Option<bool> = None;
1204            let y: bool = expect!(x);
1205            Ok(y)
1206        }
1207
1208        let r = _inner();
1209        println!("{:?}", r);
1210        assert!(r.is_err());
1211
1212        fn _inner2() -> ProcResult<bool> {
1213            let _f: std::fs::File = expect!(std::fs::File::open("/doesnotexist"));
1214            Ok(true)
1215        }
1216
1217        let r = _inner2();
1218        println!("{:?}", r);
1219        assert!(r.is_err());
1220    }
1221
1222    #[cfg(feature = "backtrace")]
1223    #[test]
1224    fn test_backtrace() {
1225        fn _inner() -> ProcResult<bool> {
1226            let _f: std::fs::File = expect!(std::fs::File::open("/doesnotexist"));
1227            Ok(true)
1228        }
1229
1230        let r = _inner();
1231        println!("{:?}", r);
1232    }
1233}