1pub mod loadavg;
39pub mod meminfo;
40pub mod stat;
41pub mod uptime;
42pub mod vmstat;
43
44pub mod diskstats;
45pub mod netdevs;
46
47pub mod cpufreqs;
48pub mod pidentries;
49
50pub mod error;
51mod scanner;
52
53mod parser;
54mod util;
55
56pub use crate::error::{ProcError, ProcResult};
57
58pub type Pid = i32;
59
60pub const DEFAULT_CAPACITY_LOADAVG: usize = 40;
61pub const DEFAULT_CAPACITY_MEMINFO: usize = 1300;
62pub const DEFAULT_CAPACITY_STAT: usize = 1500;
63pub const DEFAULT_CAPACITY_UPTIME: usize = 50;
64pub const DEFAULT_CAPACITY_VMSTAT: usize = 1500;
65pub const DEFAULT_CAPACITY_DISKSTATS: usize = 1000;
66pub const DEFAULT_CAPACITY_NETDEVS: usize = 1000;
67pub const DEFAULT_CAPACITY_PID_STAT: usize = 400;
68pub const DEFAULT_CAPACITY_PID_STATM: usize = 40;
69pub const DEFAULT_CAPACITY_PID_STATUS: usize = 1100;
70pub const DEFAULT_CAPACITY_PID_CMDLINE: usize = 600;
71pub const DEFAULT_CAPACITY_PID_COMM: usize = 600;
72pub const DEFAULT_CAPACITY_CPUFREQ: usize = 20;
73pub const DEFAULT_CAPACITY_CPUFREQ_STATS: usize = 100;
74
75#[derive(Debug, Clone)]
76pub struct SystemConfig {
77 pub loadavg_cap: usize,
78 pub meminfo_cap: usize,
79 pub stat_cap: usize,
80 pub uptime_cap: usize,
81 pub vmstat_cap: usize,
82 pub diskstats_cap: usize,
83 pub netdevs_cap: usize,
84 pub pid_stat_cap: usize,
85 pub pid_statm_cap: usize,
86 pub pid_status_cap: usize,
87 pub pid_cmdline_cap: usize,
88 pub pid_comm_cap: usize,
89 pub cpufreq_cap: usize,
90 pub cpufreq_stats_cap: usize,
91}
92
93impl Default for SystemConfig {
94 fn default() -> Self {
95 Self {
96 loadavg_cap: DEFAULT_CAPACITY_LOADAVG,
97 meminfo_cap: DEFAULT_CAPACITY_MEMINFO,
98 stat_cap: DEFAULT_CAPACITY_STAT,
99 uptime_cap: DEFAULT_CAPACITY_UPTIME,
100 vmstat_cap: DEFAULT_CAPACITY_VMSTAT,
101 diskstats_cap: DEFAULT_CAPACITY_DISKSTATS,
102 netdevs_cap: DEFAULT_CAPACITY_NETDEVS,
103 pid_stat_cap: DEFAULT_CAPACITY_PID_STAT,
104 pid_statm_cap: DEFAULT_CAPACITY_PID_STATM,
105 pid_status_cap: DEFAULT_CAPACITY_PID_STATUS,
106 pid_cmdline_cap: DEFAULT_CAPACITY_PID_CMDLINE,
107 pid_comm_cap: DEFAULT_CAPACITY_PID_COMM,
108 cpufreq_cap: DEFAULT_CAPACITY_CPUFREQ,
109 cpufreq_stats_cap: DEFAULT_CAPACITY_CPUFREQ_STATS,
110 }
111 }
112}
113
114pub struct System {
116 base_path: PathBuf,
117 fb: util::FileBuffer,
118 config: SystemConfig,
119}
120
121use std::fs;
122use std::path::{Path, PathBuf};
123
124impl System {
125 pub fn new<P: AsRef<Path>>(base_path: P) -> Self {
133 Self::with_config(base_path, SystemConfig::default())
134 }
135
136 pub fn with_config<P: AsRef<Path>>(base_path: P, config: SystemConfig) -> Self {
137 Self {
138 base_path: base_path.as_ref().to_path_buf(),
139 fb: util::FileBuffer::new(),
140 config,
141 }
142 }
143 pub fn get_loadavg(&mut self) -> ProcResult<loadavg::LoadAvg> {
146 let proc_fb = util::ProcFb {
147 capacity: self.config.loadavg_cap,
148 name: "loadavg",
149 };
150 let slice = proc_fb.try_update(&self.base_path, &mut self.fb)?;
151 parser::loadavg::LoadAvgParser::default().parse(slice)
152 }
153 pub fn get_meminfo(&mut self) -> ProcResult<meminfo::MemInfo> {
156 let proc_fb = util::ProcFb {
157 capacity: self.config.meminfo_cap,
158 name: "meminfo",
159 };
160 let slice = proc_fb.try_update(&self.base_path, &mut self.fb)?;
161 parser::meminfo::MemInfoParser::default().parse(slice)
162 }
163 pub fn get_stat(&mut self) -> ProcResult<stat::Stat> {
166 let proc_fb = util::ProcFb {
167 capacity: self.config.stat_cap,
168 name: "stat",
169 };
170 let slice = proc_fb.try_update(&self.base_path, &mut self.fb)?;
171 parser::stat::StatParser::default().parse(slice)
172 }
173 pub fn get_uptime(&mut self) -> ProcResult<uptime::Uptime> {
176 let proc_fb = util::ProcFb {
177 capacity: self.config.uptime_cap,
178 name: "uptime",
179 };
180 let slice = proc_fb.try_update(&self.base_path, &mut self.fb)?;
181 parser::uptime::UptimeParser::default().parse(slice)
182 }
183 pub fn get_vmstat(&mut self) -> ProcResult<vmstat::VmStat> {
186 let proc_fb = util::ProcFb {
187 capacity: self.config.vmstat_cap,
188 name: "vmstat",
189 };
190 let slice = proc_fb.try_update(&self.base_path, &mut self.fb)?;
191 parser::vmstat::VmStatParser::default().parse(slice)
192 }
193 pub fn get_diskstats(&mut self) -> ProcResult<diskstats::DiskStats> {
196 let proc_fb = util::ProcFb {
197 capacity: self.config.diskstats_cap,
198 name: "diskstats",
199 };
200 let slice = proc_fb.try_update(&self.base_path, &mut self.fb)?;
201 parser::diskstats::DiskStatsParser::default().parse(slice)
202 }
203 pub fn get_netdevs(&mut self) -> ProcResult<netdevs::NetDevs> {
206 let proc_fb = util::ProcFb {
207 capacity: self.config.netdevs_cap,
208 name: "net/dev",
209 };
210 let slice = proc_fb.try_update(&self.base_path, &mut self.fb)?;
211 parser::netdevs::NetDevsParser::default().parse(slice)
212 }
213 pub fn get_max_cpu_num(&mut self) -> ProcResult<usize> {
216 let mut max_cpu_num = 0;
217 let cpu_path = self.base_path.join("sys/devices/system/cpu");
218 for entry in fs::read_dir(cpu_path).map_err(|_| crate::ProcError::PermissionDenied)? {
219 let entry = entry.map_err(|_| crate::ProcError::InternalError)?;
220 let os_name = entry.file_name();
221 let name = os_name.to_string_lossy();
222 if !name.starts_with("cpu") {
223 continue;
224 }
225 let s_num = &name[3..];
226 if s_num.as_bytes().iter().any(|&b| !b.is_ascii_digit()) {
227 continue;
228 }
229 if let Ok(num) = s_num.parse::<usize>() {
230 if max_cpu_num < num {
231 max_cpu_num = num;
232 }
233 }
234 }
235 Ok(max_cpu_num + 1)
236 }
237 pub fn get_cpufreqs(&mut self, max_cpu_num: usize) -> ProcResult<cpufreqs::CpuFreqs> {
240 let sys_cpufreq_cur = util::SysCpuFb {
241 capacity: self.config.cpufreq_cap,
242 name: "cpufreq/cpuinfo_cur_freq",
243 };
244 let sys_cpufreq_max = util::SysCpuFb {
245 capacity: self.config.cpufreq_cap,
246 name: "cpufreq/cpuinfo_max_freq",
247 };
248 let sys_cpufreq_stats_time_in_state = util::SysCpuFb {
249 capacity: self.config.cpufreq_stats_cap,
250 name: "cpufreq/stats/time_in_state",
251 };
252 let mut cpufreqs = cpufreqs::CpuFreqs::default();
254 cpufreqs
255 .cpufreqs
256 .resize(max_cpu_num, cpufreqs::CpuFreq::default());
257 for idx in 0..max_cpu_num {
258 let cpufreq = &mut cpufreqs.cpufreqs[idx];
259 cpufreq.cur = {
260 let slice = match sys_cpufreq_cur.try_update_with_cpu_num(
261 &self.base_path,
262 &mut self.fb,
263 idx,
264 ) {
265 Ok(s) => s,
266 Err(_) => &[],
267 };
268 parser::cpufreqs::CpuFreqMaxParser::default().parse(slice)?
269 };
270 cpufreq.max = {
271 let slice = match sys_cpufreq_max.try_update_with_cpu_num(
272 &self.base_path,
273 &mut self.fb,
274 idx,
275 ) {
276 Ok(s) => s,
277 Err(_) => &[],
278 };
279 parser::cpufreqs::CpuFreqMaxParser::default().parse(slice)?
280 };
281 cpufreq.time_in_states = {
282 let slice = match sys_cpufreq_stats_time_in_state.try_update_with_cpu_num(
283 &self.base_path,
284 &mut self.fb,
285 idx,
286 ) {
287 Ok(s) => s,
288 Err(_) => &[],
289 };
290 parser::cpufreqs::CpuFreqStatsTimeInStateParser::default().parse(slice)?
291 };
292 }
293 Ok(cpufreqs)
294 }
295 pub fn get_pids(&mut self) -> ProcResult<Vec<Pid>> {
298 let mut v_pid = Vec::new();
299 let proc_path = self.base_path.join("proc");
301 for entry in fs::read_dir(proc_path).map_err(|_| crate::ProcError::PermissionDenied)? {
302 let entry = entry.map_err(|_| crate::ProcError::InternalError)?;
303 let os_name = entry.file_name();
304 let name = os_name.to_string_lossy();
305 if name.as_bytes().iter().any(|&b| !b.is_ascii_digit()) {
306 continue;
307 }
308 if let Ok(pid) = name.parse::<Pid>() {
309 v_pid.push(pid);
310 }
311 }
312 v_pid.sort_unstable();
313 Ok(v_pid)
315 }
316 pub fn get_pidentries(&mut self) -> ProcResult<pidentries::PidEntries> {
319 let pid_vec = self.get_pids()?;
320 let mut pids = pidentries::PidEntries::default();
322 pids.pidentries
323 .resize(pid_vec.len(), pidentries::PidEntry::default());
324 for (idx, &pid) in pid_vec.iter().enumerate() {
325 let pidentry = &mut pids.pidentries[idx];
326 pidentry.is_empty = true;
327 pidentry.stat = match self.get_pidentry_stat(pid)? {
329 Some(a) => a,
330 None => continue,
331 };
332 pidentry.statm = match self.get_pidentry_statm(pid)? {
333 Some(a) => a,
334 None => continue,
335 };
336 pidentry.status = match self.get_pidentry_status(pid)? {
337 Some(a) => a,
338 None => continue,
339 };
340 pidentry.cmdline = match self.get_pidentry_cmdline(pid)? {
341 Some(a) => a,
342 None => continue,
343 };
344 pidentry.is_empty = false;
345 }
346 Ok(pids)
347 }
348 pub fn get_pidentry_stat(&mut self, pid: Pid) -> ProcResult<Option<pidentries::PidStat>> {
351 let pidprocs_stat = util::PidFb {
352 capacity: self.config.pid_stat_cap,
353 name: "stat",
354 };
355 let slice = match pidprocs_stat.try_update_with_pid(&self.base_path, &mut self.fb, pid) {
356 Ok(s) => s,
357 Err(_) => return Ok(None),
358 };
359 if !slice.is_empty() {
360 Ok(Some(
361 parser::pidstat::PidStatParser::default().parse(slice)?,
362 ))
363 } else {
364 Ok(None)
365 }
366 }
367 pub fn get_pidentry_statm(&mut self, pid: Pid) -> ProcResult<Option<pidentries::PidStatm>> {
370 let pidprocs_statm = util::PidFb {
371 capacity: self.config.pid_statm_cap,
372 name: "statm",
373 };
374 let slice = match pidprocs_statm.try_update_with_pid(&self.base_path, &mut self.fb, pid) {
375 Ok(s) => s,
376 Err(_) => return Ok(None),
377 };
378 if !slice.is_empty() {
379 Ok(Some(
380 parser::pidstatm::PidStatmParser::default().parse(slice)?,
381 ))
382 } else {
383 Ok(None)
384 }
385 }
386 pub fn get_pidentry_status(&mut self, pid: Pid) -> ProcResult<Option<pidentries::PidStatus>> {
389 let pidprocs_status = util::PidFb {
390 capacity: self.config.pid_status_cap,
391 name: "status",
392 };
393 let slice = match pidprocs_status.try_update_with_pid(&self.base_path, &mut self.fb, pid) {
394 Ok(s) => s,
395 Err(_) => return Ok(None),
396 };
397 if !slice.is_empty() {
398 Ok(Some(
399 parser::pidstatus::PidStatusParser::default().parse(slice)?,
400 ))
401 } else {
402 Ok(None)
403 }
404 }
405 pub fn get_pidentry_cmdline(&mut self, pid: Pid) -> ProcResult<Option<pidentries::PidCmdline>> {
408 let pidprocs_cmdline = util::PidFb {
409 capacity: self.config.pid_cmdline_cap,
410 name: "cmdline",
411 };
412 let slice = match pidprocs_cmdline.try_update_with_pid(&self.base_path, &mut self.fb, pid) {
413 Ok(s) => s,
414 Err(_) => return Ok(None),
415 };
416 if !slice.is_empty() {
417 Ok(Some(
418 parser::pidcmdline::PidCmdlineParser::default().parse(slice)?,
419 ))
420 } else {
421 Ok(None)
422 }
423 }
424 pub fn get_pidentry_comm(&mut self, pid: Pid) -> ProcResult<Option<pidentries::PidCmdline>> {
427 let pidprocs_comm = util::PidFb {
428 capacity: self.config.pid_comm_cap,
429 name: "comm",
430 };
431 let slice = match pidprocs_comm.try_update_with_pid(&self.base_path, &mut self.fb, pid) {
432 Ok(s) => s,
433 Err(_) => return Ok(None),
434 };
435 if !slice.is_empty() {
436 Ok(Some(
437 parser::pidcmdline::PidCmdlineParser::default().parse(slice)?,
438 ))
439 } else {
440 Ok(None)
441 }
442 }
443}