use std::fs::File;
use std::io::Result;
use std::os::unix::raw::{gid_t, pid_t, uid_t};
use nom::{IResult, line_ending, not_line_ending, space};
use State;
use parsers::{map_result, parse_i32, parse_i32s, parse_kb, parse_to_end, parse_u32,
parse_u32_mask_list, parse_u32s, parse_u64, parse_u64_hex, read_to_end};
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum SeccompMode {
Disabled,
Strict,
Filter,
}
impl Default for SeccompMode {
fn default() -> SeccompMode {
SeccompMode::Disabled
}
}
named!(parse_seccomp_mode<SeccompMode>,
alt!(tag!("0") => { |_| SeccompMode::Disabled }
| tag!("1") => { |_| SeccompMode::Strict }
| tag!("2") => { |_| SeccompMode::Filter }));
#[derive(Default, Debug, PartialEq, Eq, Hash)]
pub struct Status {
pub command: String,
pub state: State,
pub pid: pid_t,
pub numa_gid: pid_t,
pub tid: pid_t,
pub ppid: pid_t,
pub tracer_pid: pid_t,
pub uid_real: uid_t,
pub uid_effective: uid_t,
pub uid_saved: uid_t,
pub uid_fs: uid_t,
pub gid_real: gid_t,
pub gid_effective: gid_t,
pub gid_saved: gid_t,
pub gid_fs: gid_t,
pub fd_allocated: u32,
pub groups: Vec<gid_t>,
pub ns_pids: Vec<pid_t>,
pub ns_tids: Vec<pid_t>,
pub ns_pgids: Vec<pid_t>,
pub ns_sids: Vec<pid_t>,
pub vm_peak: usize,
pub vm_size: usize,
pub vm_locked: usize,
pub vm_pin: usize,
pub vm_hwm: usize,
pub vm_rss: usize,
pub vm_data: usize,
pub vm_stack: usize,
pub vm_exe: usize,
pub vm_lib: usize,
pub vm_pte: usize,
pub vm_pmd: usize,
pub vm_swap: usize,
pub threads: u32,
pub sig_queued: u64,
pub sig_queued_max: u64,
pub sig_pending_thread: u64,
pub sig_pending_process: u64,
pub sig_blocked: u64,
pub sig_ignored: u64,
pub sig_caught: u64,
pub cap_inherited: u64,
pub cap_permitted: u64,
pub cap_effective: u64,
pub cap_bounding: u64,
pub seccomp: SeccompMode,
pub cpus_allowed: Box<[u8]>,
pub mems_allowed: Box<[u8]>,
pub voluntary_ctxt_switches: u64,
pub nonvoluntary_ctxt_switches: u64,
}
named!(parse_status_state<State>,
alt!(tag!("R (running)") => { |_| State::Running }
| tag!("S (sleeping)") => { |_| State::Sleeping }
| tag!("D (disk sleep)") => { |_| State::Waiting }
| tag!("Z (zombie)") => { |_| State::Zombie }
| tag!("X (dead)") => { |_| State::Dead }));
named!(parse_command<String>, delimited!(tag!("Name:\t"), parse_to_end, line_ending));
named!(parse_state<State>, delimited!(tag!("State:\t"), parse_status_state, line_ending));
named!(parse_pid<pid_t>, delimited!(tag!("Tgid:\t"), parse_i32, line_ending));
named!(parse_numa_gid<pid_t>, delimited!(tag!("Ngid:\t"), parse_i32, line_ending));
named!(parse_tid<pid_t>, delimited!(tag!("Pid:\t"), parse_i32, line_ending));
named!(parse_ppid<pid_t>, delimited!(tag!("PPid:\t"), parse_i32, line_ending));
named!(parse_tracer_pid<pid_t>, delimited!(tag!("TracerPid:\t"), parse_i32, line_ending));
named!(parse_uid<(uid_t, uid_t, uid_t, uid_t)>, chain!(tag!("Uid:\t") ~ real: parse_u32 ~ space ~ effective: parse_u32
~ space ~ saved: parse_u32 ~ space ~ fs: parse_u32 ~ line_ending,
|| { (real, effective, saved, fs) }));
named!(parse_gid<(gid_t, gid_t, gid_t, gid_t)>, chain!(tag!("Gid:\t") ~ real: parse_u32 ~ space ~ effective: parse_u32
~ space ~ saved: parse_u32 ~ space ~ fs: parse_u32 ~ line_ending,
|| { (real, effective, saved, fs) }));
named!(parse_fd_allocated<u32>, delimited!(tag!("FDSize:\t"), parse_u32, line_ending));
named!(parse_groups<Vec<gid_t> >, delimited!(tag!("Groups:\t"), parse_u32s, line_ending));
named!(parse_ns_pids<Vec<pid_t> >, delimited!(tag!("NStgid:\t"), parse_i32s, line_ending));
named!(parse_ns_tids<Vec<pid_t> >, delimited!(tag!("NSpid:\t"), parse_i32s, line_ending));
named!(parse_ns_pgids<Vec<pid_t> >, delimited!(tag!("NSpgid:\t"), parse_i32s, line_ending));
named!(parse_ns_sids<Vec<pid_t> >, delimited!(tag!("NSsid:\t"), parse_i32s, line_ending));
named!(parse_vm_peak<usize>, delimited!(tag!("VmPeak:\t"), parse_kb, line_ending));
named!(parse_vm_size<usize>, delimited!(tag!("VmSize:\t"), parse_kb, line_ending));
named!(parse_vm_locked<usize>, delimited!(tag!("VmLck:\t"), parse_kb, line_ending));
named!(parse_vm_pin<usize>, delimited!(tag!("VmPin:\t"), parse_kb, line_ending));
named!(parse_vm_hwm<usize>, delimited!(tag!("VmHWM:\t"), parse_kb, line_ending));
named!(parse_vm_rss<usize>, delimited!(tag!("VmRSS:\t"), parse_kb, line_ending));
named!(parse_vm_data<usize>, delimited!(tag!("VmData:\t"), parse_kb, line_ending));
named!(parse_vm_stack<usize>, delimited!(tag!("VmStk:\t"), parse_kb, line_ending));
named!(parse_vm_exe<usize>, delimited!(tag!("VmExe:\t"), parse_kb, line_ending));
named!(parse_vm_lib<usize>, delimited!(tag!("VmLib:\t"), parse_kb, line_ending));
named!(parse_vm_pte<usize>, delimited!(tag!("VmPTE:\t"), parse_kb, line_ending));
named!(parse_vm_pmd<usize>, delimited!(tag!("VmPMD:\t"), parse_kb, line_ending));
named!(parse_vm_swap<usize>, delimited!(tag!("VmSwap:\t"), parse_kb, line_ending));
named!(parse_threads<u32>, delimited!(tag!("Threads:\t"), parse_u32, line_ending));
named!(parse_sig_queued<(u64, u64)>, delimited!(tag!("SigQ:\t"), separated_pair!(parse_u64, tag!("/"), parse_u64), line_ending));
named!(parse_sig_pending_thread<u64>, delimited!(tag!("SigPnd:\t"), parse_u64_hex, line_ending));
named!(parse_sig_pending_process<u64>, delimited!(tag!("ShdPnd:\t"), parse_u64_hex, line_ending));
named!(parse_sig_blocked<u64>, delimited!(tag!("SigBlk:\t"), parse_u64_hex, line_ending));
named!(parse_sig_ignored<u64>, delimited!(tag!("SigIgn:\t"), parse_u64_hex, line_ending));
named!(parse_sig_caught<u64>, delimited!(tag!("SigCgt:\t"), parse_u64_hex, line_ending));
named!(parse_cap_inherited<u64>, delimited!(tag!("CapInh:\t"), parse_u64_hex, line_ending));
named!(parse_cap_permitted<u64>, delimited!(tag!("CapPrm:\t"), parse_u64_hex, line_ending));
named!(parse_cap_effective<u64>, delimited!(tag!("CapEff:\t"), parse_u64_hex, line_ending));
named!(parse_cap_bounding<u64>, delimited!(tag!("CapBnd:\t"), parse_u64_hex, line_ending));
named!(parse_seccomp<SeccompMode>, delimited!(tag!("Seccomp:\t"), parse_seccomp_mode, line_ending));
named!(parse_cpus_allowed<Box<[u8]> >, delimited!(tag!("Cpus_allowed:\t"), parse_u32_mask_list, line_ending));
named!(parse_mems_allowed<Box<[u8]> >, delimited!(tag!("Mems_allowed:\t"), parse_u32_mask_list, line_ending));
named!(parse_cpus_allowed_list<()>, chain!(tag!("Cpus_allowed_list:\t") ~ not_line_ending ~ line_ending, || { () }));
named!(parse_mems_allowed_list<()>, chain!(tag!("Mems_allowed_list:\t") ~ not_line_ending ~ line_ending, || { () }));
named!(parse_voluntary_ctxt_switches<u64>, delimited!(tag!("voluntary_ctxt_switches:\t"), parse_u64, line_ending));
named!(parse_nonvoluntary_ctxt_switches<u64>, delimited!(tag!("nonvoluntary_ctxt_switches:\t"), parse_u64, line_ending));
fn parse_status<'a>(i: &'a [u8]) -> IResult<'a, &'a [u8], Status> {
let mut status: Status = Default::default();
map!(i,
many0!( alt!(parse_command => { |value| status.command = value }
| parse_state => { |value| status.state = value }
| parse_pid => { |value| status.pid = value }
| parse_numa_gid => { |value| status.numa_gid = value }
| parse_tid => { |value| status.tid = value }
| parse_ppid => { |value| status.ppid = value }
| parse_tracer_pid => { |value| status.tracer_pid = value }
| parse_uid => { |(real, effective, saved, fs)| { status.uid_real = real;
status.uid_effective = effective;
status.uid_saved = saved;
status.uid_fs = fs; } }
| parse_gid => { |(real, effective, saved, fs)| { status.gid_real = real;
status.gid_effective = effective;
status.gid_saved = saved;
status.gid_fs = fs; } }
| parse_fd_allocated => { |value| status.fd_allocated = value }
| parse_groups => { |value| status.groups = value }
| parse_ns_pids => { |value| status.ns_pids = value }
| parse_ns_tids => { |value| status.ns_tids = value }
| parse_ns_pgids => { |value| status.ns_pgids = value }
| parse_ns_sids => { |value| status.ns_sids = value }
| parse_vm_peak => { |value| status.vm_peak = value }
| parse_vm_size => { |value| status.vm_size = value }
| parse_vm_locked => { |value| status.vm_locked = value }
| parse_vm_pin => { |value| status.vm_pin = value }
| parse_vm_hwm => { |value| status.vm_hwm = value }
| parse_vm_rss => { |value| status.vm_rss = value }
| parse_vm_data => { |value| status.vm_data = value }
| parse_vm_stack => { |value| status.vm_stack = value }
| parse_vm_exe => { |value| status.vm_exe = value }
| parse_vm_lib => { |value| status.vm_lib = value }
| parse_vm_pte => { |value| status.vm_pte = value }
| parse_vm_pmd => { |value| status.vm_pmd = value }
| parse_vm_swap => { |value| status.vm_swap = value }
| parse_threads => { |value| status.threads = value }
| parse_sig_queued => { |(count, max)| { status.sig_queued = count;
status.sig_queued_max = max } }
| parse_sig_pending_thread => { |value| status.sig_pending_thread = value }
| parse_sig_pending_process => { |value| status.sig_pending_process = value }
| parse_sig_blocked => { |value| status.sig_blocked = value }
| parse_sig_ignored => { |value| status.sig_ignored = value }
| parse_sig_caught => { |value| status.sig_caught = value }
| parse_cap_inherited => { |value| status.cap_inherited = value }
| parse_cap_permitted => { |value| status.cap_permitted = value }
| parse_cap_effective => { |value| status.cap_effective = value }
| parse_cap_bounding => { |value| status.cap_bounding = value }
| parse_seccomp => { |value| status.seccomp = value }
| parse_cpus_allowed => { |value| status.cpus_allowed = value }
| parse_cpus_allowed_list
| parse_mems_allowed => { |value| status.mems_allowed = value }
| parse_mems_allowed_list
| parse_voluntary_ctxt_switches => { |value| status.voluntary_ctxt_switches = value }
| parse_nonvoluntary_ctxt_switches => { |value| status.nonvoluntary_ctxt_switches = value }
)
),
{ |_| { status }})
}
fn status_file(file: &mut File) -> Result<Status> {
let mut buf = [0; 2048]; map_result(parse_status(try!(read_to_end(file, &mut buf))))
}
pub fn status(pid: pid_t) -> Result<Status> {
status_file(&mut try!(File::open(&format!("/proc/{}/status", pid))))
}
pub fn status_self() -> Result<Status> {
status_file(&mut try!(File::open("/proc/self/status")))
}
#[cfg(test)]
mod tests {
extern crate test;
use std::fs::File;
use std::os::unix::raw::gid_t;
use parsers::read_to_end;
use parsers::tests::unwrap;
use super::{SeccompMode, parse_status, status, status_self};
use State;
#[test]
fn test_status() {
status_self().unwrap();
status(1).unwrap();
}
#[test]
fn test_parse_status() {
let status_text = b"Name:\tsystemd\n\
State:\tS (sleeping)\n\
Tgid:\t1\n\
Ngid:\t0\n\
Pid:\t1\n\
PPid:\t0\n\
TracerPid:\t0\n\
Uid:\t0\t0\t0\t0\n\
Gid:\t0\t0\t0\t0\n\
FDSize:\t64\n\
Groups:\t\n\
NStgid:\t1\n\
NSpid:\t1\n\
NSpgid:\t1\n\
NSsid:\t1\n\
VmPeak:\t 48784 kB\n\
VmSize:\t 47348 kB\n\
VmLck:\t 0 kB\n\
VmPin:\t 0 kB\n\
VmHWM:\t 9212 kB\n\
VmRSS:\t 9212 kB\n\
VmData:\t 3424 kB\n\
VmStk:\t 136 kB\n\
VmExe:\t 1320 kB\n\
VmLib:\t 3848 kB\n\
VmPTE:\t 108 kB\n\
VmPMD:\t 12 kB\n\
VmSwap:\t 0 kB\n\
Threads:\t1\n\
SigQ:\t0/257232\n\
SigPnd:\t0000000000000000\n\
ShdPnd:\t0000000000000000\n\
SigBlk:\t7be3c0fe28014a03\n\
SigIgn:\t0000000000001000\n\
SigCgt:\t00000001800004ec\n\
CapInh:\t0000000000000000\n\
CapPrm:\t0000003fffffffff\n\
CapEff:\t0000003fffffffff\n\
CapBnd:\t0000003fffffffff\n\
Seccomp:\t0\n\
Cpus_allowed:\tffff\n\
Cpus_allowed_list:\t0-15\n\
Mems_allowed:\t00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001\n\
Mems_allowed_list:\t0\n\
voluntary_ctxt_switches:\t242129\n\
nonvoluntary_ctxt_switches:\t1748\n";
let status = unwrap(parse_status(status_text));
assert_eq!("systemd", status.command);
assert_eq!(State::Sleeping, status.state);
assert_eq!(1, status.pid);
assert_eq!(0, status.numa_gid);
assert_eq!(1, status.tid);
assert_eq!(0, status.ppid);
assert_eq!(0, status.tracer_pid);
assert_eq!(0, status.uid_real);
assert_eq!(0, status.uid_effective);
assert_eq!(0, status.uid_saved);
assert_eq!(0, status.uid_fs);
assert_eq!(0, status.gid_real);
assert_eq!(0, status.gid_effective);
assert_eq!(0, status.gid_saved);
assert_eq!(0, status.gid_fs);
assert_eq!(64, status.fd_allocated);
assert_eq!(Vec::<gid_t>::new(), status.groups);
assert_eq!(vec![1], status.ns_pids);
assert_eq!(vec![1], status.ns_tids);
assert_eq!(vec![1], status.ns_pgids);
assert_eq!(vec![1], status.ns_sids);
assert_eq!(48784, status.vm_peak);
assert_eq!(47348, status.vm_size);
assert_eq!(0, status.vm_locked);
assert_eq!(0, status.vm_pin);
assert_eq!(9212, status.vm_hwm);
assert_eq!(9212, status.vm_rss);
assert_eq!(3424, status.vm_data);
assert_eq!(136, status.vm_stack);
assert_eq!(1320, status.vm_exe);
assert_eq!(3848, status.vm_lib);
assert_eq!(108, status.vm_pte);
assert_eq!(12, status.vm_pmd);
assert_eq!(0, status.vm_swap);
assert_eq!(1, status.threads);
assert_eq!(0, status.sig_queued);
assert_eq!(257232, status.sig_queued_max);
assert_eq!(0x0000000000000000, status.sig_pending_thread);
assert_eq!(0x0000000000000000, status.sig_pending_process);
assert_eq!(0x7be3c0fe28014a03, status.sig_blocked);
assert_eq!(0x0000000000001000, status.sig_ignored);
assert_eq!(0x00000001800004ec, status.sig_caught);
assert_eq!(0x0000000000000000, status.cap_inherited);
assert_eq!(0x0000003fffffffff, status.cap_permitted);
assert_eq!(0x0000003fffffffff, status.cap_effective);
assert_eq!(0x0000003fffffffff, status.cap_bounding);
assert_eq!(SeccompMode::Disabled, status.seccomp);
assert_eq!(&[0xff, 0xff, 0x00, 0x00], &*status.cpus_allowed);
let mems_allowed: &mut [u8] = &mut [0; 64];
mems_allowed[0] = 0x80;
assert_eq!(mems_allowed, &*status.mems_allowed);
assert_eq!(242129, status.voluntary_ctxt_switches);
assert_eq!(1748, status.nonvoluntary_ctxt_switches);
}
#[bench]
fn bench_status(b: &mut test::Bencher) {
b.iter(|| test::black_box(status(1)));
}
#[bench]
fn bench_status_parse(b: &mut test::Bencher) {
let mut buf = [0; 2048];
let status = read_to_end(&mut File::open("/proc/1/status").unwrap(), &mut buf).unwrap();
b.iter(|| test::black_box(parse_status(status)));
}
}