use crate::{FromStrRadix, ProcResult};
use std::collections::HashMap;
use std::io::{BufRead, BufReader, Read};
#[derive(Debug, Clone)]
pub struct Status {
pub name: String,
pub umask: Option<u32>,
pub state: String,
pub tgid: i32,
pub ngid: Option<i32>,
pub pid: i32,
pub ppid: i32,
pub tracerpid: i32,
pub ruid: u32,
pub euid: u32,
pub suid: u32,
pub fuid: u32,
pub rgid: u32,
pub egid: u32,
pub sgid: u32,
pub fgid: u32,
pub fdsize: u32,
pub groups: Vec<i32>,
pub nstgid: Option<Vec<i32>>,
pub nspid: Option<Vec<i32>>,
pub nspgid: Option<Vec<i32>>,
pub nssid: Option<Vec<i32>>,
pub vmpeak: Option<u64>,
pub vmsize: Option<u64>,
pub vmlck: Option<u64>,
pub vmpin: Option<u64>,
pub vmhwm: Option<u64>,
pub vmrss: Option<u64>,
pub rssanon: Option<u64>,
pub rssfile: Option<u64>,
pub rssshmem: Option<u64>,
pub vmdata: Option<u64>,
pub vmstk: Option<u64>,
pub vmexe: Option<u64>,
pub vmlib: Option<u64>,
pub vmpte: Option<u64>,
pub vmswap: Option<u64>,
pub hugetblpages: Option<u64>,
pub threads: u64,
pub sigq: (u64, u64),
pub sigpnd: u64,
pub shdpnd: u64,
pub sigblk: u64,
pub sigign: u64,
pub sigcgt: u64,
pub capinh: u64,
pub capprm: u64,
pub capeff: u64,
pub capbnd: Option<u64>,
pub capamb: Option<u64>,
pub nonewprivs: Option<u64>,
pub seccomp: Option<u32>,
pub speculation_store_bypass: Option<String>,
pub cpus_allowed: Option<Vec<u32>>,
pub cpus_allowed_list: Option<Vec<(u32, u32)>>,
pub mems_allowed: Option<Vec<u32>>,
pub mems_allowed_list: Option<Vec<(u32, u32)>>,
pub voluntary_ctxt_switches: Option<u64>,
pub nonvoluntary_ctxt_switches: Option<u64>,
pub core_dumping: Option<bool>,
pub thp_enabled: Option<bool>,
}
impl Status {
pub fn from_reader<R: Read>(r: R) -> ProcResult<Status> {
let mut map = HashMap::new();
let reader = BufReader::new(r);
for line in reader.lines() {
let line = line?;
if line.is_empty() {
continue;
}
let mut s = line.split(':');
let field = expect!(s.next());
let value = expect!(s.next()).trim();
map.insert(field.to_string(), value.to_string());
}
let status = Status {
name: expect!(map.remove("Name")),
umask: map
.remove("Umask")
.map(|x| Ok(from_str!(u32, &x, 8)))
.transpose()?,
state: expect!(map.remove("State")),
tgid: from_str!(i32, &expect!(map.remove("Tgid"))),
ngid: map
.remove("Ngid")
.map(|x| Ok(from_str!(i32, &x)))
.transpose()?,
pid: from_str!(i32, &expect!(map.remove("Pid"))),
ppid: from_str!(i32, &expect!(map.remove("PPid"))),
tracerpid: from_str!(i32, &expect!(map.remove("TracerPid"))),
ruid: expect!(Status::parse_uid_gid(&expect!(map.get("Uid")), 0)),
euid: expect!(Status::parse_uid_gid(&expect!(map.get("Uid")), 1)),
suid: expect!(Status::parse_uid_gid(&expect!(map.get("Uid")), 2)),
fuid: expect!(Status::parse_uid_gid(&expect!(map.remove("Uid")), 3)),
rgid: expect!(Status::parse_uid_gid(&expect!(map.get("Gid")), 0)),
egid: expect!(Status::parse_uid_gid(&expect!(map.get("Gid")), 1)),
sgid: expect!(Status::parse_uid_gid(&expect!(map.get("Gid")), 2)),
fgid: expect!(Status::parse_uid_gid(&expect!(map.remove("Gid")), 3)),
fdsize: from_str!(u32, &expect!(map.remove("FDSize"))),
groups: Status::parse_list(&expect!(map.remove("Groups")))?,
nstgid: map
.remove("NStgid")
.map(|x| Status::parse_list(&x))
.transpose()?,
nspid: map
.remove("NSpid")
.map(|x| Status::parse_list(&x))
.transpose()?,
nspgid: map
.remove("NSpgid")
.map(|x| Status::parse_list(&x))
.transpose()?,
nssid: map
.remove("NSsid")
.map(|x| Status::parse_list(&x))
.transpose()?,
vmpeak: Status::parse_with_kb(map.remove("VmPeak"))?,
vmsize: Status::parse_with_kb(map.remove("VmSize"))?,
vmlck: Status::parse_with_kb(map.remove("VmLck"))?,
vmpin: Status::parse_with_kb(map.remove("VmPin"))?,
vmhwm: Status::parse_with_kb(map.remove("VmHWM"))?,
vmrss: Status::parse_with_kb(map.remove("VmRSS"))?,
rssanon: Status::parse_with_kb(map.remove("RssAnon"))?,
rssfile: Status::parse_with_kb(map.remove("RssFile"))?,
rssshmem: Status::parse_with_kb(map.remove("RssShmem"))?,
vmdata: Status::parse_with_kb(map.remove("VmData"))?,
vmstk: Status::parse_with_kb(map.remove("VmStk"))?,
vmexe: Status::parse_with_kb(map.remove("VmExe"))?,
vmlib: Status::parse_with_kb(map.remove("VmLib"))?,
vmpte: Status::parse_with_kb(map.remove("VmPTE"))?,
vmswap: Status::parse_with_kb(map.remove("VmSwap"))?,
hugetblpages: Status::parse_with_kb(map.remove("HugetlbPages"))?,
threads: from_str!(u64, &expect!(map.remove("Threads"))),
sigq: expect!(Status::parse_sigq(&expect!(map.remove("SigQ")))),
sigpnd: from_str!(u64, &expect!(map.remove("SigPnd")), 16),
shdpnd: from_str!(u64, &expect!(map.remove("ShdPnd")), 16),
sigblk: from_str!(u64, &expect!(map.remove("SigBlk")), 16),
sigign: from_str!(u64, &expect!(map.remove("SigIgn")), 16),
sigcgt: from_str!(u64, &expect!(map.remove("SigCgt")), 16),
capinh: from_str!(u64, &expect!(map.remove("CapInh")), 16),
capprm: from_str!(u64, &expect!(map.remove("CapPrm")), 16),
capeff: from_str!(u64, &expect!(map.remove("CapEff")), 16),
capbnd: map
.remove("CapBnd")
.map(|x| Ok(from_str!(u64, &x, 16)))
.transpose()?,
capamb: map
.remove("CapAmb")
.map(|x| Ok(from_str!(u64, &x, 16)))
.transpose()?,
nonewprivs: map
.remove("NoNewPrivs")
.map(|x| Ok(from_str!(u64, &x)))
.transpose()?,
seccomp: map
.remove("Seccomp")
.map(|x| Ok(from_str!(u32, &x)))
.transpose()?,
speculation_store_bypass: map.remove("Speculation_Store_Bypass"),
cpus_allowed: map
.remove("Cpus_allowed")
.map(|x| Status::parse_allowed(&x))
.transpose()?,
cpus_allowed_list: map
.remove("Cpus_allowed_list")
.and_then(|x| Status::parse_allowed_list(&x).ok()),
mems_allowed: map
.remove("Mems_allowed")
.map(|x| Status::parse_allowed(&x))
.transpose()?,
mems_allowed_list: map
.remove("Mems_allowed_list")
.and_then(|x| Status::parse_allowed_list(&x).ok()),
voluntary_ctxt_switches: map
.remove("voluntary_ctxt_switches")
.map(|x| Ok(from_str!(u64, &x)))
.transpose()?,
nonvoluntary_ctxt_switches: map
.remove("nonvoluntary_ctxt_switches")
.map(|x| Ok(from_str!(u64, &x)))
.transpose()?,
core_dumping: map.remove("CoreDumping").map(|x| x == "1"),
thp_enabled: map.remove("THP_enabled").map(|x| x == "1"),
};
if cfg!(test) && !map.is_empty() {
eprintln!("Warning: status map is not empty: {:#?}", map);
}
Ok(status)
}
fn parse_with_kb<T: FromStrRadix>(s: Option<String>) -> ProcResult<Option<T>> {
if let Some(s) = s {
Ok(Some(from_str!(T, &s.replace(" kB", ""))))
} else {
Ok(None)
}
}
pub(crate) fn parse_uid_gid(s: &str, i: usize) -> ProcResult<u32> {
Ok(from_str!(u32, expect!(s.split_whitespace().nth(i))))
}
fn parse_sigq(s: &str) -> ProcResult<(u64, u64)> {
let mut iter = s.split('/');
let first = from_str!(u64, expect!(iter.next()));
let second = from_str!(u64, expect!(iter.next()));
Ok((first, second))
}
fn parse_list<T: FromStrRadix>(s: &str) -> ProcResult<Vec<T>> {
let mut ret = Vec::new();
for i in s.split_whitespace() {
ret.push(from_str!(T, i));
}
Ok(ret)
}
fn parse_allowed(s: &str) -> ProcResult<Vec<u32>> {
let mut ret = Vec::new();
for i in s.split(',') {
ret.push(from_str!(u32, i, 16));
}
Ok(ret)
}
fn parse_allowed_list(s: &str) -> ProcResult<Vec<(u32, u32)>> {
let mut ret = Vec::new();
for s in s.split(',') {
if s.contains('-') {
let mut s = s.split('-');
let beg = from_str!(u32, expect!(s.next()));
if let Some(x) = s.next() {
let end = from_str!(u32, x);
ret.push((beg, end));
}
} else {
let beg = from_str!(u32, s);
let end = from_str!(u32, s);
ret.push((beg, end));
}
}
Ok(ret)
}
}
#[cfg(test)]
mod tests {
use crate::process::*;
#[test]
fn test_proc_status() {
let myself = Process::myself().unwrap();
let status = myself.status().unwrap();
println!("{:?}", status);
assert_eq!(status.name, myself.stat.comm);
assert_eq!(status.pid, myself.stat.pid);
assert_eq!(status.ppid, myself.stat.ppid);
}
#[test]
fn test_proc_status_for_kthreadd() {
let kthreadd = process::Process::new(2).unwrap();
let status = kthreadd.status().unwrap();
println!("{:?}", status);
assert_eq!(status.pid, 2);
assert_eq!(status.vmpeak, None);
assert_eq!(status.vmsize, None);
assert_eq!(status.vmlck, None);
assert_eq!(status.vmpin, None);
assert_eq!(status.vmhwm, None);
assert_eq!(status.vmrss, None);
assert_eq!(status.rssanon, None);
assert_eq!(status.rssfile, None);
assert_eq!(status.rssshmem, None);
assert_eq!(status.vmdata, None);
assert_eq!(status.vmstk, None);
assert_eq!(status.vmexe, None);
assert_eq!(status.vmlib, None);
assert_eq!(status.vmpte, None);
assert_eq!(status.vmswap, None);
assert_eq!(status.hugetblpages, None);
}
}