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 Ok(a) => match a {
330 Some(b) => b,
331 None => continue,
332 },
333 Err(e) if e.is_not_found() => continue,
334 Err(e) => return Err(e),
335 };
336 pidentry.statm = match self.get_pidentry_statm(pid) {
337 Ok(a) => match a {
338 Some(b) => b,
339 None => continue,
340 },
341 Err(e) if e.is_not_found() => continue,
342 Err(e) => return Err(e),
343 };
344 pidentry.status = match self.get_pidentry_status(pid) {
345 Ok(a) => match a {
346 Some(b) => b,
347 None => continue,
348 },
349 Err(e) if e.is_not_found() => continue,
350 Err(e) => return Err(e),
351 };
352 pidentry.cmdline = match self.get_pidentry_cmdline(pid) {
353 Ok(a) => match a {
354 Some(b) => b,
355 None => continue,
356 },
357 Err(e) if e.is_not_found() => continue,
358 Err(e) => return Err(e),
359 };
360 pidentry.is_empty = false;
361 }
362 Ok(pids)
363 }
364 pub fn get_pidentry_stat(&mut self, pid: Pid) -> ProcResult<Option<pidentries::PidStat>> {
367 let pidprocs_stat = util::PidFb {
368 capacity: self.config.pid_stat_cap,
369 name: "stat",
370 };
371 let slice = pidprocs_stat.try_update_with_pid(&self.base_path, &mut self.fb, pid)?;
372 if !slice.is_empty() {
373 Ok(Some(
374 parser::pidstat::PidStatParser::default().parse(slice)?,
375 ))
376 } else {
377 Ok(None)
378 }
379 }
380 pub fn get_pidentry_statm(&mut self, pid: Pid) -> ProcResult<Option<pidentries::PidStatm>> {
383 let pidprocs_statm = util::PidFb {
384 capacity: self.config.pid_statm_cap,
385 name: "statm",
386 };
387 let slice = pidprocs_statm.try_update_with_pid(&self.base_path, &mut self.fb, pid)?;
388 if !slice.is_empty() {
389 Ok(Some(
390 parser::pidstatm::PidStatmParser::default().parse(slice)?,
391 ))
392 } else {
393 Ok(None)
394 }
395 }
396 pub fn get_pidentry_status(&mut self, pid: Pid) -> ProcResult<Option<pidentries::PidStatus>> {
399 let pidprocs_status = util::PidFb {
400 capacity: self.config.pid_status_cap,
401 name: "status",
402 };
403 let slice = pidprocs_status.try_update_with_pid(&self.base_path, &mut self.fb, pid)?;
404 if !slice.is_empty() {
405 Ok(Some(
406 parser::pidstatus::PidStatusParser::default().parse(slice)?,
407 ))
408 } else {
409 Ok(None)
410 }
411 }
412 pub fn get_pidentry_cmdline(&mut self, pid: Pid) -> ProcResult<Option<pidentries::PidCmdline>> {
415 let pidprocs_cmdline = util::PidFb {
416 capacity: self.config.pid_cmdline_cap,
417 name: "cmdline",
418 };
419 let slice = pidprocs_cmdline.try_update_with_pid(&self.base_path, &mut self.fb, pid)?;
420 if !slice.is_empty() {
421 Ok(Some(
422 parser::pidcmdline::PidCmdlineParser::default().parse(slice)?,
423 ))
424 } else {
425 Ok(None)
426 }
427 }
428 pub fn get_pidentry_comm(&mut self, pid: Pid) -> ProcResult<Option<pidentries::PidCmdline>> {
431 let pidprocs_comm = util::PidFb {
432 capacity: self.config.pid_comm_cap,
433 name: "comm",
434 };
435 let slice = pidprocs_comm.try_update_with_pid(&self.base_path, &mut self.fb, pid)?;
436 if !slice.is_empty() {
437 Ok(Some(
438 parser::pidcmdline::PidCmdlineParser::default().parse(slice)?,
439 ))
440 } else {
441 Ok(None)
442 }
443 }
444}