Skip to main content

linux_procfs/
lib.rs

1/*!
2The linux-procfs crate is the data snap library for the `/proc/` filesystem on the linux os.
3
4This crate reads from `/proc` filesystem, scanne it, stores the value into the struct and returns it. This scans and holds only the required values.
5
6# Usage
7
8## Setup
9
10Add the following to your `Cargo.toml`:
11
12```toml
13[dependencies]
14linux-procfs = "0.3.17"
15```
16
17## Example 1: load average
18
19```
20use linux_procfs::System;
21let mut sys = System::new("/");
22let loadavg = sys.get_loadavg().unwrap();
23println!("{}, {}, {}, {}", loadavg.a1, loadavg.a5, loadavg.a15, loadavg.last_pid);
24```
25
26## Example 2: disk stats
27
28```
29use linux_procfs::System;
30let mut sys = System::new("/");
31let diskstats = sys.get_diskstats().unwrap();
32for disk in diskstats.disks {
33    println!("{}, {}, {}", disk.name, disk.rblk, disk.wblk);
34}
35```
36*/
37
38pub 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
114/// system interface of linux-procfs
115pub 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    /// create instance
126    ///
127    /// Example:
128    /// ```
129    ///use linux_procfs::System;
130    ///let mut sys = System::new("/");
131    /// ```
132    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    //
144    /// `/proc/loadavg`
145    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    //
154    /// `/proc/meminfo`
155    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    //
164    /// `/proc/stat`
165    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    //
174    /// `/proc/uptime`
175    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    //
184    /// `/proc/vmstat`
185    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    //
194    /// `/proc/diskstats`
195    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    //
204    /// `/proc/net/dev`
205    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    //
214    /// maximum cpu number
215    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    //
238    /// `/sys/devices/system/cpu/cpu*/cpufreq/`
239    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        //
253        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    //
296    /// `/proc/<pid>/`
297    pub fn get_pids(&mut self) -> ProcResult<Vec<Pid>> {
298        let mut v_pid = Vec::new();
299        //
300        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        //
314        Ok(v_pid)
315    }
316    //
317    /// `/proc/<pid>/{stat, statm, status, cmdline}`
318    pub fn get_pidentries(&mut self) -> ProcResult<pidentries::PidEntries> {
319        let pid_vec = self.get_pids()?;
320        //
321        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            //
328            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    //
365    /// `/proc/<pid>/stat`
366    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    //
381    /// `/proc/<pid>/statm`
382    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    //
397    /// `/proc/<pid>/status`
398    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    //
413    /// `/proc/<pid>/cmdline`
414    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    //
429    /// `/proc/<pid>/comm`
430    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}