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