├── src ├── net │ ├── mod.rs │ └── dev.rs ├── sys │ ├── mod.rs │ └── fs │ │ ├── mod.rs │ │ └── file_max.rs ├── lib.rs ├── pid │ ├── cwd.rs │ ├── mod.rs │ ├── statm.rs │ ├── mountinfo.rs │ ├── limits.rs │ ├── stat.rs │ └── status.rs ├── loadavg.rs └── parsers.rs ├── .gitignore ├── .travis.yml ├── default.toml ├── Cargo.toml ├── LICENSE-MIT ├── README.md └── LICENSE-APACHE /src/net/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dev; 2 | -------------------------------------------------------------------------------- /src/sys/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod fs; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /src/sys/fs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod file_max; 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - 1.13.0 5 | - stable 6 | - nightly 7 | 8 | os: 9 | - linux 10 | 11 | script: 12 | - cargo build --verbose 13 | - if [[ $TRAVIS_RUST_VERSION = nightly* ]]; then 14 | env RUST_BACKTRACE=1 cargo test -v; 15 | fi 16 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "1000"] 2 | #![cfg_attr(rustc_nightly, feature(test))] 3 | 4 | #![doc(html_root_url = "https://docs.rs/procinfo/0.4.2")] 5 | 6 | #![allow(dead_code)] // TODO: remove 7 | 8 | #[macro_use] 9 | extern crate nom; 10 | 11 | extern crate byteorder; 12 | extern crate libc; 13 | 14 | #[macro_use] 15 | mod parsers; 16 | 17 | mod loadavg; 18 | pub mod pid; 19 | pub mod sys; 20 | pub mod net; 21 | 22 | pub use loadavg::{LoadAvg, loadavg}; 23 | -------------------------------------------------------------------------------- /default.toml: -------------------------------------------------------------------------------- 1 | max_width = 100 2 | ideal_width = 80 3 | leeway = 5 4 | tab_spaces = 4 5 | newline_style = "Unix" 6 | fn_brace_style = "SameLineWhere" 7 | fn_return_indent = "WithArgs" 8 | fn_args_paren_newline = true 9 | struct_trailing_comma = "Vertical" 10 | struct_lit_style = "BlockIndent" 11 | struct_lit_trailing_comma = "Vertical" 12 | enum_trailing_comma = true 13 | report_todo = "Always" 14 | report_fixme = "Never" 15 | reorder_imports = true 16 | expr_indent_style = "Tabbed" 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "procinfo" 3 | # NB: When modifying, also modify html_root_url in lib.rs. 4 | version = "0.4.2" 5 | authors = ["Dan Burkert "] 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/danburkert/procinfo-rs" 8 | documentation = "https://docs.rs/procinfo" 9 | description = "A library for accessing Linux process and system information" 10 | keywords = ["proc", "process", "system", "Linux"] 11 | exclude = [ 12 | ".gitignore", 13 | ".travis.yml", 14 | ] 15 | build = "build.rs" 16 | 17 | [badges] 18 | travis-ci = { repository = "danburkert/procinfo-rs" } 19 | 20 | [dependencies] 21 | libc = "0.2" 22 | nom = { version = "2", features = ["verbose-errors"] } 23 | byteorder = "1.0" 24 | 25 | [build-dependencies] 26 | rustc_version = "0.2" 27 | -------------------------------------------------------------------------------- /src/pid/cwd.rs: -------------------------------------------------------------------------------- 1 | //! Concerning the current working directory of a process, from 2 | //! `/proc/[pid]/cwd`. 3 | 4 | use std::fs; 5 | use std::io::Result; 6 | use std::path::PathBuf; 7 | 8 | use libc::pid_t; 9 | 10 | /// Gets path of current working directory for the process with the provided 11 | /// pid. 12 | pub fn cwd(pid: pid_t) -> Result { 13 | fs::read_link(format!("/proc/{}/cwd", pid)) 14 | } 15 | 16 | /// Gets path of current working directory for the current process. 17 | pub fn cwd_self() -> Result { 18 | fs::read_link("/proc/self/cwd") 19 | } 20 | 21 | #[cfg(test)] 22 | pub mod tests { 23 | use super::cwd_self; 24 | use std::env; 25 | 26 | #[test] 27 | fn test_cwd_self() { 28 | assert_eq!(env::current_dir().unwrap(), cwd_self().unwrap()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/sys/fs/file_max.rs: -------------------------------------------------------------------------------- 1 | //! Retreive the file-max value from /proc/sys/fs/file-max 2 | 3 | use std::fs::File; 4 | use std::io::Result; 5 | 6 | use parsers::{map_result, parse_u64, read_to_end}; 7 | use nom::eol; 8 | 9 | /// Path to the file-max value 10 | static FILE_MAX_PATH: &'static str = "/proc/sys/fs/file-max"; 11 | 12 | // Linux kernel uses get_max_files() which returns an unsigned long 13 | // see include/linux/fs.h 14 | 15 | named!(parse_file_max, 16 | do_parse!(max: parse_u64 >> eol >> (max)) 17 | ); 18 | 19 | /// Get file-max value for the current system 20 | pub fn file_max() -> Result { 21 | let mut buf = [0;32]; 22 | let mut file = try!(File::open(FILE_MAX_PATH)); 23 | map_result(parse_file_max(try!(read_to_end(&mut file, &mut buf)))) 24 | } 25 | 26 | #[cfg(test)] 27 | pub mod tests { 28 | use super::file_max; 29 | 30 | #[test] 31 | fn test_file_max() { 32 | let max = file_max(); 33 | assert_eq!(max.is_ok(), true); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # procinfo 2 | 3 | [![Build Status](https://travis-ci.org/danburkert/procinfo-rs.svg?branch=master)](https://travis-ci.org/danburkert/procinfo-rs) 4 | 5 | [Documentation](https://docs.rs/procinfo) 6 | 7 | A Rust library for reading information from `/proc`, the Linux process 8 | information psuedo-filesystem. `procinfo` provides a simple interface for inspecting 9 | process and system information on Linux. 10 | 11 | ## Status 12 | 13 | The goal is that `procinfo` will provide interfaces for all of the files in `/proc`, 14 | currently the following interfaces are provided: 15 | 16 | * `/proc/loadavg` 17 | * `/proc//cwd` 18 | * `/proc//limits` 19 | * `/proc//mountinfo` 20 | * `/proc//stat` 21 | * `/proc//statm` 22 | * `/proc//status` 23 | * `/proc/sys/fs/file-max` 24 | * `/proc/net/dev` 25 | 26 | `procinfo` requires Rust 1.13 or later. 27 | 28 | ## Contributing 29 | 30 | Contributions will be gladly accepted for new `/proc` file parsers. In addition 31 | to parsers, help is needed testing `procinfo` on uncommon, old, bleeding edge, 32 | containerized, and namespaced kernels. If you find that any of the documentation 33 | is misleading, incomplete, or insufficient, please file an issue! 34 | 35 | ## License 36 | 37 | `procinfo` is primarily distributed under the terms of both the MIT license and the 38 | Apache License (Version 2.0). 39 | 40 | See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT) for details. 41 | 42 | Copyright (c) 2015 Dan Burkert. 43 | -------------------------------------------------------------------------------- /src/pid/mod.rs: -------------------------------------------------------------------------------- 1 | //! Process-specific information from `/proc/[pid]/`. 2 | 3 | mod cwd; 4 | mod limits; 5 | mod mountinfo; 6 | mod stat; 7 | mod statm; 8 | mod status; 9 | 10 | pub use pid::cwd::{cwd, cwd_self}; 11 | pub use pid::limits::{Limit, Limits, limits, limits_self}; 12 | pub use pid::mountinfo::{Mountinfo, mountinfo, mountinfo_self}; 13 | pub use pid::statm::{Statm, statm, statm_self}; 14 | pub use pid::status::{SeccompMode, Status, status, status_self}; 15 | pub use pid::stat::{Stat, stat, stat_self}; 16 | 17 | /// The state of a process. 18 | #[derive(Debug, PartialEq, Eq, Hash)] 19 | pub enum State { 20 | /// Running. 21 | Running, 22 | /// Sleeping in an interruptible wait. 23 | Sleeping, 24 | /// Waiting in uninterruptible disk sleep. 25 | Waiting, 26 | /// Zombie. 27 | Zombie, 28 | /// Stopped (on a signal) or (before Linux 2.6.33) trace stopped. 29 | Stopped, 30 | /// trace stopped. 31 | /// 32 | /// Linux 2.6.33 onward. 33 | TraceStopped, 34 | /// Paging. 35 | /// 36 | /// Only before linux 2.6.0. 37 | Paging, 38 | /// Dead. 39 | /// 40 | /// Linux 2.6.33 to 3.13 only. 41 | Dead, 42 | /// Wakekill. 43 | /// 44 | /// Linux 2.6.33 to 3.13 only. 45 | Wakekill, 46 | /// Waking. 47 | /// 48 | /// Linux 2.6.33 to 3.13 only. 49 | Waking, 50 | /// Parked. 51 | /// 52 | /// Linux 3.9 to 3.13 only. 53 | Parked, 54 | } 55 | 56 | impl Default for State { 57 | fn default() -> State { 58 | State::Running 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/loadavg.rs: -------------------------------------------------------------------------------- 1 | //! System load and task statistics from `/proc/loadavg`. 2 | 3 | use std::fs::File; 4 | use std::io::Result; 5 | 6 | use libc::pid_t; 7 | use nom::{line_ending, space}; 8 | 9 | use parsers::{map_result, parse_f32, parse_i32, parse_u32, read_to_end}; 10 | 11 | /// System load and task statistics. 12 | /// 13 | /// The load average is the ratio of runnable and uninterruptible (waiting on IO) tasks to total 14 | /// tasks on the system. 15 | /// 16 | /// See `man 5 proc` and `Linux/fs/proc/loadavg.c`. 17 | #[derive(Debug, Default, PartialEq)] 18 | pub struct LoadAvg { 19 | /// Load average over the last minute. 20 | pub load_avg_1_min: f32, 21 | /// Load average of the last 5 minutes. 22 | pub load_avg_5_min: f32, 23 | /// Load average of the last 10 minutes 24 | pub load_avg_10_min: f32, 25 | /// the number of currently runnable kernel scheduling entities (processes, threads). 26 | pub tasks_runnable: u32, 27 | /// the number of kernel scheduling entities that currently exist on the system. 28 | pub tasks_total: u32, 29 | /// the PID of the process that was most recently created on the system. 30 | pub last_created_pid: pid_t, 31 | } 32 | 33 | /// Parses the loadavg file format. 34 | named!(parse_loadavg, 35 | chain!(load_avg_1_min: parse_f32 ~ space ~ 36 | load_avg_5_min: parse_f32 ~ space ~ 37 | load_avg_10_min: parse_f32 ~ space ~ 38 | tasks_runnable: parse_u32 ~ tag!("/") ~ 39 | tasks_total: parse_u32 ~ space ~ 40 | last_created_pid: parse_i32 ~ line_ending, 41 | || { LoadAvg { load_avg_1_min: load_avg_1_min, 42 | load_avg_5_min: load_avg_5_min, 43 | load_avg_10_min: load_avg_10_min, 44 | tasks_runnable: tasks_runnable, 45 | tasks_total: tasks_total, 46 | last_created_pid: last_created_pid } })); 47 | 48 | /// Returns the system load average. 49 | pub fn loadavg() -> Result { 50 | let mut buf = [0; 128]; // A typical loadavg file is about 32 bytes. 51 | let mut file = try!(File::open("/proc/loadavg")); 52 | map_result(parse_loadavg(try!(read_to_end(&mut file, &mut buf)))) 53 | } 54 | 55 | #[cfg(test)] 56 | mod tests { 57 | use super::{loadavg, parse_loadavg}; 58 | use parsers::tests::unwrap; 59 | 60 | /// Test that the system loadavg file can be parsed. 61 | #[test] 62 | fn test_loadavg() { 63 | loadavg().unwrap(); 64 | } 65 | 66 | #[test] 67 | fn test_parse_loadavg() { 68 | let loadavg_text = b"0.46 0.33 0.28 34/625 8435\n"; 69 | let loadavg = unwrap(parse_loadavg(loadavg_text)); 70 | assert_eq!(0.46, loadavg.load_avg_1_min); 71 | assert_eq!(0.33, loadavg.load_avg_5_min); 72 | assert_eq!(0.28, loadavg.load_avg_10_min); 73 | assert_eq!(34, loadavg.tasks_runnable); 74 | assert_eq!(625, loadavg.tasks_total); 75 | assert_eq!(8435, loadavg.last_created_pid); 76 | } 77 | } 78 | 79 | #[cfg(all(test, rustc_nightly))] 80 | mod benches { 81 | extern crate test; 82 | 83 | use std::fs::File; 84 | 85 | use parsers::read_to_end; 86 | use super::{loadavg, parse_loadavg}; 87 | 88 | #[bench] 89 | fn bench_loadavg(b: &mut test::Bencher) { 90 | b.iter(|| test::black_box(loadavg())); 91 | } 92 | 93 | #[bench] 94 | fn bench_loadavg_parse(b: &mut test::Bencher) { 95 | let mut buf = [0; 128]; 96 | let statm = read_to_end(&mut File::open("/proc/loadavg").unwrap(), &mut buf).unwrap(); 97 | b.iter(|| test::black_box(parse_loadavg(statm))); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/pid/statm.rs: -------------------------------------------------------------------------------- 1 | //! Process memory usage information from `/proc/[pid]/statm`. 2 | 3 | use std::fs::File; 4 | use std::io::Result; 5 | 6 | use libc::pid_t; 7 | use nom::{digit, line_ending, space}; 8 | 9 | use parsers::{map_result, parse_usize, read_to_end}; 10 | 11 | /// Process memory usage information. 12 | /// 13 | /// All values are in units of pages. 14 | /// 15 | /// See `man 5 proc` and `Linux/fs/proc/array.c`. 16 | #[derive(Debug, Default, PartialEq, Eq, Hash)] 17 | pub struct Statm { 18 | /// Total virtual memory size. 19 | pub size: usize, 20 | /// Resident non-swapped memory. 21 | pub resident: usize, 22 | /// Shared memory. 23 | pub share: usize, 24 | /// Resident executable memory. 25 | pub text: usize, 26 | /// Resident data and stack memory. 27 | pub data: usize, 28 | } 29 | 30 | /// Parses the statm file format. 31 | named!(parse_statm, 32 | chain!(size: parse_usize ~ space ~ 33 | resident: parse_usize ~ space ~ 34 | share: parse_usize ~ space ~ 35 | text: parse_usize ~ space ~ 36 | digit ~ space ~ // lib - unused since linux 2.6 37 | data: parse_usize ~ space ~ 38 | digit ~ line_ending, // dt - unused since linux 2.6 39 | || { Statm { size: size, 40 | resident: resident, 41 | share: share, 42 | text: text, 43 | data: data } })); 44 | 45 | /// Parses the provided statm file. 46 | fn statm_file(file: &mut File) -> Result { 47 | let mut buf = [0; 256]; // A typical statm file is about 25 bytes 48 | map_result(parse_statm(try!(read_to_end(file, &mut buf)))) 49 | } 50 | 51 | /// Returns memory status information for the process with the provided pid. 52 | pub fn statm(pid: pid_t) -> Result { 53 | statm_file(&mut try!(File::open(&format!("/proc/{}/statm", pid)))) 54 | } 55 | 56 | /// Returns memory status information for the current process. 57 | pub fn statm_self() -> Result { 58 | statm_file(&mut try!(File::open("/proc/self/statm"))) 59 | } 60 | 61 | /// Returns memory status information from the thread with the provided parent process ID and thread ID. 62 | pub fn statm_task(process_id: pid_t, thread_id: pid_t) -> Result { 63 | statm_file(&mut try!(File::open(&format!("/proc/{}/task/{}/statm", process_id, thread_id)))) 64 | } 65 | 66 | #[cfg(test)] 67 | mod tests { 68 | use parsers::tests::unwrap; 69 | use super::{parse_statm, statm, statm_self}; 70 | 71 | /// Test that the system statm files can be parsed. 72 | #[test] 73 | fn test_statm() { 74 | statm_self().unwrap(); 75 | statm(1).unwrap(); 76 | } 77 | 78 | #[test] 79 | fn test_parse_statm() { 80 | let statm_text = b"11837 2303 1390 330 0 890 0\n"; 81 | let statm = unwrap(parse_statm(statm_text)); 82 | assert_eq!(11837, statm.size); 83 | assert_eq!(2303, statm.resident); 84 | assert_eq!(1390, statm.share); 85 | assert_eq!(330, statm.text); 86 | assert_eq!(890, statm.data); 87 | } 88 | } 89 | 90 | #[cfg(all(test, rustc_nightly))] 91 | mod benches { 92 | extern crate test; 93 | 94 | use std::fs::File; 95 | 96 | use parsers::read_to_end; 97 | use super::{parse_statm, statm}; 98 | 99 | #[bench] 100 | fn bench_statm(b: &mut test::Bencher) { 101 | b.iter(|| test::black_box(statm(1))); 102 | } 103 | 104 | #[bench] 105 | fn bench_statm_parse(b: &mut test::Bencher) { 106 | let mut buf = [0; 256]; 107 | let statm = read_to_end(&mut File::open("/proc/1/statm").unwrap(), &mut buf).unwrap(); 108 | b.iter(|| test::black_box(parse_statm(statm))); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/net/dev.rs: -------------------------------------------------------------------------------- 1 | //! Network device information from `/proc/net/dev`. 2 | 3 | use std::fs::File; 4 | use std::io::{Read, Result}; 5 | 6 | use nom::{space, line_ending}; 7 | use parsers::{ 8 | map_result, 9 | parse_u64, 10 | }; 11 | 12 | const NET_DEV_FILE: &'static str = "/proc/net/dev"; 13 | 14 | /// Network device status information. 15 | /// 16 | /// See `man 5 proc` and `Linux/net/core/net-procfs.c` 17 | pub struct DeviceStatus { 18 | /// Name of the interface representing this device. 19 | pub interface: String, 20 | 21 | /// Number of received bytes. 22 | pub receive_bytes: u64, 23 | /// Number of received packets. 24 | pub receive_packets: u64, 25 | /// Number of bad packets received. 26 | pub receive_errs: u64, 27 | /// Number of incoming packets dropped. 28 | pub receive_drop: u64, 29 | /// Number of incoming packets dropped due to fifo overrun. 30 | pub receive_fifo: u64, 31 | /// Number of incoming packets dropped due to frame alignment errors. 32 | pub receive_frame: u64, 33 | /// Number of CSLIP packets received. 34 | pub receive_compressed: u64, 35 | /// Number of multicast packets received. 36 | pub receive_multicast: u64, 37 | 38 | /// Number of transmitted bytes. 39 | pub transmit_bytes: u64, 40 | /// Number of transmitted packets. 41 | pub transmit_packets: u64, 42 | /// Number of occurred transmission problems. 43 | pub transmit_errs: u64, 44 | /// Number of outgoing packets dropped. 45 | pub transmit_drop: u64, 46 | /// Number of outgoing packets dropped due to fifo overrun. 47 | pub transmit_fifo: u64, 48 | /// Number of occurred packet collisions. 49 | pub transmit_colls: u64, 50 | /// Number of occurred carrier errors. 51 | pub transmit_carrier: u64, 52 | /// Number of CSLIP packets transmitted. 53 | pub transmit_compressed: u64, 54 | } 55 | 56 | named!(interface_stats, 57 | do_parse!( 58 | opt!(space) >> 59 | interface: take_until_and_consume!(":") >> 60 | space >> 61 | receive_bytes: terminated!(parse_u64, space) >> 62 | receive_packets: terminated!(parse_u64, space) >> 63 | receive_errs: terminated!(parse_u64, space) >> 64 | receive_drop: terminated!(parse_u64, space) >> 65 | receive_fifo: terminated!(parse_u64, space) >> 66 | receive_frame: terminated!(parse_u64, space) >> 67 | receive_compressed: terminated!(parse_u64, space) >> 68 | receive_multicast: terminated!(parse_u64, space) >> 69 | transmit_bytes: terminated!(parse_u64, space) >> 70 | transmit_packets: terminated!(parse_u64, space) >> 71 | transmit_errs: terminated!(parse_u64, space) >> 72 | transmit_drop: terminated!(parse_u64, space) >> 73 | transmit_fifo: terminated!(parse_u64, space) >> 74 | transmit_colls: terminated!(parse_u64, space) >> 75 | transmit_carrier: terminated!(parse_u64, space) >> 76 | transmit_compressed: parse_u64 >> 77 | (DeviceStatus { 78 | interface: String::from_utf8_lossy(interface).to_string(), 79 | receive_bytes: receive_bytes, 80 | receive_packets: receive_packets, 81 | receive_errs: receive_errs, 82 | receive_drop: receive_drop, 83 | receive_fifo: receive_fifo, 84 | receive_frame: receive_frame, 85 | receive_compressed: receive_compressed, 86 | receive_multicast: receive_multicast, 87 | transmit_bytes: transmit_bytes, 88 | transmit_packets: transmit_packets, 89 | transmit_errs: transmit_errs, 90 | transmit_drop: transmit_drop, 91 | transmit_fifo: transmit_fifo, 92 | transmit_colls: transmit_colls, 93 | transmit_carrier: transmit_carrier, 94 | transmit_compressed: transmit_compressed, 95 | }))); 96 | 97 | named!(interface_list< Vec >, 98 | do_parse!( 99 | interfaces: separated_list!(line_ending, interface_stats) >> 100 | line_ending >> 101 | (interfaces))); 102 | 103 | named!(empty_list< Vec >, 104 | value!(Vec::new(), eof!())); 105 | 106 | named!(parse_dev< Vec >, 107 | do_parse!( 108 | count!(take_until_and_consume!("\n"), 2) >> 109 | interfaces: alt_complete!(interface_list | empty_list) >> 110 | (interfaces))); 111 | 112 | /// Returns list of all network devices and information about their state. 113 | pub fn dev() -> Result> { 114 | let mut file = File::open(NET_DEV_FILE)?; 115 | 116 | let mut buffer = vec![]; 117 | file.read_to_end(&mut buffer)?; 118 | 119 | map_result(parse_dev(buffer.as_slice())) 120 | } 121 | 122 | #[cfg(test)] 123 | mod test { 124 | use super::{dev, parse_dev}; 125 | use parsers::map_result; 126 | 127 | #[test] 128 | fn two_interfaces() { 129 | let file = br#"Inter-| Receive | Transmit 130 | face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed 131 | lo: 206950 2701 0 0 0 0 0 0 206950 2701 0 0 0 0 0 0 132 | wlp58s0: 631994599 596110 0 1 0 0 0 0 47170335 384943 0 0 0 0 0 0 133 | "#; 134 | let interfaces = map_result(parse_dev(file)).unwrap(); 135 | 136 | assert!(interfaces.len() == 2); 137 | 138 | assert!(interfaces[0].interface == "lo"); 139 | assert!(interfaces[0].receive_bytes == 206950); 140 | assert!(interfaces[0].receive_packets == 2701); 141 | assert!(interfaces[0].receive_errs == 0); 142 | assert!(interfaces[0].receive_drop == 0); 143 | assert!(interfaces[0].receive_fifo == 0); 144 | assert!(interfaces[0].receive_frame == 0); 145 | assert!(interfaces[0].receive_compressed == 0); 146 | assert!(interfaces[0].receive_multicast == 0); 147 | assert!(interfaces[0].transmit_bytes == 206950); 148 | assert!(interfaces[0].transmit_packets == 2701); 149 | assert!(interfaces[0].transmit_errs == 0); 150 | assert!(interfaces[0].transmit_drop == 0); 151 | assert!(interfaces[0].transmit_fifo == 0); 152 | assert!(interfaces[0].transmit_colls == 0); 153 | assert!(interfaces[0].transmit_carrier == 0); 154 | 155 | assert!(interfaces[1].interface == "wlp58s0"); 156 | assert!(interfaces[1].receive_bytes == 631994599); 157 | assert!(interfaces[1].receive_packets == 596110); 158 | assert!(interfaces[1].receive_errs == 0); 159 | assert!(interfaces[1].receive_drop == 1); 160 | assert!(interfaces[1].receive_fifo == 0); 161 | assert!(interfaces[1].receive_frame == 0); 162 | assert!(interfaces[1].receive_multicast == 0); 163 | assert!(interfaces[1].transmit_bytes == 47170335); 164 | assert!(interfaces[1].transmit_packets == 384943); 165 | assert!(interfaces[1].transmit_errs == 0); 166 | assert!(interfaces[1].transmit_drop == 0); 167 | assert!(interfaces[1].transmit_fifo == 0); 168 | assert!(interfaces[1].transmit_colls == 0); 169 | assert!(interfaces[1].transmit_carrier == 0); 170 | assert!(interfaces[1].transmit_compressed == 0); 171 | } 172 | 173 | #[test] 174 | fn no_interfaces() { 175 | let file = br#"Inter-| Receive | Transmit 176 | face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed 177 | "#; 178 | let interfaces = map_result(parse_dev(file)).unwrap(); 179 | assert!(interfaces.len() == 0); 180 | } 181 | 182 | #[test] 183 | fn parse_native() { 184 | dev().unwrap(); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/pid/mountinfo.rs: -------------------------------------------------------------------------------- 1 | //! Information about mounts from `/proc/[pid]/mountinfo`. 2 | 3 | use std::fs::File; 4 | use std::io::{BufRead, BufReader, Result}; 5 | use std::path::PathBuf; 6 | use std::str::{self, FromStr}; 7 | use std::io::{Error, ErrorKind}; 8 | 9 | use libc::pid_t; 10 | use nom::{Err, IResult, Needed}; 11 | use nom::ErrorKind::Tag; 12 | 13 | use parsers::{map_result, parse_isize, parse_usize}; 14 | 15 | /// Process mounts information. 16 | /// 17 | /// See `proc(5)` for format details. 18 | #[derive(Debug, PartialEq, Eq, Hash)] 19 | pub struct Mountinfo { 20 | /// Unique ID for the mount. 21 | pub mount_id: isize, 22 | /// ID of the parent mount. 23 | pub parent_id: isize, 24 | /// Device major ID (class). 25 | pub major: usize, 26 | /// Device minor ID (instance). 27 | pub minor: usize, 28 | /// Pathname which forms the root of this mount. 29 | pub root: PathBuf, 30 | /// Mount pathname relative to the process's root. 31 | pub mount_point: PathBuf, 32 | /// mount options. 33 | pub mount_options: Vec, 34 | /// Optional fields (tag with optional value). 35 | pub opt_fields: Vec, 36 | /// Filesystem type (main type with optional sub-type). 37 | pub fs_type: (String, Option), 38 | /// Filesystem specific information. 39 | pub mount_src: Option, 40 | /// Superblock options. 41 | pub super_opts: Vec, 42 | } 43 | 44 | /// Mountinfo optional field 45 | /// 46 | /// See `proc(5)` and `mount_namespace(7)` for more details. 47 | #[derive(Debug, PartialEq, Eq, Hash)] 48 | pub enum OptionalField { 49 | /// A mount shared in peer group `ID` 50 | Shared(usize), 51 | /// A mount which is a slave of shared peer group `ID` 52 | Master(usize), 53 | /// A slave mount which receives propagation events from 54 | /// shared peer group `ID` 55 | PropagateFrom(usize), 56 | /// An unbindable mount 57 | Unbindable, 58 | /// A private mount 59 | Private 60 | } 61 | 62 | /// Mountpoint option 63 | /// 64 | /// See `mount(8)` for more details. 65 | #[derive(Debug, PartialEq, Eq, Hash)] 66 | pub enum MountOption { 67 | /// Do not update inode access time 68 | Noatime, 69 | /// Do not interpret special device files 70 | Nodev, 71 | /// Do not update directory inode access time 72 | Nodiratime, 73 | /// No direct binary execution 74 | Noexec, 75 | /// Do not allow suid and sgid bits effects 76 | Nosuid, 77 | /// Conditionally update inode access time 78 | Relatime, 79 | /// Read-only 80 | Ro, 81 | /// Read-write 82 | Rw, 83 | /// Other custom options 84 | Other(String), 85 | } 86 | 87 | /// Consumes a space, main fields separator and optional fields separator 88 | named!(space, tag!(" ")); 89 | 90 | /// Consumes an hypen, the optional fields terminator 91 | named!(hypen, tag!("-")); 92 | 93 | /// Consumes a colon, the major-minor separator 94 | named!(colon, tag!(":")); 95 | 96 | /// Consumes a dot, the fs sub-type separator 97 | named!(dot, tag!(".")); 98 | 99 | /// Parses a space-terminated string field in a mountinfo entry 100 | named!(parse_string_field, 101 | map_res!(map_res!(is_not!(" "), str::from_utf8), FromStr::from_str)); 102 | 103 | 104 | /// Parses a string of optional fields. 105 | fn mount_options(opts: String) -> Vec { 106 | opts.split(",").map(|o| 107 | match o { 108 | "noatime" => MountOption::Noatime, 109 | "nodev" => MountOption::Nodev, 110 | "nodiratime" => MountOption::Nodiratime, 111 | "noexec" => MountOption::Noexec, 112 | "nosuid" => MountOption::Nosuid, 113 | "relatime" => MountOption::Relatime, 114 | "ro" => MountOption::Ro, 115 | "rw" => MountOption::Rw, 116 | x => MountOption::Other(x.into()), 117 | } 118 | ).collect() 119 | } 120 | 121 | /// Parses a comma-separated list of mount options. 122 | named!(parse_mnt_options >, 123 | do_parse!(token: parse_string_field >> 124 | (mount_options(token)) 125 | ) 126 | ); 127 | 128 | /// Parses a string of optional fields. 129 | fn opt_fields(fs: &str) -> Result> { 130 | let mut v = Vec::new(); 131 | 132 | for i in fs.split_terminator(' ') { 133 | let t: Vec<&str> = i.split(':').collect(); 134 | if t.len() > 2 { 135 | return Err(Error::new(ErrorKind::InvalidInput, "too many colons")); 136 | } 137 | match (t.get(0), t.get(1)) { 138 | (Some(&"shared"), Some(x)) if usize::from_str(x).is_ok() => 139 | v.push(OptionalField::Shared(usize::from_str(x).unwrap())), 140 | (Some(&"master"), Some(x)) if usize::from_str(x).is_ok() => 141 | v.push(OptionalField::Master(usize::from_str(x).unwrap())), 142 | (Some(&"propagate_from"), Some(x)) if usize::from_str(x).is_ok() => 143 | v.push(OptionalField::PropagateFrom(usize::from_str(x).unwrap())), 144 | (Some(&"unbindable"), None) => 145 | v.push(OptionalField::Unbindable), 146 | (_, _) => return Err(Error::new(ErrorKind::InvalidInput, "invalid optional value")), 147 | }; 148 | } 149 | 150 | if v.len() == 0 { 151 | v.push(OptionalField::Private); 152 | } 153 | 154 | Ok(v) 155 | } 156 | 157 | /// Parses a space-separated list of tag:value optional fields. 158 | fn parse_opt_fields(input: &[u8]) -> IResult<&[u8], Vec> { 159 | // look for the mandatory terminator (hypen) 160 | let mut hypen = None; 161 | for idx in 0..input.len() { 162 | if '-' as u8 == input[idx] { 163 | hypen = Some(idx); 164 | break 165 | } 166 | } 167 | if hypen.is_none() { 168 | return IResult::Incomplete(Needed::Unknown); 169 | } 170 | 171 | // parse all optional fields 172 | let term = hypen.unwrap(); 173 | let fs = str::from_utf8(&input[0..term]); 174 | match fs { 175 | Err(_) => IResult::Error(Err::Position(Tag, input)), 176 | Ok(f) => match opt_fields(f) { 177 | Err(_) => IResult::Error(Err::Position(Tag, input)), 178 | Ok(r) => IResult::Done(&input[term..], r), 179 | } 180 | } 181 | } 182 | 183 | /// Parses a fs type label, with optional dotted sub-type. 184 | named!(parse_fs_type<(String, Option)>, 185 | do_parse!(k: map_res!(map_res!(take_until_either!(" ."), str::from_utf8), FromStr::from_str) >> 186 | v: opt!(do_parse!(dot >> s: parse_string_field >> (s))) >> 187 | (k, v) 188 | ) 189 | ); 190 | 191 | /// Parses a mount source. 192 | named!(parse_mount_src >, 193 | do_parse!(src: parse_string_field >> 194 | (if src == "none" { None } else { Some(src) }) 195 | ) 196 | ); 197 | 198 | /// Parses a comma-separated list of options. 199 | named!(parse_options >, 200 | do_parse!(token: parse_string_field >> 201 | (token.split(",").map(|s| s.into()).collect()) 202 | ) 203 | ); 204 | 205 | /// Parses a mountpoint entry according to mountinfo file format. 206 | named!(parse_mountinfo_entry, 207 | do_parse!(mount_id: parse_isize >> space >> 208 | parent_id: parse_isize >> space >> 209 | major: parse_usize >> colon >> 210 | minor: parse_usize >> space >> 211 | root: parse_string_field >> space >> 212 | mount_point: parse_string_field >> space >> 213 | mount_options: parse_mnt_options >> space >> 214 | opt_fields: parse_opt_fields >> hypen >> space >> 215 | fs_type: parse_fs_type >> space >> 216 | mount_src: parse_mount_src >> space >> 217 | super_opts: parse_options >> 218 | ( Mountinfo { 219 | mount_id: mount_id, 220 | parent_id: parent_id, 221 | major: major, 222 | minor: minor, 223 | root: root.into(), 224 | mount_point: mount_point.into(), 225 | mount_options: mount_options, 226 | opt_fields: opt_fields, 227 | fs_type: fs_type, 228 | mount_src: mount_src, 229 | super_opts: super_opts, 230 | } ))); 231 | 232 | /// Parses the provided mountinfo file. 233 | fn mountinfo_file(file: &mut File) -> Result> { 234 | let mut r = Vec::new(); 235 | for line in BufReader::new(file).lines() { 236 | let mi = try!(map_result(parse_mountinfo_entry(try!(line).as_bytes()))); 237 | r.push(mi); 238 | } 239 | Ok(r) 240 | } 241 | 242 | /// Returns mounts information for the process with the provided pid. 243 | pub fn mountinfo(pid: pid_t) -> Result> { 244 | mountinfo_file(&mut try!(File::open(&format!("/proc/{}/mountinfo", pid)))) 245 | } 246 | 247 | /// Returns mounts information for the current process. 248 | pub fn mountinfo_self() -> Result> { 249 | mountinfo_file(&mut try!(File::open("/proc/self/mountinfo"))) 250 | } 251 | 252 | /// Returns mounts information from the thread with the provided parent process ID and thread ID. 253 | pub fn mountinfo_task(process_id: pid_t, thread_id: pid_t) -> Result> { 254 | mountinfo_file(&mut try!(File::open(&format!("/proc/{}/task/{}/mountinfo", process_id, thread_id)))) 255 | } 256 | 257 | #[cfg(test)] 258 | pub mod tests { 259 | use super::{Mountinfo, MountOption, OptionalField, mountinfo, mountinfo_self, parse_mountinfo_entry}; 260 | 261 | /// Test parsing a single mountinfo entry (positive check). 262 | #[test] 263 | fn test_parse_mountinfo_entry() { 264 | let entry = 265 | b"19 23 0:4 / /proc rw,nosuid,foo shared:13 master:20 - proc.sys proc rw,nosuid"; 266 | let got_mi = parse_mountinfo_entry(entry).unwrap().1; 267 | let want_mi = Mountinfo { 268 | mount_id: 19, 269 | parent_id: 23, 270 | major: 0, 271 | minor: 4, 272 | root: "/".into(), 273 | mount_point: "/proc".into(), 274 | mount_options: vec![ 275 | MountOption::Rw, 276 | MountOption::Nosuid, 277 | MountOption::Other("foo".to_string()) 278 | ], 279 | opt_fields: vec![ 280 | OptionalField::Shared(13), 281 | OptionalField::Master(20) 282 | ], 283 | fs_type: ("proc".to_string(), Some("sys".to_string())), 284 | mount_src: Some("proc".to_string()), 285 | super_opts: vec!["rw","nosuid"].iter().map(|&s| s.into()).collect(), 286 | }; 287 | assert_eq!(got_mi, want_mi); 288 | } 289 | 290 | /// Test parsing a single mountinfo entry (negative check). 291 | #[test] 292 | fn test_parse_mountinfo_error() { 293 | let entry = b"10 - 0:4 / /sys rw master -"; 294 | parse_mountinfo_entry(entry).unwrap_err(); 295 | } 296 | 297 | /// Test that the system mountinfo files can be parsed. 298 | #[test] 299 | fn test_mountinfo() { 300 | mountinfo_self().unwrap(); 301 | mountinfo(1).unwrap(); 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/parsers.rs: -------------------------------------------------------------------------------- 1 | //! Parsers and utility functions. 2 | 3 | use std::borrow::ToOwned; 4 | use std::fs::File; 5 | use std::io::{Error, ErrorKind, Read, Result}; 6 | use std::str::{self, FromStr}; 7 | 8 | use byteorder::{ByteOrder, LittleEndian}; 9 | use libc::clock_t; 10 | use nom::{ 11 | alphanumeric, 12 | digit, 13 | Err, 14 | IResult, 15 | is_digit, 16 | not_line_ending, 17 | space 18 | }; 19 | use nom::ErrorKind::Digit; 20 | 21 | /// Read all bytes in the file until EOF, placing them into `buf`. 22 | /// 23 | /// All bytes read from this source will be written to `buf`. If `buf` is not large enough an 24 | /// underflow error will be returned. This function will continuously call `read` to append more 25 | /// data to `buf` until read returns either `Ok(0)`, or an error of non-`ErrorKind::Interrupted` 26 | /// kind. 27 | /// 28 | /// If successful, this function will return the slice of read bytes. 29 | /// 30 | /// # Errors 31 | /// 32 | /// If this function encounters an error of the kind `ErrorKind::Interrupted` then the error is 33 | /// ignored and the operation will continue. 34 | /// 35 | /// If any other read error is encountered then this function immediately returns. Any bytes which 36 | /// have already been read will be written to `buf`. 37 | /// 38 | /// If `buf` is not large enough to hold the file, an underflow error will be returned. 39 | pub fn read_to_end<'a>(file: &mut File, buf: &'a mut [u8]) -> Result<&'a mut [u8]> { 40 | let mut from = 0; 41 | 42 | loop { 43 | if from == buf.len() { 44 | return Err(Error::new(ErrorKind::Other, "read underflow")); 45 | } 46 | match file.read(&mut buf[from..]) { 47 | Ok(0) => return Ok(&mut buf[..from]), 48 | Ok(n) => from += n, 49 | Err(ref e) if e.kind() == ErrorKind::Interrupted => {} 50 | Err(e) => return Err(e), 51 | } 52 | } 53 | } 54 | 55 | /// Transforms a `nom` parse result into a io result. 56 | /// 57 | /// The parser must completely consume the input. 58 | pub fn map_result(result: IResult<&[u8], T>) -> Result { 59 | match result { 60 | IResult::Done(remaining, val) => { 61 | if remaining.is_empty() { 62 | Ok(val) 63 | } else { 64 | let remaining = str::from_utf8(remaining); 65 | Err(Error::new(ErrorKind::InvalidInput, 66 | format!("unable to parse whole input, remaining: {:?}", remaining))) 67 | } 68 | } 69 | IResult::Error(err) => Err(Error::new(ErrorKind::InvalidInput, 70 | format!("unable to parse input: {:?}", err))), 71 | _ => Err(Error::new(ErrorKind::InvalidInput, "unable to parse input")), 72 | } 73 | } 74 | 75 | 76 | /// Recognizes numerical characters: 0-9, and periods: '.'. 77 | fn fdigit(input: &[u8]) -> IResult<&[u8], &[u8]> { 78 | for idx in 0..input.len() { 79 | if (!is_digit(input[idx])) && ('.' as u8 != input[idx]) { 80 | return IResult::Done(&input[idx..], &input[0..idx]) 81 | } 82 | } 83 | IResult::Done(b"", input) 84 | } 85 | 86 | /// Recognizes numerical characters: 0-9, and an optional leading dash: '-'. 87 | pub fn sdigit(input:&[u8]) -> IResult<&[u8], &[u8]> { 88 | if input.is_empty() { 89 | return IResult::Done(b"", input) 90 | } 91 | 92 | let start = if input[0] == '-' as u8 { 1 } else { 0 }; 93 | for (idx, item) in input.iter().enumerate().skip(start) { 94 | if !is_digit(*item) { 95 | if idx == start { 96 | return IResult::Error(Err::Position(Digit, input)); 97 | } else { 98 | return IResult::Done(&input[idx..], &input[0..idx]); 99 | } 100 | } 101 | } 102 | IResult::Done(b"", input) 103 | } 104 | 105 | /// Parses a line to a string. 106 | named!(pub parse_line, 107 | map!(map_res!(not_line_ending, str::from_utf8), ToOwned::to_owned)); 108 | 109 | /// Parses a clock_t in base-10 format. 110 | named!(pub parse_clock, 111 | map_res!(map_res!(sdigit, str::from_utf8), FromStr::from_str)); 112 | 113 | /// Parses an i32 in base-10 format. 114 | named!(pub parse_i32, 115 | map_res!(map_res!(sdigit, str::from_utf8), FromStr::from_str)); 116 | 117 | /// Parses an i64 in base-10 format. 118 | named!(pub parse_i64, 119 | map_res!(map_res!(sdigit, str::from_utf8), FromStr::from_str)); 120 | 121 | /// Parses an isize in base-10 format. 122 | named!(pub parse_isize, 123 | map_res!(map_res!(sdigit, str::from_utf8), FromStr::from_str)); 124 | 125 | /// Parses a u32 in base-10 format. 126 | named!(pub parse_u32, 127 | map_res!(map_res!(digit, str::from_utf8), FromStr::from_str)); 128 | 129 | /// Parses a u64 in base-10 format. 130 | named!(pub parse_u64, 131 | map_res!(map_res!(digit, str::from_utf8), FromStr::from_str)); 132 | 133 | /// Parses a usize in base-10 format. 134 | named!(pub parse_usize, 135 | map_res!(map_res!(digit, str::from_utf8), FromStr::from_str)); 136 | 137 | /// Parses a f32 in base-10 format. 138 | named!(pub parse_f32, 139 | map_res!(map_res!(fdigit, str::from_utf8), FromStr::from_str)); 140 | 141 | /// Parses a sequence of whitespace seperated u32s. 142 | named!(pub parse_u32s >, separated_list!(space, complete!(parse_u32))); 143 | 144 | /// Parses a sequence of whitespace seperated i32s. 145 | named!(pub parse_i32s >, separated_list!(space, parse_i32)); 146 | 147 | /// Parses a bit into a boolean 148 | named!(pub parse_bit, alt!( 149 | char!('0') => { |_| false } 150 | | char!('1') => { |_| true } 151 | )); 152 | 153 | /// Parses a usize followed by a kB unit tag. 154 | named!(pub parse_kb, 155 | chain!(space ~ bytes: parse_usize ~ space ~ tag!("kB"), || { bytes })); 156 | 157 | /// Parses a u32 in base-16 format. 158 | named!(pub parse_u32_hex, 159 | map_res!(map_res!(alphanumeric, str::from_utf8), 160 | |s| u32::from_str_radix(s, 16))); 161 | 162 | /// Parses a u32 in base-8 format. 163 | named!(pub parse_u32_octal, 164 | map_res!(map_res!(alphanumeric, str::from_utf8), 165 | |s| u32::from_str_radix(s, 8))); 166 | 167 | /// Parses a u64 in base-16 format. 168 | named!(pub parse_u64_hex, 169 | map_res!(map_res!(alphanumeric, str::from_utf8), 170 | |s| u64::from_str_radix(s, 16))); 171 | 172 | /// Reverses the bits in a byte. 173 | fn reverse(n: u8) -> u8 { 174 | // stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte 175 | const LOOKUP: [u8; 16] = [ 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 176 | 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf ]; 177 | (LOOKUP[(n & 0b1111) as usize] << 4) | LOOKUP[(n >> 4) as usize] 178 | } 179 | 180 | /// Parses a list of u32 masks into an array of bytes in `BitVec` format. 181 | /// 182 | /// See cpuset(7) for the format being parsed. 183 | named!(pub parse_u32_mask_list >, 184 | map!(separated_nonempty_list!(tag!(","), parse_u32_hex), |mut ints: Vec| { 185 | let mut bytes: Vec = Vec::with_capacity(ints.len() * 4); 186 | let mut buf: [u8; 4] = [0; 4]; 187 | ints.reverse(); 188 | for int in ints { 189 | LittleEndian::write_u32(&mut buf, int); 190 | for b in buf.iter_mut() { 191 | *b = reverse(*b); 192 | } 193 | bytes.extend(&buf); 194 | } 195 | bytes.into_boxed_slice() 196 | })); 197 | 198 | /// `take_until_right_and_consume!(tag) => &[T] -> IResult<&[T], &[T]>` 199 | /// generates a parser consuming bytes until the specified byte sequence is found, and consumes it. 200 | /// The sequence is searched for in the input in right to left order. 201 | macro_rules! take_until_right_and_consume( 202 | ($i:expr, $inp:expr) => ({ 203 | #[inline(always)] 204 | fn as_bytes(b: &T) -> &[u8] { 205 | b.as_bytes() 206 | } 207 | 208 | let expected = $inp; 209 | let bytes = as_bytes(&expected); 210 | let mut index = 0; 211 | let mut parsed = false; 212 | for idx in (0..(($i.len() + 1) - bytes.len())).rev() { 213 | if &$i[idx..idx + bytes.len()] == bytes { 214 | index = idx; 215 | parsed = true; 216 | break; 217 | } 218 | } 219 | if parsed { 220 | nom::IResult::Done(&$i[(index + bytes.len())..], &$i[0..index]) 221 | } else { 222 | nom::IResult::Error(nom::Err::Position(nom::ErrorKind::TakeUntilAndConsume, $i)) 223 | } 224 | }); 225 | ); 226 | 227 | #[cfg(test)] 228 | pub mod tests { 229 | use std::u32; 230 | 231 | use nom::IResult; 232 | 233 | use super::{map_result, parse_f32, parse_i32, parse_i32s, parse_bit, parse_i64, parse_u32_hex, 234 | parse_u32_mask_list, parse_u32s, reverse}; 235 | 236 | /// Unwrap a complete parse result. 237 | pub fn unwrap(result: IResult<&[u8], T>) -> T { 238 | map_result(result).unwrap() 239 | } 240 | 241 | #[test] 242 | fn test_reverse() { 243 | assert_eq!(0b00000000, reverse(0b00000000)); 244 | assert_eq!(0b00000010, reverse(0b01000000)); 245 | assert_eq!(0b00011000, reverse(0b00011000)); 246 | assert_eq!(0b01011000, reverse(0b00011010)); 247 | assert_eq!(0b11111111, reverse(0b11111111)); 248 | } 249 | 250 | #[test] 251 | fn test_parse_u32_hex() { 252 | assert_eq!(0, unwrap(parse_u32_hex(b"00000000"))); 253 | assert_eq!(1, unwrap(parse_u32_hex(b"00000001"))); 254 | assert_eq!(42, unwrap(parse_u32_hex(b"0000002a"))); 255 | assert_eq!(286331153, unwrap(parse_u32_hex(b"11111111"))); 256 | assert_eq!(u32::MAX, unwrap(parse_u32_hex(b"ffffffff"))); 257 | } 258 | 259 | #[test] 260 | fn test_u32_mask_list() { 261 | // Examples adapted from cpuset(7). 262 | assert_eq!([0, 0, 0, 0], &*unwrap(parse_u32_mask_list(b"00000000"))); 263 | 264 | assert_eq!([0x80, 0, 0, 0], &*unwrap(parse_u32_mask_list(b"00000001"))); 265 | 266 | assert_eq!([0, 0, 0, 0, 267 | 0, 0, 0, 0, 268 | 0, 0, 0, 2], &*unwrap(parse_u32_mask_list(b"40000000,00000000,00000000"))); 269 | 270 | assert_eq!([0, 0, 0, 0, 271 | 0, 0, 0, 0, 272 | 128, 0, 0, 0], &*unwrap(parse_u32_mask_list(b"00000001,00000000,00000000"))); 273 | 274 | assert_eq!([0, 0, 0, 0, 275 | 0xff, 0, 0, 0], &*unwrap(parse_u32_mask_list(b"000000ff,00000000"))); 276 | 277 | assert_eq!([0x46, 0x1c, 0x70, 0, 278 | 0, 0, 0, 0], &*unwrap(parse_u32_mask_list(b"00000000,000e3862"))); 279 | } 280 | 281 | #[test] 282 | fn test_parse_u32s() { 283 | assert_eq!(Vec::::new(), &*unwrap(parse_u32s(b""))); 284 | assert_eq!(vec![0u32], &*unwrap(parse_u32s(b"0"))); 285 | assert_eq!(vec![0u32, 1], &*unwrap(parse_u32s(b"0 1"))); 286 | assert_eq!(vec![99999u32, 32, 22, 888], &*unwrap(parse_u32s(b"99999 32 22 888"))); 287 | } 288 | 289 | #[test] 290 | fn test_parse_i32s() { 291 | assert_eq!(Vec::::new(), &*unwrap(parse_i32s(b""))); 292 | assert_eq!(vec![0i32], &*unwrap(parse_i32s(b"0"))); 293 | assert_eq!(vec![0i32, 1], &*unwrap(parse_i32s(b"0 1"))); 294 | assert_eq!(vec![99999i32, 0, -22, 32, 888], &*unwrap(parse_i32s(b"99999 0 -22 32 888"))); 295 | } 296 | 297 | #[test] 298 | fn test_parse_i32() { 299 | assert_eq!(0i32, unwrap(parse_i32(b"0"))); 300 | assert_eq!(0i32, unwrap(parse_i32(b"-0"))); 301 | assert_eq!(32i32, unwrap(parse_i32(b"32"))); 302 | assert_eq!(-32i32, unwrap(parse_i32(b"-32"))); 303 | } 304 | 305 | #[test] 306 | fn test_parse_i64() { 307 | assert_eq!(0i64, unwrap(parse_i64(b"0"))); 308 | assert_eq!(0i64, unwrap(parse_i64(b"-0"))); 309 | assert_eq!(32i64, unwrap(parse_i64(b"32"))); 310 | assert_eq!(-32i64, unwrap(parse_i64(b"-32"))); 311 | } 312 | 313 | #[test] 314 | fn test_parse_f32() { 315 | assert_eq!(0.0, unwrap(parse_f32(b"0"))); 316 | assert_eq!(0.0, unwrap(parse_f32(b"0.0"))); 317 | assert_eq!(2.0, unwrap(parse_f32(b"2.0"))); 318 | assert_eq!(45.67, unwrap(parse_f32(b"45.67"))); 319 | } 320 | 321 | #[test] 322 | fn test_parse_bit() { 323 | assert_eq!(true, unwrap(parse_bit(b"1"))); 324 | assert_eq!(false, unwrap(parse_bit(b"0"))); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/pid/limits.rs: -------------------------------------------------------------------------------- 1 | //! Process resource limit information from `/proc/[pid]/limits`. 2 | 3 | use std::fs::File; 4 | use std::io::Result; 5 | use std::time::Duration; 6 | 7 | use libc::pid_t; 8 | use nom::{ 9 | IResult, 10 | space, 11 | }; 12 | 13 | use parsers::{ 14 | map_result, 15 | parse_u64, 16 | parse_usize, 17 | read_to_end 18 | }; 19 | 20 | fn parse_limit<'a, P, T>(input: &'a [u8], value_parser: P) -> IResult<&'a [u8], Limit> 21 | where P: Fn(&[u8]) -> IResult<&[u8], T> { 22 | let parse_field = closure!(&'a [u8], alt!( 23 | tag!("unlimited") => { |_| None } 24 | | value_parser => { |value| Some(value) } 25 | )); 26 | 27 | map!(input, separated_pair!(parse_field, space, parse_field), 28 | |(soft, hard)| Limit { soft: soft, hard: hard }) 29 | } 30 | 31 | fn duration_from_micros(micros: u64) -> Duration { 32 | let micros_per_sec = 1_000_000; 33 | let nanos_per_micro = 1000; 34 | let secs = micros / micros_per_sec; 35 | let nanos = ((micros % micros_per_sec) as u32) * nanos_per_micro; 36 | Duration::new(secs, nanos) 37 | } 38 | 39 | named!(parse_limit_usize( &[u8] ) -> Limit, apply!(parse_limit, parse_usize)); 40 | named!(parse_limit_u64( &[u8] ) -> Limit, apply!(parse_limit, parse_u64)); 41 | named!(parse_limit_seconds( &[u8] ) -> Limit, 42 | map!(apply!(parse_limit, parse_u64), 43 | | Limit { soft, hard } | { 44 | Limit { 45 | soft: soft.map(Duration::from_secs), 46 | hard: hard.map(Duration::from_secs), 47 | } 48 | } 49 | )); 50 | named!(parse_limit_micros( &[u8] ) -> Limit, 51 | map!(apply!(parse_limit, parse_u64), 52 | | Limit { soft, hard } | { 53 | Limit { 54 | soft: soft.map(duration_from_micros), 55 | hard: hard.map(duration_from_micros), 56 | } 57 | } 58 | )); 59 | 60 | named!(parse_limits( &[u8] ) -> Limits, 61 | ws!(do_parse!( 62 | tag!("Limit") >> tag!("Soft Limit") >> tag!("Hard Limit") >> tag!("Units") >> 63 | tag!("Max cpu time") >> max_cpu_time: parse_limit_seconds >> tag!("seconds") >> 64 | tag!("Max file size") >> max_file_size: parse_limit_u64 >> tag!("bytes") >> 65 | tag!("Max data size") >> max_data_size: parse_limit_usize >> tag!("bytes") >> 66 | tag!("Max stack size") >> max_stack_size: parse_limit_usize >> tag!("bytes") >> 67 | tag!("Max core file size") >> max_core_file_size: parse_limit_usize >> tag!("bytes") >> 68 | tag!("Max resident set") >> max_resident_set: parse_limit_usize >> tag!("bytes") >> 69 | tag!("Max processes") >> max_processes: parse_limit_usize >> tag!("processes") >> 70 | tag!("Max open files") >> max_open_files: parse_limit_usize >> tag!("files") >> 71 | tag!("Max locked memory") >> max_locked_memory: parse_limit_usize >> tag!("bytes") >> 72 | tag!("Max address space") >> max_address_space: parse_limit_usize >> tag!("bytes") >> 73 | tag!("Max file locks") >> max_file_locks: parse_limit_usize >> tag!("locks") >> 74 | tag!("Max pending signals") >> max_pending_signals: parse_limit_usize >> tag!("signals") >> 75 | tag!("Max msgqueue size") >> max_msgqueue_size: parse_limit_usize >> tag!("bytes") >> 76 | tag!("Max nice priority") >> max_nice_priority: parse_limit_usize >> 77 | tag!("Max realtime priority") >> max_realtime_priority: parse_limit_usize >> 78 | tag!("Max realtime timeout") >> max_realtime_timeout: parse_limit_micros >> tag!("us") >> 79 | (Limits { 80 | max_cpu_time: max_cpu_time, 81 | max_file_size: max_file_size, 82 | max_data_size: max_data_size, 83 | max_stack_size: max_stack_size, 84 | max_core_file_size: max_core_file_size, 85 | max_resident_set: max_resident_set, 86 | max_processes: max_processes, 87 | max_open_files: max_open_files, 88 | max_locked_memory: max_locked_memory, 89 | max_address_space: max_address_space, 90 | max_file_locks: max_file_locks, 91 | max_pending_signals: max_pending_signals, 92 | max_msgqueue_size: max_msgqueue_size, 93 | max_nice_priority: max_nice_priority, 94 | max_realtime_priority: max_realtime_priority, 95 | max_realtime_timeout: max_realtime_timeout, 96 | }) 97 | )) 98 | ); 99 | 100 | /// A resource limit, including a soft and hard bound. 101 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 102 | pub struct Limit { 103 | /// The soft resource limit. 104 | /// 105 | /// The kernel enforces that resource usage does not exceed this value. 106 | pub soft: Option, 107 | 108 | /// The hard resource limit. 109 | /// 110 | /// The kernel allows the soft limit to be raised until this limit using 111 | /// `setrlimit`. 112 | pub hard: Option, 113 | } 114 | 115 | /// Process limits information 116 | /// See `man 2 getrlimit`. 117 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 118 | pub struct Limits { 119 | /// The maximum CPU time a process can use. 120 | pub max_cpu_time: Limit, 121 | /// The maximum size of files that the process may create in bytes. 122 | pub max_file_size: Limit, 123 | /// The maximum size of the process's data segment in bytes. 124 | pub max_data_size: Limit, 125 | /// The maximum size of the process stack in bytes. 126 | pub max_stack_size: Limit, 127 | /// Maximum size of a core file in bytes. 128 | pub max_core_file_size: Limit, 129 | /// Specifies the limit of the process's resident set in bytes. 130 | pub max_resident_set: Limit, 131 | /// The maximum number of processes (or, more precisely on Linux, threads) 132 | /// that can be created for the real user ID of the calling process. 133 | pub max_processes: Limit, 134 | /// Specifies a value one greater than the maximum file descriptor 135 | /// number that can be opened by this process. 136 | pub max_open_files: Limit, 137 | /// The maximum number of bytes of memory that may be locked into RAM. 138 | pub max_locked_memory: Limit, 139 | /// The maximum size of the process's virtual memory (address space) in bytes. 140 | pub max_address_space: Limit, 141 | /// A limit on the combined number of locks and leases that this process may 142 | /// establish. 143 | pub max_file_locks: Limit, 144 | /// Specifies the limit on the number of signals that may be queued for the 145 | /// real user ID of the calling process. 146 | pub max_pending_signals: Limit, 147 | /// Specifies the limit on the number of bytes that can be allocated for 148 | /// POSIX message queues for the real user ID of the calling process. 149 | pub max_msgqueue_size: Limit, 150 | /// Specifies a ceiling to which the process's nice value can be raised. 151 | pub max_nice_priority: Limit, 152 | /// Specifies a limit on the amount of CPU time that a process scheduled 153 | /// under a real-time scheduling policy may consume without making a blocking 154 | /// system call. 155 | pub max_realtime_priority: Limit, 156 | /// Specifies a ceiling on the real-time priority that may be set for this process. 157 | pub max_realtime_timeout: Limit, 158 | } 159 | 160 | /// Parses the provided limits file. 161 | fn limits_file(file: &mut File) -> Result { 162 | let mut buf = [0; 2048]; // A typical limits file is about 1350 bytes 163 | map_result(parse_limits(try!(read_to_end(file, &mut buf)))) 164 | } 165 | 166 | /// Returns resource limit information from the process with the provided pid. 167 | pub fn limits(pid: pid_t) -> Result { 168 | limits_file(&mut try!(File::open(&format!("/proc/{}/limits", pid)))) 169 | } 170 | 171 | /// Returns resource limit information for the current process. 172 | pub fn limits_self() -> Result { 173 | limits_file(&mut try!(File::open("/proc/self/limits"))) 174 | } 175 | 176 | /// Returns resource limit information from the thread with the provided parent process ID and thread ID. 177 | pub fn limits_task(process_id: pid_t, thread_id: pid_t) -> Result { 178 | limits_file(&mut try!(File::open(&format!("/proc/{}/task/{}/limits", process_id, thread_id)))) 179 | } 180 | 181 | #[cfg(test)] 182 | pub mod tests { 183 | 184 | use std::time::Duration; 185 | 186 | use parsers::tests::unwrap; 187 | use super::{limits, limits_self, parse_limits}; 188 | 189 | /// Test that the system limit file can be parsed. 190 | #[test] 191 | fn test_limits() { 192 | limits_self().unwrap(); 193 | limits(1).unwrap(); 194 | } 195 | 196 | #[test] 197 | fn test_parse_limits() { 198 | let text = b"Limit Soft Limit Hard Limit Units \n 199 | Max cpu time 10 60 seconds \n 200 | Max file size unlimited unlimited bytes \n 201 | Max data size unlimited unlimited bytes \n 202 | Max stack size 8388608 unlimited bytes \n 203 | Max core file size unlimited unlimited bytes \n 204 | Max resident set unlimited unlimited bytes \n 205 | Max processes 63632 63632 processes \n 206 | Max open files 1024 4096 files \n 207 | Max locked memory 65536 65536 bytes \n 208 | Max address space unlimited unlimited bytes \n 209 | Max file locks unlimited unlimited locks \n 210 | Max pending signals 63632 63632 signals \n 211 | Max msgqueue size 819200 819200 bytes \n 212 | Max nice priority 0 0 \n 213 | Max realtime priority 0 0 \n 214 | Max realtime timeout 500 unlimited us \n"; 215 | 216 | let limits = unwrap(parse_limits(text)); 217 | 218 | assert_eq!(Some(Duration::new(10, 0)), limits.max_cpu_time.soft); 219 | assert_eq!(Some(Duration::new(60, 0)), limits.max_cpu_time.hard); 220 | 221 | assert_eq!(None, limits.max_file_size.soft); 222 | assert_eq!(None, limits.max_file_size.hard); 223 | 224 | assert_eq!(None, limits.max_data_size.soft); 225 | assert_eq!(None, limits.max_data_size.hard); 226 | 227 | assert_eq!(Some(8388608), limits.max_stack_size.soft); 228 | assert_eq!(None, limits.max_stack_size.hard); 229 | 230 | assert_eq!(None, limits.max_core_file_size.soft); 231 | assert_eq!(None, limits.max_core_file_size.hard); 232 | 233 | assert_eq!(None, limits.max_resident_set.soft); 234 | assert_eq!(None, limits.max_resident_set.hard); 235 | 236 | assert_eq!(Some(63632), limits.max_processes.soft); 237 | assert_eq!(Some(63632), limits.max_processes.hard); 238 | 239 | assert_eq!(Some(1024), limits.max_open_files.soft); 240 | assert_eq!(Some(4096), limits.max_open_files.hard); 241 | 242 | assert_eq!(Some(65536), limits.max_locked_memory.soft); 243 | assert_eq!(Some(65536), limits.max_locked_memory.hard); 244 | 245 | assert_eq!(None, limits.max_address_space.soft); 246 | assert_eq!(None, limits.max_address_space.hard); 247 | 248 | assert_eq!(None, limits.max_file_locks.soft); 249 | assert_eq!(None, limits.max_file_locks.hard); 250 | 251 | assert_eq!(Some(63632), limits.max_pending_signals.soft); 252 | assert_eq!(Some(63632), limits.max_pending_signals.hard); 253 | 254 | assert_eq!(Some(819200), limits.max_msgqueue_size.soft); 255 | assert_eq!(Some(819200), limits.max_msgqueue_size.hard); 256 | 257 | assert_eq!(Some(0), limits.max_nice_priority.soft); 258 | assert_eq!(Some(0), limits.max_nice_priority.hard); 259 | 260 | assert_eq!(Some(0), limits.max_realtime_priority.soft); 261 | assert_eq!(Some(0), limits.max_realtime_priority.hard); 262 | 263 | assert_eq!(Some(Duration::new(0, 500 * 1000)), limits.max_realtime_timeout.soft); 264 | assert_eq!(None, limits.max_realtime_timeout.hard); 265 | } 266 | } 267 | 268 | #[cfg(all(test, rustc_nightly))] 269 | mod benches { 270 | extern crate test; 271 | 272 | use std::fs::File; 273 | 274 | use parsers::read_to_end; 275 | use super::*; 276 | 277 | #[bench] 278 | fn bench_limits(b: &mut test::Bencher) { 279 | b.iter(|| test::black_box(limits(1))); 280 | } 281 | 282 | #[bench] 283 | fn bench_limits_parse(b: &mut test::Bencher) { 284 | let mut buf = [0; 2048]; 285 | let limits = read_to_end(&mut File::open("/proc/1/limits").unwrap(), &mut buf).unwrap(); 286 | b.iter(|| test::black_box(parse_limits(limits))); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /src/pid/stat.rs: -------------------------------------------------------------------------------- 1 | //! Process status information from `/proc/[pid]/stat`. 2 | 3 | use std::fs::File; 4 | use std::io::Result; 5 | use std::str::{self, FromStr}; 6 | 7 | use libc::{clock_t, pid_t}; 8 | use nom::{self, IResult, line_ending, space}; 9 | use pid::State; 10 | 11 | use parsers::{ 12 | map_result, 13 | parse_clock, 14 | parse_i32, 15 | parse_u32, 16 | parse_u64, 17 | parse_usize, 18 | read_to_end 19 | }; 20 | 21 | /// Process status information. 22 | /// 23 | /// See `man 5 proc` and `Linux/fs/proc/array.c`. 24 | #[derive(Debug, Default, PartialEq, Eq, Hash)] 25 | pub struct Stat { 26 | /// Process ID (i.e., Thread Group ID). 27 | pub pid: pid_t, 28 | /// Filename of the executable. 29 | pub command: String, 30 | /// Current state of the process. 31 | pub state: State, 32 | /// Process ID of parent process. 33 | pub ppid: pid_t, 34 | /// Process group ID of the process. 35 | pub pgrp: pid_t, 36 | /// Session ID of the process. 37 | pub session: pid_t, 38 | /// The controlling terminal of the process. (The minor device number is contained in the 39 | /// combination of bits 31 to 20 and 7 to 0; the major device number is in bits 15 to 8.) 40 | pub tty_nr: pid_t, 41 | /// Process group ID of the controlling terminal of the process. 42 | pub tty_pgrp: pid_t, 43 | /// The kernel flags word of the process. For bit meanings, see the `PF_*` defines in the Linux 44 | /// kernel source file `include/linux/sched.h`. Details depend on the kernel version. 45 | pub flags: u32, 46 | /// The number of minor faults the process has made which have not required loading a memory 47 | /// page from disk. 48 | pub minflt: usize, 49 | /// The number of minor faults that the process's waited-for children have made. 50 | pub cminflt: usize, 51 | /// The number of major faults the process has made which have required loading a memory page 52 | /// from disk. 53 | pub majflt: usize, 54 | /// The number of major faults that the process's waited-for children have made. 55 | pub cmajflt: usize, 56 | /// Amount of time that this process has been scheduled in user mode, measured in clock ticks 57 | /// (divide by `sysconf(_SC_CLK_TCK)`). This includes guest time, `guest_time` (time spent 58 | /// running a virtual CPU, see below), so that applications that are not aware of the guest 59 | /// time field do not lose that time from their calculations. 60 | pub utime: clock_t, 61 | /// Amount of time that this process has been scheduled in kernel mode, measured in clock ticks 62 | /// (divide by `sysconf(_SC_CLK_TCK)`). 63 | pub stime: clock_t, 64 | /// Amount of time that this process's waited-for children have been scheduled in user mode, 65 | /// measured in clock ticks (divide by `sysconf(_SC_CLK_TCK)`). (See also `times(2)`.) This 66 | /// includes guest time, `cguest_time` (time spent running a virtual CPU, see below). 67 | pub cutime: clock_t, 68 | /// Amount of time that this process's waited-for children have been scheduled in kernel mode, 69 | /// measured in clock ticks (divide by `sysconf(_SC_CLK_TCK)`). 70 | pub cstime: clock_t, 71 | /// For processes running a real-time scheduling policy (policy below; see 72 | /// `sched_setscheduler(2)`), this is the negated scheduling priority, minus one; that is, a 73 | /// number in the range -2 to -100, corresponding to real-time priorities 1 to 99. For 74 | /// processes running under a non-real-time scheduling policy, this is the raw nice value 75 | /// (`setpriority(2)`) as represented in the kernel. The kernel stores nice values as numbers 76 | /// in the range 0 (high) to 39 (low), corresponding to the user-visible nice range of -20 to 77 | /// 19. 78 | pub priority: i32, 79 | /// The nice value (see `setpriority(2)`), a value in the range 19 (low priority) to -20 (high 80 | /// priority). 81 | pub nice: i32, 82 | /// Number of threads in this process (since Linux 2.6). 83 | pub num_threads: i32, 84 | /// The time the process started after system boot, expressed in clock ticks (divide by 85 | /// `sysconf(_SC_CLK_TCK)`). 86 | pub start_time: u64, 87 | /// Virtual memory size in bytes. 88 | pub vsize: usize, 89 | /// Resident Set Size: number of pages the process has in real memory. This is just the pages 90 | /// which count toward text, data, or stack space. This does not include pages which have not 91 | /// been demand-loaded in, or which are swapped out. 92 | pub rss: usize, 93 | /// Current soft limit in bytes on the rss of the process; see the description of `RLIMIT_RSS` 94 | /// in `getrlimit(2)`. 95 | pub rsslim: usize, 96 | /// The address above which program text can run. 97 | pub start_code: usize, 98 | /// The address below which program text can run. 99 | pub end_code: usize, 100 | /// The address of the start (i.e., bottom) of the stack. 101 | pub startstack: usize, 102 | /// The current value of ESP (stack pointer), as found in the kernel stack page for the process. 103 | pub kstkeep: usize, 104 | /// The current EIP (instruction pointer). 105 | pub kstkeip: usize, 106 | /// The bitmap of pending signals. Obsolete, because it does not provide information on 107 | /// real-time signals; use `/proc/[pid]/status` instead. 108 | pub signal: usize, 109 | /// The bitmap of blocked signals. Obsolete, because it does not provide information on 110 | /// real-time signals; use `/proc/[pid]/status` instead. 111 | pub blocked: usize, 112 | /// The bitmap of ignored signals. Obsolete, because it does not provide information on 113 | /// real-time signals; use `/proc/[pid]/status` instead. 114 | pub sigignore: usize, 115 | /// The bitmap of caught signals. Obsolete, because it does not provide information on 116 | /// real-time signals; use /proc/[pid]/status instead. 117 | pub sigcatch: usize, 118 | /// This is the "channel" in which the process is waiting. It is the address of a location in 119 | /// the kernel where the process is sleeping. The corresponding symbolic name can be found in 120 | /// `/proc/[pid]/wchan`. 121 | pub wchan: usize, 122 | /// Signal to be sent to parent when we die. 123 | pub exit_signal: i32, 124 | /// CPU number last executed on. 125 | pub processor: u32, 126 | /// Real-time scheduling priority, a number in the range 1 to 99 for processes scheduled under 127 | /// a real-time policy, or 0, for non-real-time processes (see `sched_setscheduler(2)`). 128 | pub rt_priority: u32, 129 | /// Scheduling policy (see `sched_setscheduler(2)`). Decode using the `SCHED_*` constants in 130 | /// `linux/sched.h`. 131 | pub policy: u32, 132 | /// Aggregated block I/O delays, measured in clock ticks (centiseconds). Since Linux 2.6.18. 133 | pub delayacct_blkio_ticks: u64, 134 | /// Guest time of the process (time spent running a virtual CPU for a guest operating system), 135 | /// measured in clock ticks (divide by `sysconf(_SC_CLK_TCK)`). Since Linux 2.6.24. 136 | pub guest_time: clock_t, 137 | /// Guest time of the process's children, measured in clock ticks (divide by 138 | /// `sysconf(_SC_CLK_TCK)`). Since linux 2.6.24. 139 | pub cguest_time: clock_t, 140 | /// Address above which program initialized and uninitialized (BSS) data are placed. Since 141 | /// Linux 3.3. 142 | pub start_data: usize, 143 | /// Address below which program initialized and uninitialized (BSS) data are placed. Since 144 | /// Linux 3.3. 145 | pub end_data: usize, 146 | /// Address above which program heap can be expanded with `brk(2)`. Since Linux 3.3. 147 | pub start_brk: usize, 148 | /// Address above which program command-line arguments (argv) are placed. Since Linux 3.5. 149 | pub arg_start: usize, 150 | /// Address below program command-line arguments (argv) are placed. Since Linux 3.5. 151 | pub arg_end: usize, 152 | /// Address above which program environment is placed. Since Linux 3.5. 153 | pub env_start: usize, 154 | /// Address below which program environment is placed. Since Linux 3.5. 155 | pub env_end: usize, 156 | /// The thread's exit status in the form reported by `waitpid(2)`. Since Linux 3.5. 157 | pub exit_code: i32, 158 | } 159 | 160 | named!(parse_command, 161 | map_res!(map_res!(preceded!(char!('('), 162 | take_until_right_and_consume!(")")), 163 | str::from_utf8), 164 | FromStr::from_str)); 165 | 166 | /// Parse the stat state format. 167 | named!(parse_stat_state, 168 | alt!(tag!("R") => { |_| State::Running } 169 | | tag!("S") => { |_| State::Sleeping } 170 | | tag!("D") => { |_| State::Waiting } 171 | | tag!("Z") => { |_| State::Zombie } 172 | | tag!("T") => { |_| State::Stopped } 173 | | tag!("t") => { |_| State::TraceStopped } 174 | | tag!("W") => { |_| State::Paging } 175 | | tag!("X") => { |_| State::Dead } 176 | | tag!("x") => { |_| State::Dead } 177 | | tag!("K") => { |_| State::Wakekill } 178 | | tag!("W") => { |_| State::Waking } 179 | | tag!("P") => { |_| State::Parked })); 180 | 181 | // Note: this is implemented as a function insted of via `chain!` to reduce the 182 | // stack depth in rustc by limiting the generated AST's depth. This is a work 183 | // around for 184 | // https://github.com/rust-lang/rust/issues/35408 185 | // where rustc overflows its stack. The bug affects at least rustc 1.12. 186 | fn parse_stat(input: &[u8]) -> IResult<&[u8], Stat> { 187 | /// Helper macro for space terminated parser. 188 | macro_rules! s { 189 | ($i:expr, $f:expr) => (terminated!($i, call!($f), space)) 190 | } 191 | /// Helper macro for line-ending terminated parser. 192 | macro_rules! l { 193 | ($i:expr, $f:expr) => (terminated!($i, call!($f), line_ending)) 194 | } 195 | 196 | let rest = input; 197 | 198 | let (rest, pid) = try_parse!(rest, s!(parse_i32 )); 199 | let (rest, command) = try_parse!(rest, s!(parse_command )); 200 | let (rest, state) = try_parse!(rest, s!(parse_stat_state )); 201 | let (rest, ppid) = try_parse!(rest, s!(parse_i32 )); 202 | let (rest, pgrp) = try_parse!(rest, s!(parse_i32 )); 203 | let (rest, session) = try_parse!(rest, s!(parse_i32 )); 204 | let (rest, tty_nr) = try_parse!(rest, s!(parse_i32 )); 205 | let (rest, tty_pgrp) = try_parse!(rest, s!(parse_i32 )); 206 | let (rest, flags) = try_parse!(rest, s!(parse_u32 )); 207 | let (rest, minflt) = try_parse!(rest, s!(parse_usize )); 208 | let (rest, cminflt) = try_parse!(rest, s!(parse_usize )); 209 | let (rest, majflt) = try_parse!(rest, s!(parse_usize )); 210 | let (rest, cmajflt) = try_parse!(rest, s!(parse_usize )); 211 | let (rest, utime) = try_parse!(rest, s!(parse_clock )); 212 | let (rest, stime) = try_parse!(rest, s!(parse_clock )); 213 | let (rest, cutime) = try_parse!(rest, s!(parse_clock )); 214 | let (rest, cstime) = try_parse!(rest, s!(parse_clock )); 215 | let (rest, priority) = try_parse!(rest, s!(parse_i32 )); 216 | let (rest, nice) = try_parse!(rest, s!(parse_i32 )); 217 | let (rest, num_threads) = try_parse!(rest, s!(parse_i32 )); 218 | let (rest, _itrealvalue) = try_parse!(rest, s!(parse_i32 )); 219 | let (rest, start_time) = try_parse!(rest, s!(parse_u64 )); 220 | let (rest, vsize) = try_parse!(rest, s!(parse_usize )); 221 | let (rest, rss) = try_parse!(rest, s!(parse_usize )); 222 | let (rest, rsslim) = try_parse!(rest, s!(parse_usize )); 223 | let (rest, start_code) = try_parse!(rest, s!(parse_usize )); 224 | let (rest, end_code) = try_parse!(rest, s!(parse_usize )); 225 | let (rest, startstack) = try_parse!(rest, s!(parse_usize )); 226 | let (rest, kstkeep) = try_parse!(rest, s!(parse_usize )); 227 | let (rest, kstkeip) = try_parse!(rest, s!(parse_usize )); 228 | let (rest, signal) = try_parse!(rest, s!(parse_usize )); 229 | let (rest, blocked) = try_parse!(rest, s!(parse_usize )); 230 | let (rest, sigignore) = try_parse!(rest, s!(parse_usize )); 231 | let (rest, sigcatch) = try_parse!(rest, s!(parse_usize )); 232 | let (rest, wchan) = try_parse!(rest, s!(parse_usize )); 233 | let (rest, _nswap) = try_parse!(rest, s!(parse_usize )); 234 | let (rest, _cnswap) = try_parse!(rest, s!(parse_usize )); 235 | let (rest, exit_signal) = try_parse!(rest, s!(parse_i32 )); 236 | let (rest, processor) = try_parse!(rest, s!(parse_u32 )); 237 | let (rest, rt_priority) = try_parse!(rest, s!(parse_u32 )); 238 | let (rest, policy) = try_parse!(rest, s!(parse_u32 )); 239 | let (rest, delayacct_blkio_ticks) = try_parse!(rest, s!(parse_u64 )); 240 | let (rest, guest_time) = try_parse!(rest, s!(parse_clock )); 241 | let (rest, cguest_time) = try_parse!(rest, s!(parse_clock )); 242 | let (rest, start_data) = try_parse!(rest, s!(parse_usize )); 243 | let (rest, end_data) = try_parse!(rest, s!(parse_usize )); 244 | let (rest, start_brk) = try_parse!(rest, s!(parse_usize )); 245 | let (rest, arg_start) = try_parse!(rest, s!(parse_usize )); 246 | let (rest, arg_end) = try_parse!(rest, s!(parse_usize )); 247 | let (rest, env_start) = try_parse!(rest, s!(parse_usize )); 248 | let (rest, env_end) = try_parse!(rest, s!(parse_usize )); 249 | let (rest, exit_code) = try_parse!(rest, l!(parse_i32 )); 250 | 251 | IResult::Done(rest, Stat { 252 | pid : pid, 253 | command : command, 254 | state : state, 255 | ppid : ppid, 256 | pgrp : pgrp, 257 | session : session, 258 | tty_nr : tty_nr, 259 | tty_pgrp : tty_pgrp, 260 | flags : flags, 261 | minflt : minflt, 262 | cminflt : cminflt, 263 | majflt : majflt, 264 | cmajflt : cmajflt, 265 | utime : utime, 266 | stime : stime, 267 | cutime : cutime, 268 | cstime : cstime, 269 | priority : priority, 270 | nice : nice, 271 | num_threads : num_threads, 272 | start_time : start_time, 273 | vsize : vsize, 274 | rss : rss, 275 | rsslim : rsslim, 276 | start_code : start_code, 277 | end_code : end_code, 278 | startstack : startstack, 279 | kstkeep : kstkeep, 280 | kstkeip : kstkeip, 281 | signal : signal, 282 | blocked : blocked, 283 | sigignore : sigignore, 284 | sigcatch : sigcatch, 285 | wchan : wchan, 286 | exit_signal : exit_signal, 287 | processor : processor, 288 | rt_priority : rt_priority, 289 | policy : policy, 290 | delayacct_blkio_ticks : delayacct_blkio_ticks, 291 | guest_time : guest_time, 292 | cguest_time : cguest_time, 293 | start_data : start_data, 294 | end_data : end_data, 295 | start_brk : start_brk, 296 | arg_start : arg_start, 297 | arg_end : arg_end, 298 | env_start : env_start, 299 | env_end : env_end, 300 | exit_code : exit_code, 301 | }) 302 | } 303 | 304 | /// Parses the provided stat file. 305 | fn stat_file(file: &mut File) -> Result { 306 | let mut buf = [0; 1024]; // A typical statm file is about 300 bytes 307 | map_result(parse_stat(try!(read_to_end(file, &mut buf)))) 308 | } 309 | 310 | /// Returns status information for the process with the provided pid. 311 | pub fn stat(pid: pid_t) -> Result { 312 | stat_file(&mut try!(File::open(&format!("/proc/{}/stat", pid)))) 313 | } 314 | 315 | /// Returns status information for the current process. 316 | pub fn stat_self() -> Result { 317 | stat_file(&mut try!(File::open("/proc/self/stat"))) 318 | } 319 | 320 | /// Returns status information from the thread with the provided parent process ID and thread ID. 321 | pub fn stat_task(process_id: pid_t, thread_id: pid_t) -> Result { 322 | stat_file(&mut try!(File::open(&format!("/proc/{}/task/{}/stat", process_id, thread_id)))) 323 | } 324 | 325 | #[cfg(test)] 326 | pub mod tests { 327 | use parsers::tests::unwrap; 328 | use pid::State; 329 | use super::{ 330 | parse_command, 331 | parse_stat, 332 | stat, 333 | stat_self 334 | }; 335 | 336 | #[test] 337 | fn test_parse_command() { 338 | assert_eq!("cat", &unwrap(parse_command(b"(cat)"))); 339 | assert_eq!("cat ) (( )) ", &unwrap(parse_command(b"(cat ) (( )) )"))); 340 | } 341 | 342 | /// Test that the system stat files can be parsed. 343 | #[test] 344 | fn test_stat() { 345 | stat_self().unwrap(); 346 | stat(1).unwrap(); 347 | } 348 | 349 | #[test] 350 | fn test_parse_stat() { 351 | let text = b"19853 (cat) R 19435 19853 19435 34819 19853 4218880 98 0 0 0 0 0 0 0 20 0 1 0 \ 352 | 279674171 112295936 180 18446744073709551615 4194304 4238772 140736513999744 \ 353 | 140736513999080 139957028908944 0 0 0 0 0 0 0 17 15 0 0 0 0 0 6339648 6341408 \ 354 | 17817600 140736514006312 140736514006332 140736514006332 140736514007019 0\n"; 355 | let stat = unwrap(parse_stat(text)); 356 | 357 | assert_eq!(19853, stat.pid); 358 | assert_eq!("cat", &stat.command); 359 | assert_eq!(State::Running, stat.state); 360 | assert_eq!(19435, stat.ppid); 361 | assert_eq!(19853, stat.pgrp); 362 | assert_eq!(19435, stat.session); 363 | assert_eq!(34819, stat.tty_nr); 364 | assert_eq!(19853, stat.tty_pgrp); 365 | assert_eq!(4218880, stat.flags); 366 | assert_eq!(98, stat.minflt); 367 | assert_eq!(0, stat.cminflt); 368 | assert_eq!(0, stat.majflt); 369 | assert_eq!(0, stat.cmajflt); 370 | assert_eq!(0, stat.utime); 371 | assert_eq!(0, stat.stime); 372 | assert_eq!(0, stat.cutime); 373 | assert_eq!(0, stat.cstime); 374 | assert_eq!(20, stat.priority); 375 | assert_eq!(0, stat.nice); 376 | assert_eq!(1, stat.num_threads); 377 | assert_eq!(279674171, stat.start_time); 378 | assert_eq!(112295936, stat.vsize); 379 | assert_eq!(180, stat.rss); 380 | assert_eq!(18446744073709551615, stat.rsslim); 381 | assert_eq!(4194304, stat.start_code); 382 | assert_eq!(4238772, stat.end_code); 383 | assert_eq!(140736513999744, stat.startstack); 384 | assert_eq!(140736513999080, stat.kstkeep); 385 | assert_eq!(139957028908944, stat.kstkeip); 386 | assert_eq!(0, stat.signal); 387 | assert_eq!(0, stat.blocked); 388 | assert_eq!(0, stat.sigignore); 389 | assert_eq!(0, stat.sigcatch); 390 | assert_eq!(0, stat.wchan); 391 | assert_eq!(17, stat.exit_signal); 392 | assert_eq!(15, stat.processor); 393 | assert_eq!(0, stat.rt_priority); 394 | assert_eq!(0, stat.policy); 395 | assert_eq!(0, stat.delayacct_blkio_ticks); 396 | assert_eq!(0, stat.guest_time); 397 | assert_eq!(0, stat.cguest_time); 398 | assert_eq!(6339648, stat.start_data); 399 | assert_eq!(6341408, stat.end_data); 400 | assert_eq!(17817600, stat.start_brk); 401 | assert_eq!(140736514006312, stat.arg_start); 402 | assert_eq!(140736514006332, stat.arg_end); 403 | assert_eq!(140736514006332, stat.env_start); 404 | assert_eq!(140736514007019, stat.env_end); 405 | assert_eq!(0, stat.exit_code); 406 | } 407 | } 408 | 409 | #[cfg(all(test, rustc_nightly))] 410 | mod benches { 411 | extern crate test; 412 | 413 | use std::fs::File; 414 | 415 | use parsers::read_to_end; 416 | use super::{parse_stat, stat}; 417 | 418 | #[bench] 419 | fn bench_stat(b: &mut test::Bencher) { 420 | b.iter(|| test::black_box(stat(1))); 421 | } 422 | 423 | #[bench] 424 | fn bench_stat_parse(b: &mut test::Bencher) { 425 | let mut buf = [0; 256]; 426 | let stat = read_to_end(&mut File::open("/proc/1/stat").unwrap(), &mut buf).unwrap(); 427 | b.iter(|| test::black_box(parse_stat(stat))); 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /src/pid/status.rs: -------------------------------------------------------------------------------- 1 | //! Process status information information from `/proc/[pid]/status`. 2 | 3 | use std::fs::File; 4 | use std::io::Result; 5 | 6 | use libc::{gid_t, mode_t, pid_t, uid_t}; 7 | use nom::{IResult, line_ending, multispace, not_line_ending, space}; 8 | 9 | use parsers::{ 10 | map_result, 11 | parse_i32, 12 | parse_bit, 13 | parse_i32s, 14 | parse_kb, 15 | parse_line, 16 | parse_u32, 17 | parse_u32_mask_list, 18 | parse_u32_octal, 19 | parse_u32s, 20 | parse_u64, 21 | parse_u64_hex, 22 | read_to_end 23 | }; 24 | use pid::State; 25 | 26 | /// The Secure Computing state of a process. 27 | #[derive(Debug, PartialEq, Eq, Hash)] 28 | pub enum SeccompMode { 29 | Disabled, 30 | Strict, 31 | Filter, 32 | } 33 | 34 | impl Default for SeccompMode { 35 | fn default() -> SeccompMode { 36 | SeccompMode::Disabled 37 | } 38 | } 39 | 40 | named!(parse_seccomp_mode, 41 | alt!(tag!("0") => { |_| SeccompMode::Disabled } 42 | | tag!("1") => { |_| SeccompMode::Strict } 43 | | tag!("2") => { |_| SeccompMode::Filter })); 44 | 45 | /// Process status information. 46 | /// 47 | /// See `man 5 proc` and `Linux/fs/proc/array.c`. 48 | #[derive(Default, Debug, PartialEq, Eq, Hash)] 49 | pub struct Status { 50 | /// Filename of the executable. 51 | pub command: String, 52 | /// File mode creation mask (since Linux 4.7). 53 | pub umask: mode_t, 54 | /// Current state of the process. 55 | pub state: State, 56 | /// Process ID (i.e., Thread Group ID). 57 | pub pid: pid_t, 58 | /// NUMA group ID. 59 | pub numa_gid: pid_t, 60 | /// Thread ID. 61 | pub tid: pid_t, 62 | /// Process ID of parent process. 63 | pub ppid: pid_t, 64 | /// Process ID of the process tracing this process (0 if not being traced). 65 | pub tracer_pid: pid_t, 66 | /// Real user ID. 67 | pub uid_real: uid_t, 68 | /// Effective user ID. 69 | pub uid_effective: uid_t, 70 | /// Saved user ID. 71 | pub uid_saved: uid_t, 72 | /// Filesystem user ID. 73 | pub uid_fs: uid_t, 74 | /// Real group ID. 75 | pub gid_real: gid_t, 76 | /// Effective group ID. 77 | pub gid_effective: gid_t, 78 | /// Saved group ID. 79 | pub gid_saved: gid_t, 80 | /// Filesystem group ID. 81 | pub gid_fs: gid_t, 82 | /// Number of file descriptor slots currently allocated. 83 | pub fd_allocated: u32, 84 | /// Supplementary group list. 85 | pub groups: Vec, 86 | /// Process IDs for each namespace which the process belongs to. 87 | pub ns_pids: Vec, 88 | /// Thread IDs for each namespace which the process belongs to. 89 | pub ns_tids: Vec, 90 | /// Process group IDs for each namespace which the process belongs to. 91 | pub ns_pgids: Vec, 92 | /// Session IDs of the process for each namespace to which it belongs. 93 | pub ns_sids: Vec, 94 | /// Peak virtual memory size (kB). 95 | pub vm_peak: usize, 96 | /// Virtual memory size (kB). 97 | pub vm_size: usize, 98 | /// Locked memory size (kB) (see mlock(3)). 99 | pub vm_locked: usize, 100 | /// Pinned memory size (since Linux 3.2). These are pages that can't be moved because 101 | /// something needs to directly access physical memory. 102 | pub vm_pin: usize, 103 | /// Peak resident size (kB) ("high water mark"). 104 | pub vm_hwm: usize, 105 | /// Resident set size (kB). Comprised of `vm_rss_anon`, `vm_rss_file`, 106 | /// and `vm_rss_shared`. 107 | pub vm_rss: usize, 108 | /// Size of resident anonymous memory (kB) (since Linux 4.5). 109 | pub vm_rss_anon: usize, 110 | /// Size of resident file mappings (kB) (since Linux 4.5). 111 | pub vm_rss_file: usize, 112 | /// Size of resident shared memory (kB) (since Linux 4.5). Includes SysV 113 | /// shm, mapping of tmpfs and shared anonymous mappings. 114 | pub vm_rss_shared: usize, 115 | /// Size of data segments (kB). 116 | pub vm_data: usize, 117 | /// Size of stack segments (kB). 118 | pub vm_stack: usize, 119 | /// Size of text (executable) segments (kB). 120 | pub vm_exe: usize, 121 | /// Shared library code size (kB). 122 | pub vm_lib: usize, 123 | /// Page table entries size (since Linux 2.6.10). 124 | pub vm_pte: usize, 125 | /// Size of second-level page tables (since Linux 4.0). 126 | pub vm_pmd: usize, 127 | /// Swapped-out-virtual memory size (since Linux 2.6.34). 128 | pub vm_swap: usize, 129 | /// Size of hugetlb memory portions (since Linux 4.4). 130 | pub hugetlb_pages: usize, 131 | /// Process's memory is currently being dumped (since Linux 4.15). 132 | pub core_dumping: bool, 133 | /// Number of threads in process containing this thread. 134 | pub threads: u32, 135 | /// The number of currently queued signals for this real user ID 136 | /// (see the description of RLIMIT_SIGPENDING in getrlimit(2)). 137 | pub sig_queued: u64, 138 | /// The resource limit on the number of queued signals for this process. 139 | pub sig_queued_max: u64, 140 | /// Number of signals pending for the thread (see pthreads(7)). 141 | pub sig_pending_thread: u64, 142 | /// Number of signals pending for the process (see signal(7)). 143 | pub sig_pending_process: u64, 144 | /// Mask indicating signals being blocked. 145 | pub sig_blocked: u64, 146 | /// Mask indicating signals being ignored. 147 | pub sig_ignored: u64, 148 | /// Mask indicating signals being caught. 149 | pub sig_caught: u64, 150 | /// Mask of capabilities enabled in inheritable sets (see capabilities(7)). 151 | pub cap_inherited: u64, 152 | /// Mask of capabilities enabled in permitted sets. 153 | pub cap_permitted: u64, 154 | /// Mask of capabilities enabled in effective sets. 155 | pub cap_effective: u64, 156 | /// Capability Bounding set (since Linux 2.6.26). 157 | pub cap_bounding: u64, 158 | /// Ambient capability set (since Linux 4.3). 159 | pub cap_ambient: u64, 160 | /// Whether the process can acquire new privileges (since Linux 4.10) 161 | pub no_new_privs: bool, 162 | /// Secure Computing mode of the process (since Linux 3.8, see seccomp(2)). 163 | /// This field is provided only if the kernel was built with the 164 | /// `CONFIG_SECCOMP` kernel configuration option enabled. 165 | pub seccomp: SeccompMode, 166 | /// CPUs on which this process may run (since Linux 2.6.24, see cpuset(7)). 167 | /// 168 | /// The slice represents a bitmask in the same format as `BitVec`. 169 | pub cpus_allowed: Box<[u8]>, 170 | /// Memory nodes allowed to this process (since Linux 2.6.24, see cpuset(7)). 171 | /// 172 | /// The slice represents a bitmask in the same format as `BitVec`. 173 | pub mems_allowed: Box<[u8]>, 174 | /// Number of voluntary context switches. 175 | pub voluntary_ctxt_switches: u64, 176 | /// Number of involuntary context switches. 177 | pub nonvoluntary_ctxt_switches: u64, 178 | } 179 | 180 | /// Parse the status state format. 181 | named!(parse_status_state, 182 | alt!(tag!("R (running)") => { |_| State::Running } 183 | | tag!("S (sleeping)") => { |_| State::Sleeping } 184 | | tag!("D (disk sleep)") => { |_| State::Waiting } 185 | | tag!("T (stopped)") => { |_| State::Stopped } 186 | | tag!("t (tracing stop)") => { |_| State::TraceStopped } 187 | | tag!("X (dead)") => { |_| State::Dead } 188 | | tag!("Z (zombie)") => { |_| State::Zombie })); 189 | 190 | named!(parse_command, delimited!(tag!("Name:\t"), parse_line, line_ending)); 191 | named!(parse_umask, delimited!(tag!("Umask:\t"), parse_u32_octal, line_ending)); 192 | named!(parse_state, delimited!(tag!("State:\t"), parse_status_state, line_ending)); 193 | named!(parse_pid, delimited!(tag!("Tgid:\t"), parse_i32, line_ending)); 194 | named!(parse_numa_gid, delimited!(tag!("Ngid:\t"), parse_i32, line_ending)); 195 | named!(parse_tid, delimited!(tag!("Pid:\t"), parse_i32, line_ending)); 196 | named!(parse_ppid, delimited!(tag!("PPid:\t"), parse_i32, line_ending)); 197 | named!(parse_tracer_pid, delimited!(tag!("TracerPid:\t"), parse_i32, line_ending)); 198 | 199 | named!(parse_uid<(uid_t, uid_t, uid_t, uid_t)>, chain!(tag!("Uid:\t") ~ real: parse_u32 ~ space ~ effective: parse_u32 200 | ~ space ~ saved: parse_u32 ~ space ~ fs: parse_u32 ~ line_ending, 201 | || { (real, effective, saved, fs) })); 202 | named!(parse_gid<(gid_t, gid_t, gid_t, gid_t)>, chain!(tag!("Gid:\t") ~ real: parse_u32 ~ space ~ effective: parse_u32 203 | ~ space ~ saved: parse_u32 ~ space ~ fs: parse_u32 ~ line_ending, 204 | || { (real, effective, saved, fs) })); 205 | 206 | named!(parse_fd_allocated, delimited!(tag!("FDSize:\t"), parse_u32, line_ending)); 207 | named!(parse_groups >, delimited!(tag!("Groups:\t"), parse_u32s, multispace)); 208 | 209 | named!(parse_ns_pids >, delimited!(tag!("NStgid:\t"), parse_i32s, line_ending)); 210 | named!(parse_ns_tids >, delimited!(tag!("NSpid:\t"), parse_i32s, line_ending)); 211 | named!(parse_ns_pgids >, delimited!(tag!("NSpgid:\t"), parse_i32s, line_ending)); 212 | named!(parse_ns_sids >, delimited!(tag!("NSsid:\t"), parse_i32s, line_ending)); 213 | 214 | named!(parse_vm_peak, delimited!(tag!("VmPeak:"), parse_kb, line_ending)); 215 | named!(parse_vm_size, delimited!(tag!("VmSize:"), parse_kb, line_ending)); 216 | named!(parse_vm_locked, delimited!(tag!("VmLck:"), parse_kb, line_ending)); 217 | named!(parse_vm_pin, delimited!(tag!("VmPin:"), parse_kb, line_ending)); 218 | named!(parse_vm_hwm, delimited!(tag!("VmHWM:"), parse_kb, line_ending)); 219 | named!(parse_vm_rss, delimited!(tag!("VmRSS:"), parse_kb, line_ending)); 220 | named!(parse_vm_rss_anon, delimited!(tag!("RssAnon:"), parse_kb, line_ending)); 221 | named!(parse_vm_rss_file, delimited!(tag!("RssFile:"), parse_kb, line_ending)); 222 | named!(parse_vm_rss_shared, delimited!(tag!("RssShmem:"), parse_kb, line_ending)); 223 | named!(parse_vm_data, delimited!(tag!("VmData:"), parse_kb, line_ending)); 224 | named!(parse_vm_stack, delimited!(tag!("VmStk:"), parse_kb, line_ending)); 225 | named!(parse_vm_exe, delimited!(tag!("VmExe:"), parse_kb, line_ending)); 226 | named!(parse_vm_lib, delimited!(tag!("VmLib:"), parse_kb, line_ending)); 227 | named!(parse_vm_pte, delimited!(tag!("VmPTE:"), parse_kb, line_ending)); 228 | named!(parse_vm_pmd, delimited!(tag!("VmPMD:"), parse_kb, line_ending)); 229 | named!(parse_vm_swap, delimited!(tag!("VmSwap:"), parse_kb, line_ending)); 230 | named!(parse_hugetlb_pages, delimited!(tag!("HugetlbPages:"), parse_kb, line_ending)); 231 | 232 | named!(parse_core_dumping, delimited!(tag!("CoreDumping:\t"), parse_bit, line_ending)); 233 | 234 | named!(parse_threads, delimited!(tag!("Threads:\t"), parse_u32, line_ending)); 235 | 236 | named!(parse_sig_queued<(u64, u64)>, delimited!(tag!("SigQ:\t"), separated_pair!(parse_u64, tag!("/"), parse_u64), line_ending)); 237 | 238 | named!(parse_sig_pending_thread, delimited!(tag!("SigPnd:\t"), parse_u64_hex, line_ending)); 239 | named!(parse_sig_pending_process, delimited!(tag!("ShdPnd:\t"), parse_u64_hex, line_ending)); 240 | named!(parse_sig_blocked, delimited!(tag!("SigBlk:\t"), parse_u64_hex, line_ending)); 241 | named!(parse_sig_ignored, delimited!(tag!("SigIgn:\t"), parse_u64_hex, line_ending)); 242 | named!(parse_sig_caught, delimited!(tag!("SigCgt:\t"), parse_u64_hex, line_ending)); 243 | 244 | named!(parse_cap_inherited, delimited!(tag!("CapInh:\t"), parse_u64_hex, line_ending)); 245 | named!(parse_cap_permitted, delimited!(tag!("CapPrm:\t"), parse_u64_hex, line_ending)); 246 | named!(parse_cap_effective, delimited!(tag!("CapEff:\t"), parse_u64_hex, line_ending)); 247 | named!(parse_cap_bounding, delimited!(tag!("CapBnd:\t"), parse_u64_hex, line_ending)); 248 | named!(parse_cap_ambient, delimited!(tag!("CapAmb:\t"), parse_u64_hex, line_ending)); 249 | 250 | named!(parse_no_new_privs, delimited!(tag!("NoNewPrivs:\t"), parse_bit, line_ending)); 251 | named!(parse_seccomp, delimited!(tag!("Seccomp:\t"), parse_seccomp_mode, line_ending)); 252 | named!(parse_cpus_allowed >, delimited!(tag!("Cpus_allowed:\t"), parse_u32_mask_list, line_ending)); 253 | named!(parse_mems_allowed >, delimited!(tag!("Mems_allowed:\t"), parse_u32_mask_list, line_ending)); 254 | 255 | named!(parse_cpus_allowed_list<()>, chain!(tag!("Cpus_allowed_list:\t") ~ not_line_ending ~ line_ending, || { () })); 256 | named!(parse_mems_allowed_list<()>, chain!(tag!("Mems_allowed_list:\t") ~ not_line_ending ~ line_ending, || { () })); 257 | 258 | named!(parse_voluntary_ctxt_switches, delimited!(tag!("voluntary_ctxt_switches:\t"), parse_u64, line_ending)); 259 | named!(parse_nonvoluntary_ctxt_switches, delimited!(tag!("nonvoluntary_ctxt_switches:\t"), parse_u64, line_ending)); 260 | 261 | /// Parse the status format. 262 | fn parse_status(i: &[u8]) -> IResult<&[u8], Status> { 263 | let mut status: Status = Default::default(); 264 | map!(i, 265 | many0!( // TODO: use a loop here instead of many0 to avoid allocating a vec. 266 | alt!(parse_command => { |value| status.command = value } 267 | | parse_umask => { |value| status.umask = value } 268 | | parse_state => { |value| status.state = value } 269 | | parse_pid => { |value| status.pid = value } 270 | | parse_numa_gid => { |value| status.numa_gid = value } 271 | | parse_tid => { |value| status.tid = value } 272 | | parse_ppid => { |value| status.ppid = value } 273 | | parse_tracer_pid => { |value| status.tracer_pid = value } 274 | | parse_uid => { |(real, effective, saved, fs)| { status.uid_real = real; 275 | status.uid_effective = effective; 276 | status.uid_saved = saved; 277 | status.uid_fs = fs; } } 278 | | parse_gid => { |(real, effective, saved, fs)| { status.gid_real = real; 279 | status.gid_effective = effective; 280 | status.gid_saved = saved; 281 | status.gid_fs = fs; } } 282 | | parse_fd_allocated => { |value| status.fd_allocated = value } 283 | | parse_groups => { |value| status.groups = value } 284 | | parse_ns_pids => { |value| status.ns_pids = value } 285 | | parse_ns_tids => { |value| status.ns_tids = value } 286 | | parse_ns_pgids => { |value| status.ns_pgids = value } 287 | | parse_ns_sids => { |value| status.ns_sids = value } 288 | | parse_vm_peak => { |value| status.vm_peak = value } 289 | | parse_vm_size => { |value| status.vm_size = value } 290 | | parse_vm_locked => { |value| status.vm_locked = value } 291 | | parse_vm_pin => { |value| status.vm_pin = value } 292 | | parse_vm_hwm => { |value| status.vm_hwm = value } 293 | | parse_vm_rss => { |value| status.vm_rss = value } 294 | | parse_vm_rss_anon => { |value| status.vm_rss_anon = value } 295 | | parse_vm_rss_file => { |value| status.vm_rss_file = value } 296 | | parse_vm_rss_shared => { |value| status.vm_rss_shared = value } 297 | | parse_vm_data => { |value| status.vm_data = value } 298 | | parse_vm_stack => { |value| status.vm_stack = value } 299 | | parse_vm_exe => { |value| status.vm_exe = value } 300 | | parse_vm_lib => { |value| status.vm_lib = value } 301 | | parse_vm_pte => { |value| status.vm_pte = value } 302 | | parse_vm_pmd => { |value| status.vm_pmd = value } 303 | | parse_vm_swap => { |value| status.vm_swap = value } 304 | | parse_hugetlb_pages => { |value| status.hugetlb_pages = value } 305 | | parse_core_dumping => { |value| status.core_dumping = value } 306 | 307 | | parse_threads => { |value| status.threads = value } 308 | | parse_sig_queued => { |(count, max)| { status.sig_queued = count; 309 | status.sig_queued_max = max } } 310 | | parse_sig_pending_thread => { |value| status.sig_pending_thread = value } 311 | | parse_sig_pending_process => { |value| status.sig_pending_process = value } 312 | | parse_sig_blocked => { |value| status.sig_blocked = value } 313 | | parse_sig_ignored => { |value| status.sig_ignored = value } 314 | | parse_sig_caught => { |value| status.sig_caught = value } 315 | 316 | | parse_cap_inherited => { |value| status.cap_inherited = value } 317 | | parse_cap_permitted => { |value| status.cap_permitted = value } 318 | | parse_cap_effective => { |value| status.cap_effective = value } 319 | | parse_cap_bounding => { |value| status.cap_bounding = value } 320 | | parse_cap_ambient => { |value| status.cap_ambient = value } 321 | 322 | | parse_no_new_privs => { |value| status.no_new_privs = value } 323 | | parse_seccomp => { |value| status.seccomp = value } 324 | | parse_cpus_allowed => { |value| status.cpus_allowed = value } 325 | | parse_cpus_allowed_list 326 | | parse_mems_allowed => { |value| status.mems_allowed = value } 327 | | parse_mems_allowed_list 328 | | parse_voluntary_ctxt_switches => { |value| status.voluntary_ctxt_switches = value } 329 | | parse_nonvoluntary_ctxt_switches => { |value| status.nonvoluntary_ctxt_switches = value } 330 | ) 331 | ), 332 | { |_| { status }}) 333 | } 334 | 335 | /// Parses the provided status file. 336 | fn status_file(file: &mut File) -> Result { 337 | let mut buf = [0; 2048]; // A typical status file is about 1000 bytes 338 | map_result(parse_status(try!(read_to_end(file, &mut buf)))) 339 | } 340 | 341 | /// Returns memory status information for the process with the provided pid. 342 | pub fn status(pid: pid_t) -> Result { 343 | status_file(&mut try!(File::open(&format!("/proc/{}/status", pid)))) 344 | } 345 | 346 | /// Returns memory status information for the current process. 347 | pub fn status_self() -> Result { 348 | status_file(&mut try!(File::open("/proc/self/status"))) 349 | } 350 | 351 | /// Returns memory status information from the thread with the provided parent process ID and thread ID. 352 | pub fn status_task(process_id: pid_t, thread_id: pid_t) -> Result { 353 | status_file(&mut try!(File::open(&format!("/proc/{}/task/{}/status", process_id, thread_id)))) 354 | } 355 | 356 | #[cfg(test)] 357 | mod tests { 358 | use parsers::tests::unwrap; 359 | use super::{SeccompMode, parse_status, status, status_self}; 360 | use pid::State; 361 | 362 | /// Test that the system status files can be parsed. 363 | #[test] 364 | fn test_status() { 365 | status_self().unwrap(); 366 | status(1).unwrap(); 367 | } 368 | 369 | #[test] 370 | fn test_parse_status() { 371 | let status_text = b"Name:\tsystemd\n\ 372 | Umask:\t0022\n\ 373 | State:\tS (sleeping)\n\ 374 | Tgid:\t1\n\ 375 | Ngid:\t0\n\ 376 | Pid:\t1\n\ 377 | PPid:\t0\n\ 378 | TracerPid:\t0\n\ 379 | Uid:\t0\t0\t0\t0\n\ 380 | Gid:\t0\t0\t0\t0\n\ 381 | FDSize:\t64\n\ 382 | Groups:\t10\t1000\n\ 383 | NStgid:\t1\n\ 384 | NSpid:\t1\n\ 385 | NSpgid:\t1\n\ 386 | NSsid:\t1\n\ 387 | VmPeak:\t10927688 kB\n\ 388 | VmSize:\t 47348 kB\n\ 389 | VmLck:\t 0 kB\n\ 390 | VmPin:\t 0 kB\n\ 391 | VmHWM:\t 9212 kB\n\ 392 | VmRSS:\t 9212 kB\n\ 393 | RssAnon:\t 3700 kB\n\ 394 | RssFile:\t 5768 kB\n\ 395 | RssShmem:\t 0 kB\n\ 396 | VmData:\t 3424 kB\n\ 397 | VmStk:\t 136 kB\n\ 398 | VmExe:\t 1320 kB\n\ 399 | VmLib:\t 3848 kB\n\ 400 | VmPTE:\t 108 kB\n\ 401 | VmPMD:\t 12 kB\n\ 402 | VmSwap:\t 0 kB\n\ 403 | HugetlbPages:\t 0 kB\n\ 404 | CoreDumping:\t0\n\ 405 | Threads:\t1\n\ 406 | SigQ:\t0/257232\n\ 407 | SigPnd:\t0000000000000000\n\ 408 | ShdPnd:\t0000000000000000\n\ 409 | SigBlk:\t7be3c0fe28014a03\n\ 410 | SigIgn:\t0000000000001000\n\ 411 | SigCgt:\t00000001800004ec\n\ 412 | CapInh:\t0000000000000000\n\ 413 | CapPrm:\t0000003fffffffff\n\ 414 | CapEff:\t0000003fffffffff\n\ 415 | CapBnd:\t0000003fffffffff\n\ 416 | CapAmb:\t0000000000000000\n\ 417 | NoNewPrivs:\t0\n\ 418 | Seccomp:\t0\n\ 419 | Cpus_allowed:\tffff\n\ 420 | Cpus_allowed_list:\t0-15\n\ 421 | Mems_allowed:\t00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001\n\ 422 | Mems_allowed_list:\t0\n\ 423 | voluntary_ctxt_switches:\t242129\n\ 424 | nonvoluntary_ctxt_switches:\t1748\n"; 425 | 426 | let status = unwrap(parse_status(status_text)); 427 | assert_eq!("systemd", status.command); 428 | assert_eq!(18, status.umask); 429 | assert_eq!(State::Sleeping, status.state); 430 | assert_eq!(1, status.pid); 431 | assert_eq!(0, status.numa_gid); 432 | assert_eq!(1, status.tid); 433 | assert_eq!(0, status.ppid); 434 | assert_eq!(0, status.tracer_pid); 435 | assert_eq!(0, status.uid_real); 436 | assert_eq!(0, status.uid_effective); 437 | assert_eq!(0, status.uid_saved); 438 | assert_eq!(0, status.uid_fs); 439 | assert_eq!(0, status.gid_real); 440 | assert_eq!(0, status.gid_effective); 441 | assert_eq!(0, status.gid_saved); 442 | assert_eq!(0, status.gid_fs); 443 | assert_eq!(64, status.fd_allocated); 444 | assert_eq!(vec![10, 1000], status.groups); 445 | assert_eq!(vec![1], status.ns_pids); 446 | assert_eq!(vec![1], status.ns_tids); 447 | assert_eq!(vec![1], status.ns_pgids); 448 | assert_eq!(vec![1], status.ns_sids); 449 | assert_eq!(10927688, status.vm_peak); 450 | assert_eq!(47348, status.vm_size); 451 | assert_eq!(0, status.vm_locked); 452 | assert_eq!(0, status.vm_pin); 453 | assert_eq!(9212, status.vm_hwm); 454 | assert_eq!(9212, status.vm_rss); 455 | assert_eq!(3700, status.vm_rss_anon); 456 | assert_eq!(5768, status.vm_rss_file); 457 | assert_eq!(0, status.vm_rss_shared); 458 | assert_eq!(3424, status.vm_data); 459 | assert_eq!(136, status.vm_stack); 460 | assert_eq!(1320, status.vm_exe); 461 | assert_eq!(3848, status.vm_lib); 462 | assert_eq!(108, status.vm_pte); 463 | assert_eq!(12, status.vm_pmd); 464 | assert_eq!(0, status.vm_swap); 465 | assert_eq!(0, status.hugetlb_pages); 466 | assert_eq!(false, status.core_dumping); 467 | assert_eq!(1, status.threads); 468 | assert_eq!(0, status.sig_queued); 469 | assert_eq!(257232, status.sig_queued_max); 470 | assert_eq!(0x0000000000000000, status.sig_pending_thread); 471 | assert_eq!(0x0000000000000000, status.sig_pending_process); 472 | assert_eq!(0x7be3c0fe28014a03, status.sig_blocked); 473 | assert_eq!(0x0000000000001000, status.sig_ignored); 474 | assert_eq!(0x00000001800004ec, status.sig_caught); 475 | assert_eq!(0x0000000000000000, status.cap_inherited); 476 | assert_eq!(0x0000003fffffffff, status.cap_permitted); 477 | assert_eq!(0x0000003fffffffff, status.cap_effective); 478 | assert_eq!(0x0000003fffffffff, status.cap_bounding); 479 | assert_eq!(0x0000000000000000, status.cap_ambient); 480 | assert_eq!(false, status.no_new_privs); 481 | assert_eq!(SeccompMode::Disabled, status.seccomp); 482 | assert_eq!(&[0xff, 0xff, 0x00, 0x00], &*status.cpus_allowed); 483 | let mems_allowed: &mut [u8] = &mut [0; 64]; 484 | mems_allowed[0] = 0x80; 485 | assert_eq!(mems_allowed, &*status.mems_allowed); 486 | assert_eq!(242129, status.voluntary_ctxt_switches); 487 | assert_eq!(1748, status.nonvoluntary_ctxt_switches); 488 | } 489 | } 490 | 491 | #[cfg(all(test, rustc_nightly))] 492 | mod benches { 493 | extern crate test; 494 | 495 | use std::fs::File; 496 | 497 | use parsers::read_to_end; 498 | use super::{parse_status, status}; 499 | 500 | #[bench] 501 | fn bench_status(b: &mut test::Bencher) { 502 | b.iter(|| test::black_box(status(1))); 503 | } 504 | 505 | #[bench] 506 | fn bench_status_parse(b: &mut test::Bencher) { 507 | let mut buf = [0; 2048]; 508 | let status = read_to_end(&mut File::open("/proc/1/status").unwrap(), &mut buf).unwrap(); 509 | b.iter(|| test::black_box(parse_status(status))); 510 | } 511 | } 512 | --------------------------------------------------------------------------------