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                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    //
349    /// `/proc/<pid>/stat`
350    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    //
368    /// `/proc/<pid>/statm`
369    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    //
387    /// `/proc/<pid>/status`
388    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    //
406    /// `/proc/<pid>/cmdline`
407    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    //
425    /// `/proc/<pid>/comm`
426    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}