1 #![deny(intra_doc_link_resolution_failure)]
2 //! This crate provides to an interface into the linux `procfs` filesystem, usually mounted at
3 //! `/proc`.
4 //!
5 //! This is a pseudo-filesystem which is available on most every linux system and provides an
6 //! interface to kernel data structures.
7 //!
8 //!
9 //! # Kernel support
10 //!
11 //! Not all fields/data are available in each kernel.  Some fields were added in specific kernel
12 //! releases, and other fields are only present in certain kernel configuration options are
13 //! enabled.  These are represented as `Option` fields in this crate.
14 //!
15 //! This crate aims to support all 2.6 kernels (and newer).
16 //!
17 //! # Documentation
18 //!
19 //! In almost all cases, the documentation is taken from the
20 //! [`proc.5`](http://man7.org/linux/man-pages/man5/proc.5.html) manual page.  This means that
21 //! sometimes the style of writing is not very "rusty", or may do things like reference related files
22 //! (instead of referencing related structs).  Contributions to improve this are welcome.
23 //!
24 //! # Panicing
25 //!
26 //! While previous versions of the library could panic, this current version aims to be panic-free
27 //! in a many situations as possible.  Whenever the procfs crate encounters a bug in its own
28 //! parsing code, it will return an [`InternalError`](enum.ProcError.html#variant.InternalError) error.  This should be considered a
29 //! bug and should be [reported](https://github.com/eminence/procfs).  If you encounter a panic,
30 //! please report that as well.
31 //!
32 //! # Cargo features
33 //!
34 //! The following cargo features are available:
35 //!
36 //! * `chrono` -- Default.  Optional.  This feature enables a few methods that return values as `DateTime` objects.
37 //! * `backtrace` -- Optional.  This feature lets you get a stack trace whenever an `InternalError` is raised.
38 //!
39 //! # Examples
40 //!
41 //! Examples can be found in the various modules shown below.
42 //!
43 //!
44 
45 #[cfg(unix)]
46 extern crate libc;
47 #[macro_use]
48 extern crate bitflags;
49 
50 #[macro_use]
51 extern crate lazy_static;
52 extern crate byteorder;
53 extern crate hex;
54 extern crate libflate;
55 
56 #[cfg(unix)]
57 mod platform_specific_items {
58     pub use libc::pid_t;
59     pub use libc::sysconf;
60     pub use libc::{_SC_CLK_TCK, _SC_PAGESIZE};
61 }
62 
63 // Even though this lib isn't supported on windows, I want it to at least typecheck
64 #[cfg(windows)]
65 mod platform_specific_items {
66     pub type pid_t = i32; // just to make things build on windows in my IDE
sysconf(_: i32) -> i6467     pub fn sysconf(_: i32) -> i64 {
68         panic!()
69     }
70     pub const _SC_CLK_TCK: i32 = 2;
71     pub const _SC_PAGESIZE: i32 = 30;
72 }
73 
74 use crate::platform_specific_items::*;
75 
76 use std::collections::HashMap;
77 use std::ffi::CStr;
78 use std::fmt;
79 use std::fs::File;
80 use std::io::{self, Read, Write};
81 use std::mem;
82 use std::os::raw::c_char;
83 use std::path::{Path, PathBuf};
84 use std::str::FromStr;
85 
86 #[cfg(feature = "chrono")]
87 use chrono::{DateTime, Local};
88 
89 const PROC_CONFIG_GZ: &str = "/proc/config.gz";
90 const BOOT_CONFIG: &str = "/boot/config";
91 
92 trait IntoOption<T> {
into_option(t: Self) -> Option<T>93     fn into_option(t: Self) -> Option<T>;
94 }
95 
96 impl<T> IntoOption<T> for Option<T> {
into_option(t: Option<T>) -> Option<T>97     fn into_option(t: Option<T>) -> Option<T> {
98         t
99     }
100 }
101 
102 impl<T, R> IntoOption<T> for Result<T, R> {
into_option(t: Result<T, R>) -> Option<T>103     fn into_option(t: Result<T, R>) -> Option<T> {
104         t.ok()
105     }
106 }
107 
108 pub(crate) trait IntoResult<T, E> {
into(t: Self) -> Result<T, E>109     fn into(t: Self) -> Result<T, E>;
110 }
111 
112 macro_rules! build_internal_error {
113     ($err: expr) => {
114         crate::ProcError::InternalError(crate::InternalError {
115             msg: format!("Internal Unwrap Error: {}", $err),
116             file: file!(),
117             line: line!(),
118             #[cfg(feature = "backtrace")]
119             backtrace: backtrace::Backtrace::new(),
120         })
121     };
122     ($err: expr, $msg: expr) => {
123         crate::ProcError::InternalError(crate::InternalError {
124             msg: format!("Internal Unwrap Error: {}: {}", $msg, $err),
125             file: file!(),
126             line: line!(),
127             #[cfg(feature = "backtrace")]
128             backtrace: backtrace::Backtrace::new(),
129         })
130     };
131 }
132 
133 // custom NoneError, since std::option::NoneError is nightly-only
134 // See https://github.com/rust-lang/rust/issues/42327
135 struct NoneError;
136 
137 impl std::fmt::Display for NoneError {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result138     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139         write!(f, "NoneError")
140     }
141 }
142 
143 impl<T> IntoResult<T, NoneError> for Option<T> {
into(t: Option<T>) -> Result<T, NoneError>144     fn into(t: Option<T>) -> Result<T, NoneError> {
145         t.ok_or(NoneError)
146     }
147 }
148 
149 impl<T, E> IntoResult<T, E> for Result<T, E> {
into(t: Result<T, E>) -> Result<T, E>150     fn into(t: Result<T, E>) -> Result<T, E> {
151         t
152     }
153 }
154 
155 #[macro_use]
156 #[allow(unused_macros)]
157 macro_rules! proc_panic {
158     ($e:expr) => {
159         crate::IntoOption::into_option($e).unwrap_or_else(|| {
160             panic!(
161                 "Failed to unwrap {}. Please report this as a procfs bug.",
162                 stringify!($e)
163             )
164         })
165     };
166     ($e:expr, $msg:expr) => {
167         crate::IntoOption::into_option($e).unwrap_or_else(|| {
168             panic!(
169                 "Failed to unwrap {} ({}). Please report this as a procfs bug.",
170                 stringify!($e),
171                 $msg
172             )
173         })
174     };
175 }
176 
177 macro_rules! expect {
178     ($e:expr) => {
179         match crate::IntoResult::into($e) {
180             Ok(v) => v,
181             Err(e) => return Err(build_internal_error!(e)),
182         }
183     };
184     ($e:expr, $msg:expr) => {
185         match crate::IntoResult::into($e) {
186             Ok(v) => v,
187             Err(e) => return Err(build_internal_error!(e, $msg)),
188         }
189     };
190 }
191 
192 #[macro_use]
193 macro_rules! from_str {
194     ($t:tt, $e:expr) => {{
195         let e = $e;
196         expect!(
197             $t::from_str_radix(e, 10),
198             format!(
199                 "Failed to parse {} ({:?}) as a {}",
200                 stringify!($e),
201                 e,
202                 stringify!($t),
203             )
204         )
205     }};
206     ($t:tt, $e:expr, $radix:expr) => {{
207         let e = $e;
208         expect!(
209             $t::from_str_radix(e, $radix),
210             format!(
211                 "Failed to parse {} ({:?}) as a {}",
212                 stringify!($e),
213                 e,
214                 stringify!($t)
215             )
216         )
217     }};
218     ($t:tt, $e:expr, $radix:expr, pid:$pid:expr) => {{
219         let e = $e;
220         expect!(
221             $t::from_str_radix(e, $radix),
222             format!(
223                 "Failed to parse {} ({:?}) as a {} (pid {})",
224                 stringify!($e),
225                 e,
226                 stringify!($t),
227                 $pid
228             )
229         )
230     }};
231 }
232 
233 macro_rules! wrap_io_error {
234     ($path:expr, $expr:expr) => {
235         match $expr {
236             Ok(v) => Ok(v),
237             Err(e) => {
238                 let kind = e.kind();
239                 Err(io::Error::new(
240                     kind,
241                     IoErrorWrapper {
242                         path: $path.clone(),
243                         inner: e.into_inner(),
244                     },
245                 ))
246             }
247         }
248     };
249 }
250 
read_file<P: AsRef<Path>>(path: P) -> ProcResult<String>251 pub(crate) fn read_file<P: AsRef<Path>>(path: P) -> ProcResult<String> {
252     let mut f = FileWrapper::open(path)?;
253     let mut buf = String::new();
254     f.read_to_string(&mut buf)?;
255     Ok(buf)
256 }
257 
write_file<P: AsRef<Path>, T: AsRef<[u8]>>(path: P, buf: T) -> ProcResult<()>258 pub(crate) fn write_file<P: AsRef<Path>, T: AsRef<[u8]>>(path: P, buf: T) -> ProcResult<()> {
259     let mut f = File::open(path)?;
260     f.write_all(buf.as_ref())?;
261     Ok(())
262 }
263 
read_value<P, T, E>(path: P) -> ProcResult<T> where P: AsRef<Path>, T: FromStr<Err = E>, ProcError: From<E>,264 pub(crate) fn read_value<P, T, E>(path: P) -> ProcResult<T>
265 where
266     P: AsRef<Path>,
267     T: FromStr<Err = E>,
268     ProcError: From<E>,
269 {
270     let val = read_file(path)?;
271     Ok(<T as FromStr>::from_str(val.trim())?)
272     //Ok(val.trim().parse()?)
273 }
274 
write_value<P: AsRef<Path>, T: fmt::Display>(path: P, value: T) -> ProcResult<()>275 pub(crate) fn write_value<P: AsRef<Path>, T: fmt::Display>(path: P, value: T) -> ProcResult<()> {
276     write_file(path, value.to_string().as_bytes())
277 }
278 
from_iter<'a, I, U>(i: I) -> ProcResult<U> where I: IntoIterator<Item = &'a str>, U: FromStr,279 pub(crate) fn from_iter<'a, I, U>(i: I) -> ProcResult<U>
280 where
281     I: IntoIterator<Item = &'a str>,
282     U: FromStr,
283 {
284     let mut iter = i.into_iter();
285     let val = expect!(iter.next());
286     match FromStr::from_str(val) {
287         Ok(u) => Ok(u),
288         Err(..) => Err(build_internal_error!("Failed to convert")),
289     }
290 }
291 
292 pub mod process;
293 
294 mod meminfo;
295 pub use crate::meminfo::*;
296 
297 pub mod net;
298 
299 mod cpuinfo;
300 pub use crate::cpuinfo::*;
301 
302 mod cgroups;
303 pub use crate::cgroups::*;
304 
305 pub mod sys;
306 pub use crate::sys::kernel::Version as KernelVersion;
307 
308 mod pressure;
309 pub use crate::pressure::*;
310 
311 lazy_static! {
312     /// The number of clock ticks per second.
313     ///
314     /// This is calculated from `sysconf(_SC_CLK_TCK)`.
315     static ref TICKS_PER_SECOND: i64 = {
316         ticks_per_second().unwrap()
317     };
318     /// The version of the currently running kernel.
319     ///
320     /// This is a lazily constructed static.  You can also get this information via
321     /// [KernelVersion::new()].
322     static ref KERNEL: KernelVersion = {
323         KernelVersion::current().unwrap()
324     };
325     /// Memory page size, in bytes.
326     ///
327     /// This is calculated from `sysconf(_SC_PAGESIZE)`.
328     static ref PAGESIZE: i64 = {
329         page_size().unwrap()
330     };
331 }
332 
convert_to_kibibytes(num: u64, unit: &str) -> ProcResult<u64>333 fn convert_to_kibibytes(num: u64, unit: &str) -> ProcResult<u64> {
334     match unit {
335         "B" => Ok(num),
336         "KiB" | "kiB" | "kB" | "KB" => Ok(num * 1024),
337         "MiB" | "miB" | "MB" | "mB" => Ok(num * 1024 * 1024),
338         "GiB" | "giB" | "GB" | "gB" => Ok(num * 1024 * 1024 * 1024),
339         unknown => Err(build_internal_error!(format!(
340             "Unknown unit type {}",
341             unknown
342         ))),
343     }
344 }
345 
346 trait FromStrRadix: Sized {
from_str_radix(t: &str, radix: u32) -> Result<Self, std::num::ParseIntError>347     fn from_str_radix(t: &str, radix: u32) -> Result<Self, std::num::ParseIntError>;
348 }
349 
350 impl FromStrRadix for u64 {
from_str_radix(s: &str, radix: u32) -> Result<u64, std::num::ParseIntError>351     fn from_str_radix(s: &str, radix: u32) -> Result<u64, std::num::ParseIntError> {
352         u64::from_str_radix(s, radix)
353     }
354 }
355 impl FromStrRadix for i32 {
from_str_radix(s: &str, radix: u32) -> Result<i32, std::num::ParseIntError>356     fn from_str_radix(s: &str, radix: u32) -> Result<i32, std::num::ParseIntError> {
357         i32::from_str_radix(s, radix)
358     }
359 }
360 
split_into_num<T: FromStrRadix>(s: &str, sep: char, radix: u32) -> ProcResult<(T, T)>361 fn split_into_num<T: FromStrRadix>(s: &str, sep: char, radix: u32) -> ProcResult<(T, T)> {
362     let mut s = s.split(sep);
363     let a = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
364     let b = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
365     Ok((a, b))
366 }
367 
368 /// This is used to hold both an IO error as well as the path of the file that originated the error
369 #[derive(Debug)]
370 struct IoErrorWrapper {
371     path: PathBuf,
372     inner: Option<Box<dyn std::error::Error + Send + Sync>>,
373 }
374 
375 impl std::error::Error for IoErrorWrapper {}
376 impl fmt::Display for IoErrorWrapper {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>377     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
378         if let Some(inner) = &self.inner {
379             write!(f, "IO Error({}): {}", self.path.display(), inner)
380         } else {
381             write!(f, "IO Error({})", self.path.display())
382         }
383     }
384 }
385 
386 /// A wrapper around a `File` that remembers the name of the path
387 struct FileWrapper {
388     inner: File,
389     path: PathBuf,
390 }
391 
392 impl FileWrapper {
open<P: AsRef<Path>>(path: P) -> Result<FileWrapper, io::Error>393     fn open<P: AsRef<Path>>(path: P) -> Result<FileWrapper, io::Error> {
394         let p = path.as_ref();
395         match File::open(&p) {
396             Ok(f) => Ok(FileWrapper {
397                 inner: f,
398                 path: p.to_owned(),
399             }),
400             Err(e) => {
401                 let kind = e.kind();
402                 Err(io::Error::new(
403                     kind,
404                     IoErrorWrapper {
405                         path: p.to_owned(),
406                         inner: e.into_inner(),
407                     },
408                 ))
409             }
410         }
411     }
412 }
413 
414 impl Read for FileWrapper {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>415     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
416         wrap_io_error!(self.path, self.inner.read(buf))
417     }
read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize>418     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
419         wrap_io_error!(self.path, self.inner.read_to_end(buf))
420     }
read_to_string(&mut self, buf: &mut String) -> io::Result<usize>421     fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
422         wrap_io_error!(self.path, self.inner.read_to_string(buf))
423     }
read_exact(&mut self, buf: &mut [u8]) -> io::Result<()>424     fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
425         wrap_io_error!(self.path, self.inner.read_exact(buf))
426     }
427 }
428 
429 /// The main error type for the procfs crate.
430 ///
431 /// For more info, see the [ProcError] type.
432 pub type ProcResult<T> = Result<T, ProcError>;
433 
434 /// The various error conditions in the procfs crate.
435 ///
436 /// Most of the variants have an `Option<PathBuf>` component.  If the error root cause was related
437 /// to some operation on a file, the path of this file will be stored in this component.
438 #[derive(Debug)]
439 pub enum ProcError {
440     /// A standard permission denied error.
441     ///
442     /// This will be a common error, since some files in the procfs filesystem are only readable by
443     /// the root user.
444     PermissionDenied(Option<PathBuf>),
445     /// This might mean that the process no longer exists, or that your kernel doesn't support the
446     /// feature you are trying to use.
447     NotFound(Option<PathBuf>),
448     /// This might mean that a procfs file has incomplete contents.
449     ///
450     /// If you encounter this error, consider retrying the operation.
451     Incomplete(Option<PathBuf>),
452     /// Any other IO error (rare).
453     Io(std::io::Error, Option<PathBuf>),
454     /// Any other non-IO error (very rare).
455     Other(String),
456     /// This error indicates that some unexpected error occurred.  This is a bug.  The inner
457     /// [InternalError] struct will contain some more info.
458     ///
459     /// If you ever encounter this error, consider it a bug in the procfs crate and please report
460     /// it on github.
461     InternalError(InternalError),
462 }
463 
464 /// An internal error in the procfs crate
465 ///
466 /// If you encounter this error, consider it a bug and please report it on
467 /// [github](https://github.com/eminence/procfs).
468 ///
469 #[cfg_attr(
470     not(feature = "backtrace"),
471     doc = "If you compile with the optional `backtrace` feature, you can gain access to a stack trace of where the error happened."
472 )]
473 pub struct InternalError {
474     pub msg: String,
475     pub file: &'static str,
476     pub line: u32,
477     #[cfg(feature = "backtrace")]
478     pub backtrace: backtrace::Backtrace,
479 }
480 
481 impl std::fmt::Debug for InternalError {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result482     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
483         write!(
484             f,
485             "bug at {}:{} (please report this procfs bug)\n{}",
486             self.file, self.line, self.msg
487         )
488     }
489 }
490 
491 impl std::fmt::Display for InternalError {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result492     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
493         write!(
494             f,
495             "bug at {}:{} (please report this procfs bug)\n{}",
496             self.file, self.line, self.msg
497         )
498     }
499 }
500 
501 impl From<std::io::Error> for ProcError {
from(io: std::io::Error) -> Self502     fn from(io: std::io::Error) -> Self {
503         use std::io::ErrorKind;
504         let kind = io.kind();
505         let path: Option<PathBuf> = io.get_ref().and_then(|inner| {
506             if let Some(ref inner) = inner.downcast_ref::<IoErrorWrapper>() {
507                 Some(inner.path.clone())
508             } else {
509                 None
510             }
511         });
512         match kind {
513             ErrorKind::PermissionDenied => ProcError::PermissionDenied(path),
514             ErrorKind::NotFound => ProcError::NotFound(path),
515             _other => ProcError::Io(io, path),
516         }
517     }
518 }
519 
520 impl From<&'static str> for ProcError {
from(val: &'static str) -> Self521     fn from(val: &'static str) -> Self {
522         ProcError::Other(val.to_owned())
523     }
524 }
525 
526 impl From<std::num::ParseIntError> for ProcError {
from(val: std::num::ParseIntError) -> Self527     fn from(val: std::num::ParseIntError) -> Self {
528         ProcError::Other(format!("ParseIntError: {}", val))
529     }
530 }
531 
532 impl std::fmt::Display for ProcError {
fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>533     fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
534         match self {
535             // Variants with paths:
536             ProcError::PermissionDenied(Some(p)) => write!(f, "Permission Denied: {}", p.display()),
537             ProcError::NotFound(Some(p)) => write!(f, "File not found: {}", p.display()),
538             ProcError::Incomplete(Some(p)) => write!(f, "Data incomplete: {}", p.display()),
539             ProcError::Io(inner, Some(p)) => {
540                 write!(f, "Unexpected IO error({}): {}", p.display(), inner)
541             }
542             // Variants without paths:
543             ProcError::PermissionDenied(None) => write!(f, "Permission Denied"),
544             ProcError::NotFound(None) => write!(f, "File not found"),
545             ProcError::Incomplete(None) => write!(f, "Data incomplete"),
546             ProcError::Io(inner, None) => write!(f, "Unexpected IO error: {}", inner),
547 
548             ProcError::Other(s) => write!(f, "Uknown error {}", s),
549             ProcError::InternalError(e) => write!(f, "Internal error: {}", e),
550         }
551     }
552 }
553 
554 impl std::error::Error for ProcError {}
555 
556 /// Load average figures.
557 ///
558 /// Load averages are calculated as the number of jobs in the run queue (state R) or waiting for
559 /// disk I/O (state D) averaged over 1, 5, and 15 minutes.
560 #[derive(Debug)]
561 pub struct LoadAverage {
562     /// The one-minute load average
563     pub one: f32,
564     /// The five-minute load average
565     pub five: f32,
566     /// The fifteen-minute load average
567     pub fifteen: f32,
568     /// The number of currently runnable kernel scheduling  entities  (processes,  threads).
569     pub cur: u32,
570     /// The number of kernel scheduling entities that currently exist on the system.
571     pub max: u32,
572     /// The fifth field is the PID of the process that was most recently created on the system.
573     pub latest_pid: u32,
574 }
575 
576 impl LoadAverage {
577     /// Reads load average info from `/proc/loadavg`
new() -> ProcResult<LoadAverage>578     pub fn new() -> ProcResult<LoadAverage> {
579         let mut f = FileWrapper::open("/proc/loadavg")?;
580         let mut s = String::new();
581         f.read_to_string(&mut s)?;
582         let mut s = s.split_whitespace();
583 
584         let one = expect!(f32::from_str(expect!(s.next())));
585         let five = expect!(f32::from_str(expect!(s.next())));
586         let fifteen = expect!(f32::from_str(expect!(s.next())));
587         let curmax = expect!(s.next());
588         let latest_pid = expect!(u32::from_str(expect!(s.next())));
589 
590         let mut s = curmax.split('/');
591         let cur = expect!(u32::from_str(expect!(s.next())));
592         let max = expect!(u32::from_str(expect!(s.next())));
593 
594         Ok(LoadAverage {
595             one,
596             five,
597             fifteen,
598             cur,
599             max,
600             latest_pid,
601         })
602     }
603 }
604 
605 /// Return the number of ticks per second.
606 ///
607 /// This isn't part of the proc file system, but it's a useful thing to have, since several fields
608 /// count in ticks.  This is calculated from `sysconf(_SC_CLK_TCK)`.
ticks_per_second() -> std::io::Result<i64>609 pub fn ticks_per_second() -> std::io::Result<i64> {
610     if cfg!(unix) {
611         match unsafe { sysconf(_SC_CLK_TCK) } {
612             -1 => Err(std::io::Error::last_os_error()),
613             x => Ok(x.into()),
614         }
615     } else {
616         panic!("Not supported on non-unix platforms")
617     }
618 }
619 
620 /// The boottime of the system, as a `DateTime` object.
621 ///
622 /// This is calculated from `/proc/stat`.
623 #[cfg(feature = "chrono")]
boot_time() -> ProcResult<DateTime<Local>>624 pub fn boot_time() -> ProcResult<DateTime<Local>> {
625     use chrono::TimeZone;
626     let secs = boot_time_secs()?;
627 
628     Ok(chrono::Local.timestamp(secs as i64, 0))
629 }
630 
631 /// The boottime of the system, in seconds since the epoch
632 ///
633 /// This is calculated from `/proc/stat`.
634 ///
635 #[cfg_attr(
636     not(feature = "chrono"),
637     doc = "If you compile with the optional `chrono` feature, you can use the `boot_time()` method to get the boot time as a `DateTime` object."
638 )]
639 #[cfg_attr(
640     feature = "chrono",
641     doc = "See also [boot_time()] to get the boot time as a `DateTime`"
642 )]
boot_time_secs() -> ProcResult<u64>643 pub fn boot_time_secs() -> ProcResult<u64> {
644     BOOT_TIME.with(|x| {
645         let mut btime = x.borrow_mut();
646         if let Some(btime) = *btime {
647             Ok(btime)
648         } else {
649             let stat = KernelStats::new()?;
650             *btime = Some(stat.btime);
651             Ok(stat.btime)
652         }
653     })
654 }
655 
656 thread_local! {
657     static BOOT_TIME : std::cell::RefCell<Option<u64>> = std::cell::RefCell::new(None);
658 }
659 
660 /// Memory page size, in bytes.
661 ///
662 /// This is calculated from `sysconf(_SC_PAGESIZE)`.
page_size() -> std::io::Result<i64>663 pub fn page_size() -> std::io::Result<i64> {
664     if cfg!(unix) {
665         match unsafe { sysconf(_SC_PAGESIZE) } {
666             -1 => Err(std::io::Error::last_os_error()),
667             x => Ok(x.into()),
668         }
669     } else {
670         panic!("Not supported on non-unix platforms")
671     }
672 }
673 
674 /// Possible values for a kernel config option
675 #[derive(Debug, PartialEq)]
676 pub enum ConfigSetting {
677     Yes,
678     Module,
679     Value(String),
680 }
681 /// Returns a configuration options used to build the currently running kernel
682 ///
683 /// If CONFIG_KCONFIG_PROC is available, the config is read from `/proc/config.gz`.
684 /// Else look in `/boot/config-$(uname -r)` or `/boot/config` (in that order).
kernel_config() -> ProcResult<HashMap<String, ConfigSetting>>685 pub fn kernel_config() -> ProcResult<HashMap<String, ConfigSetting>> {
686     use libflate::gzip::Decoder;
687     use std::io::{BufRead, BufReader};
688 
689     let reader: Box<dyn BufRead> = if Path::new(PROC_CONFIG_GZ).exists() {
690         let file = FileWrapper::open(PROC_CONFIG_GZ)?;
691         let decoder = Decoder::new(file)?;
692         Box::new(BufReader::new(decoder))
693     } else {
694         let mut kernel: libc::utsname = unsafe { mem::zeroed() };
695 
696         if unsafe { libc::uname(&mut kernel) != 0 } {
697             return Err(ProcError::Other("Failed to call uname()".to_string()));
698         }
699 
700         let filename = format!(
701             "{}-{}",
702             BOOT_CONFIG,
703             unsafe { CStr::from_ptr(kernel.release.as_ptr() as *const c_char) }.to_string_lossy()
704         );
705 
706         if Path::new(&filename).exists() {
707             let file = FileWrapper::open(filename)?;
708             Box::new(BufReader::new(file))
709         } else {
710             let file = FileWrapper::open(BOOT_CONFIG)?;
711             Box::new(BufReader::new(file))
712         }
713     };
714 
715     let mut map = HashMap::new();
716 
717     for line in reader.lines() {
718         let line = line?;
719         if line.starts_with('#') {
720             continue;
721         }
722         if line.contains('=') {
723             let mut s = line.splitn(2, '=');
724             let name = expect!(s.next()).to_owned();
725             let value = match expect!(s.next()) {
726                 "y" => ConfigSetting::Yes,
727                 "m" => ConfigSetting::Module,
728                 s => ConfigSetting::Value(s.to_owned()),
729             };
730             map.insert(name, value);
731         }
732     }
733 
734     Ok(map)
735 }
736 
737 /// The amount of time, measured in seconds, the CPU has been in specific states
738 #[derive(Debug)]
739 pub struct CpuTime {
740     /// Seconds spent in user mode
741     pub user: f32,
742     /// Seconds spent in user mode with low priority (nice)
743     pub nice: f32,
744     /// Seconds spent in system mode
745     pub system: f32,
746     /// Seconds spent in the idle tast
747     pub idle: f32,
748     /// Seconds waiting for I/O to complete
749     ///
750     /// This value is not reliaable, for the following reasons:
751     ///
752     /// 1. The CPU will not wait for I/O to complete; iowait is the time that a
753     ///    task is waiting for I/O to complete.  When a CPU goes into idle state
754     ///    for outstanding task I/O, another task will be scheduled on this CPU.
755     ///
756     /// 2. On a multi-core CPU, this task waiting for I/O to complete is not running
757     ///    on any CPU, so the iowait for each CPU is difficult to calculate.
758     ///
759     /// 3. The value in this field may *decrease* in certain conditions.
760     ///
761     /// (Since Linux 2.5.41)
762     pub iowait: Option<f32>,
763     /// Seconds servicing interrupts
764     ///
765     /// (Since Linux 2.6.0)
766     pub irq: Option<f32>,
767     /// Seconds servicing softirqs
768     ///
769     /// (Since Linux 2.6.0)
770     pub softirq: Option<f32>,
771     /// Seconds of stolen time.
772     ///
773     /// Stolen time is the time spent in other operating systems when running in
774     /// a virtualized environemnt.
775     ///
776     /// (Since Linux 2.6.11)
777     pub steal: Option<f32>,
778     /// Seconds spent running a virtual CPU for guest operating systems under control
779     /// of the linux kernel
780     ///
781     /// (Since Linux 2.6.24)
782     pub guest: Option<f32>,
783     /// Seconds spent running a niced guest
784     ///
785     /// (Since Linux 2.6.33)
786     pub guest_nice: Option<f32>,
787 }
788 
789 impl CpuTime {
from_str(s: &str) -> ProcResult<CpuTime>790     fn from_str(s: &str) -> ProcResult<CpuTime> {
791         let mut s = s.split_whitespace();
792 
793         s.next();
794         let user = from_str!(u64, expect!(s.next())) as f32 / *TICKS_PER_SECOND as f32;
795         let nice = from_str!(u64, expect!(s.next())) as f32 / *TICKS_PER_SECOND as f32;
796         let system = from_str!(u64, expect!(s.next())) as f32 / *TICKS_PER_SECOND as f32;
797         let idle = from_str!(u64, expect!(s.next())) as f32 / *TICKS_PER_SECOND as f32;
798 
799         let iowait = s
800             .next()
801             .map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
802             .transpose()?;
803         let irq = s
804             .next()
805             .map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
806             .transpose()?;
807         let softirq = s
808             .next()
809             .map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
810             .transpose()?;
811         let steal = s
812             .next()
813             .map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
814             .transpose()?;
815         let guest = s
816             .next()
817             .map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
818             .transpose()?;
819         let guest_nice = s
820             .next()
821             .map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
822             .transpose()?;
823 
824         Ok(CpuTime {
825             user,
826             nice,
827             system,
828             idle,
829             iowait,
830             irq,
831             softirq,
832             steal,
833             guest,
834             guest_nice,
835         })
836     }
837 }
838 
839 /// Kernel/system statistics, from `/proc/stat`
840 #[derive(Debug)]
841 pub struct KernelStats {
842     /// The amount of time the system spent in various states
843     pub total: CpuTime,
844     /// The amount of time that specific CPUs spent in various states
845     pub cpu_time: Vec<CpuTime>,
846 
847     /// The number of context switches that the system underwent
848     pub ctxt: u64,
849 
850     /// Boot time, in number of seconds since the Epoch
851     pub btime: u64,
852 
853     /// Number of forks since boot
854     pub processes: u64,
855 
856     /// Number of processes in runnable state
857     ///
858     /// (Since Linux 2.5.45)
859     pub procs_running: Option<u32>,
860 
861     /// Number of processes blocked waiting for I/O
862     ///
863     /// (Since Linux 2.5.45)
864     pub procs_blocked: Option<u32>,
865 }
866 
867 impl KernelStats {
new() -> ProcResult<KernelStats>868     pub fn new() -> ProcResult<KernelStats> {
869         KernelStats::from_reader(FileWrapper::open("/proc/stat")?)
870     }
871 
from_reader<R: io::Read>(r: R) -> ProcResult<KernelStats>872     fn from_reader<R: io::Read>(r: R) -> ProcResult<KernelStats> {
873         use std::io::{BufRead, BufReader};
874 
875         let bufread = BufReader::new(r);
876         let lines = bufread.lines();
877 
878         let mut total_cpu = None;
879         let mut cpus = Vec::new();
880         let mut ctxt = None;
881         let mut btime = None;
882         let mut processes = None;
883         let mut procs_running = None;
884         let mut procs_blocked = None;
885 
886         for line in lines {
887             let line = line?;
888             if line.starts_with("cpu ") {
889                 total_cpu = Some(CpuTime::from_str(&line)?);
890             } else if line.starts_with("cpu") {
891                 cpus.push(CpuTime::from_str(&line)?);
892             } else if line.starts_with("ctxt ") {
893                 ctxt = Some(from_str!(u64, &line[5..]));
894             } else if line.starts_with("btime ") {
895                 btime = Some(from_str!(u64, &line[6..]));
896             } else if line.starts_with("processes ") {
897                 processes = Some(from_str!(u64, &line[10..]));
898             } else if line.starts_with("procs_running ") {
899                 procs_running = Some(from_str!(u32, &line[14..]));
900             } else if line.starts_with("procs_blocked ") {
901                 procs_blocked = Some(from_str!(u32, &line[14..]));
902             }
903         }
904 
905         Ok(KernelStats {
906             total: expect!(total_cpu),
907             cpu_time: cpus,
908             ctxt: expect!(ctxt),
909             btime: expect!(btime),
910             processes: expect!(processes),
911             procs_running,
912             procs_blocked,
913         })
914     }
915 }
916 
917 /// Get various vritual memory statistics
918 ///
919 /// Since the exact set of statistics will vary from kernel to kernel,
920 /// and because most of them are not well documented, this function
921 /// returns a HashMap instead of a struct.  Consult the kernel source
922 /// code for more details of this data.
923 ///
924 /// This data is taken from the `/proc/vmstat` file.
925 ///
926 /// (since Linux 2.6.0)
vmstat() -> ProcResult<HashMap<String, i64>>927 pub fn vmstat() -> ProcResult<HashMap<String, i64>> {
928     use std::io::{BufRead, BufReader};
929 
930     let file = FileWrapper::open("/proc/vmstat")?;
931     let reader = BufReader::new(file);
932     let mut map = HashMap::new();
933     for line in reader.lines() {
934         let line = line?;
935         let mut split = line.split_whitespace();
936         let name = expect!(split.next());
937         let val = from_str!(i64, expect!(split.next()));
938         map.insert(name.to_owned(), val);
939     }
940 
941     Ok(map)
942 }
943 
944 #[cfg(test)]
945 mod tests {
946     extern crate failure;
947     use super::process::Process;
948     use super::*;
949 
950     #[test]
test_statics()951     fn test_statics() {
952         println!("{:?}", *TICKS_PER_SECOND);
953         println!("{:?}", *KERNEL);
954         println!("{:?}", *PAGESIZE);
955     }
956 
957     #[test]
test_kernel_from_str()958     fn test_kernel_from_str() {
959         let k = KernelVersion::from_str("1.2.3").unwrap();
960         assert_eq!(k.major, 1);
961         assert_eq!(k.minor, 2);
962         assert_eq!(k.patch, 3);
963 
964         let k = KernelVersion::from_str("4.9.16-gentoo").unwrap();
965         assert_eq!(k.major, 4);
966         assert_eq!(k.minor, 9);
967         assert_eq!(k.patch, 16);
968     }
969 
970     #[test]
test_kernel_cmp()971     fn test_kernel_cmp() {
972         let a = KernelVersion::from_str("1.2.3").unwrap();
973         let b = KernelVersion::from_str("1.2.3").unwrap();
974         let c = KernelVersion::from_str("1.2.4").unwrap();
975         let d = KernelVersion::from_str("1.5.4").unwrap();
976         let e = KernelVersion::from_str("2.5.4").unwrap();
977 
978         assert_eq!(a, b);
979         assert!(a < c);
980         assert!(a < d);
981         assert!(a < e);
982         assert!(e > d);
983         assert!(e > c);
984         assert!(e > b);
985     }
986 
987     #[test]
test_loadavg()988     fn test_loadavg() {
989         let load = LoadAverage::new().unwrap();
990         println!("{:?}", load);
991     }
992 
993     #[test]
test_from_str() -> ProcResult<()>994     fn test_from_str() -> ProcResult<()> {
995         assert_eq!(from_str!(u8, "12"), 12);
996         assert_eq!(from_str!(u8, "A", 16), 10);
997         Ok(())
998     }
999 
1000     #[test]
test_from_str_fail()1001     fn test_from_str_fail() {
1002         fn inner() -> ProcResult<()> {
1003             let s = "four";
1004             from_str!(u8, s);
1005             unreachable!()
1006         }
1007 
1008         assert!(inner().is_err())
1009     }
1010 
1011     #[test]
test_kernel_config()1012     fn test_kernel_config() {
1013         // TRAVIS
1014         // we don't have access to the kernel_config on travis, so skip that test there
1015         match std::env::var("TRAVIS") {
1016             Ok(ref s) if s == "true" => return,
1017             _ => {}
1018         }
1019         if !Path::new(PROC_CONFIG_GZ).exists() && !Path::new(BOOT_CONFIG).exists() {
1020             return;
1021         }
1022 
1023         let config = kernel_config().unwrap();
1024         println!("{:#?}", config);
1025     }
1026 
1027     #[test]
test_file_io_errors()1028     fn test_file_io_errors() {
1029         fn inner<P: AsRef<Path>>(p: P) -> Result<(), ProcError> {
1030             let mut file = FileWrapper::open(p)?;
1031 
1032             let mut buf = [0; 128];
1033             file.read_exact(&mut buf[0..128])?;
1034 
1035             Ok(())
1036         }
1037 
1038         let err = inner("/this_should_not_exist").unwrap_err();
1039         println!("{}", err);
1040 
1041         match err {
1042             ProcError::NotFound(Some(p)) => {
1043                 assert_eq!(p, Path::new("/this_should_not_exist"));
1044             }
1045             x => panic!("Unexpected return value: {:?}", x),
1046         }
1047 
1048         match inner("/proc/loadavg") {
1049             Err(ProcError::Io(_, Some(p))) => {
1050                 assert_eq!(p, Path::new("/proc/loadavg"));
1051             }
1052             x => panic!("Unexpected return value: {:?}", x),
1053         }
1054     }
1055 
1056     /// Test that our error type can be easily used with the `failure` crate
1057     #[test]
test_failure()1058     fn test_failure() {
1059         fn inner() -> Result<(), failure::Error> {
1060             let _load = LoadAverage::new()?;
1061             Ok(())
1062         }
1063         let _ = inner();
1064 
1065         fn inner2() -> Result<(), failure::Error> {
1066             let proc = Process::new(1)?;
1067             let _io = proc.maps()?;
1068             Ok(())
1069         }
1070 
1071         let _ = inner2();
1072         // Unwrapping this failure should produce a message that looks like:
1073         // thread 'tests::test_failure' panicked at 'called `Result::unwrap()` on an `Err` value: PermissionDenied(Some("/proc/1/maps"))', src/libcore/result.rs:997:5
1074     }
1075 
1076     #[test]
test_nopanic()1077     fn test_nopanic() {
1078         fn _inner() -> ProcResult<bool> {
1079             let x: Option<bool> = None;
1080             let y: bool = expect!(x);
1081             Ok(y)
1082         }
1083 
1084         let r = _inner();
1085         println!("{:?}", r);
1086         assert!(r.is_err());
1087 
1088         fn _inner2() -> ProcResult<bool> {
1089             let _f: std::fs::File = expect!(std::fs::File::open("/doesnotexist"));
1090             Ok(true)
1091         }
1092 
1093         let r = _inner2();
1094         println!("{:?}", r);
1095         assert!(r.is_err());
1096     }
1097 
1098     #[cfg(feature = "backtrace")]
1099     #[test]
test_backtrace()1100     fn test_backtrace() {
1101         fn _inner() -> ProcResult<bool> {
1102             let _f: std::fs::File = expect!(std::fs::File::open("/doesnotexist"));
1103             Ok(true)
1104         }
1105 
1106         let r = _inner();
1107         println!("{:?}", r);
1108     }
1109 
1110     #[test]
test_kernel_stat()1111     fn test_kernel_stat() {
1112         let stat = KernelStats::new().unwrap();
1113         println!("{:#?}", stat);
1114 
1115         // the boottime from KernelStats should match the boottime from /proc/uptime
1116         let boottime = boot_time_secs().unwrap();
1117 
1118         let diff = (boottime as i32 - stat.btime as i32).abs();
1119         assert!(diff <= 1);
1120 
1121         let cpuinfo = CpuInfo::new().unwrap();
1122         assert_eq!(cpuinfo.num_cores(), stat.cpu_time.len());
1123 
1124         // the sum of each individual CPU should be equal to the total cpu entry
1125         // note: on big machines with 128 cores, it seems that the differences can be rather high,
1126         // especially when heavily loaded.  So this test tollerates a 60-second discrepency
1127 
1128         let user: f32 = stat.cpu_time.iter().map(|i| i.user).sum();
1129         let nice: f32 = stat.cpu_time.iter().map(|i| i.nice).sum();
1130         let system: f32 = stat.cpu_time.iter().map(|i| i.system).sum();
1131         assert!(
1132             (stat.total.user - user).abs() < 60.0,
1133             "sum:{} total:{} diff:{}",
1134             stat.total.user,
1135             user,
1136             stat.total.user - user
1137         );
1138         assert!(
1139             (stat.total.nice - nice).abs() < 60.0,
1140             "sum:{} total:{} diff:{}",
1141             stat.total.nice,
1142             nice,
1143             stat.total.nice - nice
1144         );
1145         assert!(
1146             (stat.total.system - system).abs() < 60.0,
1147             "sum:{} total:{} diff:{}",
1148             stat.total.system,
1149             system,
1150             stat.total.system - system
1151         );
1152 
1153         let diff = stat.total.idle - stat.cpu_time.iter().map(|i| i.idle).sum::<f32>().abs();
1154         assert!(diff < 10.0, "idle time difference too high: {}", diff);
1155     }
1156 
1157     #[test]
test_vmstat()1158     fn test_vmstat() {
1159         let stat = vmstat().unwrap();
1160         println!("{:?}", stat);
1161     }
1162 }
1163