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