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}