use std::fs;
use std::path::{Path, PathBuf};
pub mod loadavg;
pub mod meminfo;
pub mod stat;
pub mod uptime;
pub mod vmstat;
pub mod diskstats;
pub mod netdevs;
pub mod cpufreqs;
pub mod pidentries;
mod parser;
mod util;
pub type Pid = i32;
pub struct System {
base_path: PathBuf,
fb: util::FileBuffer,
}
impl System {
pub fn new<P: AsRef<Path>>(base_path: P) -> Self {
Self {
base_path: base_path.as_ref().to_path_buf(),
fb: util::FileBuffer::new(),
}
}
pub fn get_loadavg(&mut self) -> loadavg::LoadAvg {
static PROC_FB: util::ProcFb = util::ProcFb {
capacity: 40,
name: "loadavg",
};
let slice = PROC_FB.update(&self.base_path, &mut self.fb);
parser::loadavg::LoadAvgParser::default().parse(slice)
}
pub fn get_meminfo(&mut self) -> meminfo::MemInfo {
static PROC_FB: util::ProcFb = util::ProcFb {
capacity: 1300,
name: "meminfo",
};
let slice = PROC_FB.update(&self.base_path, &mut self.fb);
parser::meminfo::MemInfoParser::default().parse(slice)
}
pub fn get_stat(&mut self) -> stat::Stat {
static PROC_FB: util::ProcFb = util::ProcFb {
capacity: 1500,
name: "stat",
};
let slice = PROC_FB.update(&self.base_path, &mut self.fb);
parser::stat::StatParser::default().parse(slice)
}
pub fn get_uptime(&mut self) -> uptime::Uptime {
static PROC_FB: util::ProcFb = util::ProcFb {
capacity: 30,
name: "uptime",
};
let slice = PROC_FB.update(&self.base_path, &mut self.fb);
parser::uptime::UptimeParser::default().parse(slice)
}
pub fn get_vmstat(&mut self) -> vmstat::VmStat {
static PROC_FB: util::ProcFb = util::ProcFb {
capacity: 2600,
name: "vmstat",
};
let slice = PROC_FB.update(&self.base_path, &mut self.fb);
parser::vmstat::VmStatParser::default().parse(slice)
}
pub fn get_diskstats(&mut self) -> diskstats::DiskStats {
static PROC_FB: util::ProcFb = util::ProcFb {
capacity: 3000,
name: "diskstats",
};
let slice = PROC_FB.update(&self.base_path, &mut self.fb);
parser::diskstats::DiskStatsParser::default().parse(slice)
}
pub fn get_netdevs(&mut self) -> netdevs::NetDevs {
static PROC_FB: util::ProcFb = util::ProcFb {
capacity: 2000,
name: "net/dev",
};
let slice = PROC_FB.update(&self.base_path, &mut self.fb);
parser::netdevs::NetDevsParser::default().parse(slice)
}
pub fn get_max_cpu_num(&mut self) -> usize {
let mut max_cpu_num = 0;
let cpu_path = format!(
"{}/sys/devices/system/cpu",
&self.base_path.to_str().unwrap()
);
for entry in fs::read_dir(cpu_path).unwrap() {
let entry = entry.unwrap();
let os_name = entry.file_name();
let name = os_name.to_string_lossy();
if name.starts_with("cpu") && name.len() > 3 {
let sl = &name[3..];
if let Ok(n) = sl.parse::<usize>() {
if n > max_cpu_num {
max_cpu_num = n;
}
}
}
}
max_cpu_num + 1
}
pub fn get_cpufreqs(&mut self, max_cpu_num: usize) -> cpufreqs::CpuFreqs {
static SYS_CPUFREQ_CUR: util::SysCpuFb = util::SysCpuFb {
capacity: 10,
name: "cpufreq/cpuinfo_cur_freq",
};
static SYS_CPUFREQ_MAX: util::SysCpuFb = util::SysCpuFb {
capacity: 10,
name: "cpufreq/cpuinfo_max_freq",
};
static SYS_CPUFREQ_STATS_TIME_IN_STATE: util::SysCpuFb = util::SysCpuFb {
capacity: 100,
name: "cpufreq/stats/time_in_state",
};
let mut cpufreqs = cpufreqs::CpuFreqs::default();
cpufreqs
.cpufreqs
.resize(max_cpu_num, cpufreqs::CpuFreq::default());
for idx in 0..max_cpu_num {
let cpufreq = &mut cpufreqs.cpufreqs[idx];
cpufreq.cur = {
let slice = SYS_CPUFREQ_CUR.update_with_cpu_num(&self.base_path, &mut self.fb, idx);
parser::cpufreqs::CpuFreqMaxParser::default().parse(slice)
};
cpufreq.max = {
let slice = SYS_CPUFREQ_MAX.update_with_cpu_num(&self.base_path, &mut self.fb, idx);
parser::cpufreqs::CpuFreqMaxParser::default().parse(slice)
};
cpufreq.time_in_states = {
let slice = SYS_CPUFREQ_STATS_TIME_IN_STATE.update_with_cpu_num(
&self.base_path,
&mut self.fb,
idx,
);
parser::cpufreqs::CpuFreqStatsTimeInStateParser::default().parse(slice)
};
}
cpufreqs
}
pub fn get_pids(&mut self) -> Vec<Pid> {
let mut v_pid = Vec::new();
let proc_path = format!("{}/proc", &self.base_path.to_str().unwrap());
for entry in fs::read_dir(proc_path).unwrap() {
let entry = entry.unwrap();
let os_name = entry.file_name();
let name = os_name.to_string_lossy();
if name.as_bytes().iter().any(|&b| !b.is_ascii_digit()) {
continue;
}
let pid: Pid = name.parse().unwrap();
v_pid.push(pid);
}
v_pid.sort_unstable();
v_pid
}
pub fn get_pidentries(&mut self) -> pidentries::PidEntries {
let pid_vec = self.get_pids();
let mut pids = pidentries::PidEntries::default();
pids.pidentries
.resize(pid_vec.len(), pidentries::PidEntry::default());
for (idx, &pid) in pid_vec.iter().enumerate() {
let pidentry = &mut pids.pidentries[idx];
pidentry.is_empty = true;
pidentry.stat = match self.get_pidentry_stat(pid) {
Some(a) => a,
None => continue,
};
pidentry.statm = match self.get_pidentry_statm(pid) {
Some(a) => a,
None => continue,
};
pidentry.status = match self.get_pidentry_status(pid) {
Some(a) => a,
None => continue,
};
pidentry.cmdline = match self.get_pidentry_cmdline(pid) {
Some(a) => a,
None => continue,
};
pidentry.is_empty = false;
}
pids
}
pub fn get_pidentry_stat(&mut self, pid: Pid) -> Option<pidentries::PidStat> {
static PIDPROCS_STAT: util::PidFb = util::PidFb {
capacity: 400,
name: "stat",
};
let slice = PIDPROCS_STAT.update_with_pid(&self.base_path, &mut self.fb, pid);
if !slice.is_empty() {
Some(parser::pidstat::PidStatParser::default().parse(slice))
} else {
None
}
}
pub fn get_pidentry_statm(&mut self, pid: Pid) -> Option<pidentries::PidStatm> {
static PIDPROCS_STATM: util::PidFb = util::PidFb {
capacity: 40,
name: "statm",
};
let slice = PIDPROCS_STATM.update_with_pid(&self.base_path, &mut self.fb, pid);
if !slice.is_empty() {
Some(parser::pidstatm::PidStatmParser::default().parse(slice))
} else {
None
}
}
pub fn get_pidentry_status(&mut self, pid: Pid) -> Option<pidentries::PidStatus> {
static PIDPROCS_STATUS: util::PidFb = util::PidFb {
capacity: 1100,
name: "status",
};
let slice = PIDPROCS_STATUS.update_with_pid(&self.base_path, &mut self.fb, pid);
if !slice.is_empty() {
Some(parser::pidstatus::PidStatusParser::default().parse(slice))
} else {
None
}
}
pub fn get_pidentry_cmdline(&mut self, pid: Pid) -> Option<pidentries::PidCmdline> {
static PIDPROCS_CMDLINE: util::PidFb = util::PidFb {
capacity: 600,
name: "cmdline",
};
let slice = PIDPROCS_CMDLINE.update_with_pid(&self.base_path, &mut self.fb, pid);
if !slice.is_empty() {
Some(parser::pidcmdline::PidCmdlineParser::default().parse(slice))
} else {
None
}
}
pub fn get_pidentry_comm(&mut self, pid: Pid) -> Option<pidentries::PidCmdline> {
static PIDPROCS_COMM: util::PidFb = util::PidFb {
capacity: 600,
name: "comm",
};
let slice = PIDPROCS_COMM.update_with_pid(&self.base_path, &mut self.fb, pid);
if !slice.is_empty() {
Some(parser::pidcmdline::PidCmdlineParser::default().parse(slice))
} else {
None
}
}
}