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 by specifying `feature`.
5
6# Feature
7
8- minimum support rustc 1.58.1 (db9d1b20b 2022-01-20)
9
10# Example
11
12## Example 1: load average
13
14```
15use linux_procfs::System;
16let mut sys = System::new("/");
17let loadavg = sys.get_loadavg();
18println!("{}, {}, {}, {}", loadavg.a1, loadavg.a5, loadavg.a15, loadavg.last_pid);
19```
20
21## Example 2: disk stats
22
23```
24use linux_procfs::System;
25let mut sys = System::new("/");
26let diskstats = sys.get_diskstats();
27for disk in diskstats.disks {
28    println!("{}, {}, {}", disk.name, disk.rblk, disk.wblk);
29}
30```
31*/
32
33use std::fs;
34use std::path::{Path, PathBuf};
35
36pub mod loadavg;
37pub mod meminfo;
38pub mod stat;
39pub mod uptime;
40pub mod vmstat;
41
42pub mod diskstats;
43pub mod netdevs;
44
45pub mod cpufreqs;
46pub mod pidentries;
47
48mod parser;
49mod util;
50
51pub type Pid = i32;
52
53/// system interface of linux-procfs
54pub struct System {
55    base_path: PathBuf,
56    fb: util::FileBuffer,
57}
58
59impl System {
60    /// create instance
61    ///
62    /// Example:
63    /// ```
64    ///use linux_procfs::System;
65    ///let mut sys = System::new("/");
66    /// ```
67    pub fn new<P: AsRef<Path>>(base_path: P) -> Self {
68        Self {
69            base_path: base_path.as_ref().to_path_buf(),
70            fb: util::FileBuffer::new(),
71        }
72    }
73    //
74    /// `/proc/loadavg`
75    pub fn get_loadavg(&mut self) -> loadavg::LoadAvg {
76        static PROC_FB: util::ProcFb = util::ProcFb {
77            capacity: 40,
78            name: "loadavg",
79        };
80        let slice = PROC_FB.update(&self.base_path, &mut self.fb);
81        parser::loadavg::LoadAvgParser::default().parse(slice)
82    }
83    //
84    /// `/proc/meminfo`
85    pub fn get_meminfo(&mut self) -> meminfo::MemInfo {
86        static PROC_FB: util::ProcFb = util::ProcFb {
87            capacity: 1300,
88            name: "meminfo",
89        };
90        let slice = PROC_FB.update(&self.base_path, &mut self.fb);
91        parser::meminfo::MemInfoParser::default().parse(slice)
92    }
93    //
94    /// `/proc/stat`
95    pub fn get_stat(&mut self) -> stat::Stat {
96        static PROC_FB: util::ProcFb = util::ProcFb {
97            capacity: 1500,
98            name: "stat",
99        };
100        let slice = PROC_FB.update(&self.base_path, &mut self.fb);
101        parser::stat::StatParser::default().parse(slice)
102    }
103    //
104    /// `/proc/uptime`
105    pub fn get_uptime(&mut self) -> uptime::Uptime {
106        static PROC_FB: util::ProcFb = util::ProcFb {
107            capacity: 30,
108            name: "uptime",
109        };
110        let slice = PROC_FB.update(&self.base_path, &mut self.fb);
111        parser::uptime::UptimeParser::default().parse(slice)
112    }
113    //
114    /// `/proc/vmstat`
115    pub fn get_vmstat(&mut self) -> vmstat::VmStat {
116        static PROC_FB: util::ProcFb = util::ProcFb {
117            capacity: 2600,
118            name: "vmstat",
119        };
120        let slice = PROC_FB.update(&self.base_path, &mut self.fb);
121        parser::vmstat::VmStatParser::default().parse(slice)
122    }
123    //
124    /// `/proc/diskstats`
125    pub fn get_diskstats(&mut self) -> diskstats::DiskStats {
126        static PROC_FB: util::ProcFb = util::ProcFb {
127            capacity: 3000,
128            name: "diskstats",
129        };
130        let slice = PROC_FB.update(&self.base_path, &mut self.fb);
131        parser::diskstats::DiskStatsParser::default().parse(slice)
132    }
133    //
134    /// `/proc/net/dev`
135    pub fn get_netdevs(&mut self) -> netdevs::NetDevs {
136        static PROC_FB: util::ProcFb = util::ProcFb {
137            capacity: 2000,
138            name: "net/dev",
139        };
140        let slice = PROC_FB.update(&self.base_path, &mut self.fb);
141        parser::netdevs::NetDevsParser::default().parse(slice)
142    }
143    //
144    /// maximum cpu number
145    pub fn get_max_cpu_num(&mut self) -> usize {
146        let mut max_cpu_num = 0;
147        //
148        let cpu_path = format!(
149            "{}/sys/devices/system/cpu",
150            &self.base_path.to_str().unwrap()
151        );
152        for entry in fs::read_dir(cpu_path).unwrap() {
153            let entry = entry.unwrap();
154            let os_name = entry.file_name();
155            let name = os_name.to_string_lossy();
156            if name.starts_with("cpu") && name.len() > 3 {
157                let sl = &name[3..];
158                if let Ok(n) = sl.parse::<usize>() {
159                    if n > max_cpu_num {
160                        max_cpu_num = n;
161                    }
162                }
163            }
164        }
165        //
166        max_cpu_num + 1
167    }
168    //
169    /// `/sys/devices/system/cpu/cpu0/cpufreq/`
170    pub fn get_cpufreqs(&mut self, max_cpu_num: usize) -> cpufreqs::CpuFreqs {
171        static SYS_CPUFREQ_CUR: util::SysCpuFb = util::SysCpuFb {
172            capacity: 10,
173            name: "cpufreq/cpuinfo_cur_freq",
174        };
175        static SYS_CPUFREQ_MAX: util::SysCpuFb = util::SysCpuFb {
176            capacity: 10,
177            name: "cpufreq/cpuinfo_max_freq",
178        };
179        static SYS_CPUFREQ_STATS_TIME_IN_STATE: util::SysCpuFb = util::SysCpuFb {
180            capacity: 100,
181            name: "cpufreq/stats/time_in_state",
182        };
183        //
184        let mut cpufreqs = cpufreqs::CpuFreqs::default();
185        cpufreqs
186            .cpufreqs
187            .resize(max_cpu_num, cpufreqs::CpuFreq::default());
188        for idx in 0..max_cpu_num {
189            let cpufreq = &mut cpufreqs.cpufreqs[idx];
190            cpufreq.cur = {
191                let slice = SYS_CPUFREQ_CUR.update_with_cpu_num(&self.base_path, &mut self.fb, idx);
192                parser::cpufreqs::CpuFreqMaxParser::default().parse(slice)
193            };
194            cpufreq.max = {
195                let slice = SYS_CPUFREQ_MAX.update_with_cpu_num(&self.base_path, &mut self.fb, idx);
196                parser::cpufreqs::CpuFreqMaxParser::default().parse(slice)
197            };
198            cpufreq.time_in_states = {
199                let slice = SYS_CPUFREQ_STATS_TIME_IN_STATE.update_with_cpu_num(
200                    &self.base_path,
201                    &mut self.fb,
202                    idx,
203                );
204                parser::cpufreqs::CpuFreqStatsTimeInStateParser::default().parse(slice)
205            };
206        }
207        cpufreqs
208    }
209    //
210    /// `/proc/<pid>/`
211    pub fn get_pids(&mut self) -> Vec<Pid> {
212        let mut v_pid = Vec::new();
213        //
214        let proc_path = format!("{}/proc", &self.base_path.to_str().unwrap());
215        for entry in fs::read_dir(proc_path).unwrap() {
216            let entry = entry.unwrap();
217            let os_name = entry.file_name();
218            let name = os_name.to_string_lossy();
219            if name.as_bytes().iter().any(|&b| !b.is_ascii_digit()) {
220                continue;
221            }
222            let pid: Pid = name.parse().unwrap();
223            v_pid.push(pid);
224        }
225        v_pid.sort_unstable();
226        //
227        v_pid
228    }
229    //
230    /// `/proc/<pid>/{stat, statm, status, cmdline}`
231    pub fn get_pidentries(&mut self) -> pidentries::PidEntries {
232        let pid_vec = self.get_pids();
233        //
234        let mut pids = pidentries::PidEntries::default();
235        pids.pidentries
236            .resize(pid_vec.len(), pidentries::PidEntry::default());
237        for (idx, &pid) in pid_vec.iter().enumerate() {
238            let pidentry = &mut pids.pidentries[idx];
239            pidentry.is_empty = true;
240            //
241            pidentry.stat = match self.get_pidentry_stat(pid) {
242                Some(a) => a,
243                None => continue,
244            };
245            pidentry.statm = match self.get_pidentry_statm(pid) {
246                Some(a) => a,
247                None => continue,
248            };
249            pidentry.status = match self.get_pidentry_status(pid) {
250                Some(a) => a,
251                None => continue,
252            };
253            pidentry.cmdline = match self.get_pidentry_cmdline(pid) {
254                Some(a) => a,
255                None => continue,
256            };
257            //
258            pidentry.is_empty = false;
259        }
260        pids
261    }
262    //
263    /// `/proc/<pid>/stat`
264    pub fn get_pidentry_stat(&mut self, pid: Pid) -> Option<pidentries::PidStat> {
265        static PIDPROCS_STAT: util::PidFb = util::PidFb {
266            capacity: 400,
267            name: "stat",
268        };
269        let slice = PIDPROCS_STAT.update_with_pid(&self.base_path, &mut self.fb, pid);
270        if !slice.is_empty() {
271            Some(parser::pidstat::PidStatParser::default().parse(slice))
272        } else {
273            None
274        }
275    }
276    //
277    /// `/proc/<pid>/statm`
278    pub fn get_pidentry_statm(&mut self, pid: Pid) -> Option<pidentries::PidStatm> {
279        static PIDPROCS_STATM: util::PidFb = util::PidFb {
280            capacity: 40,
281            name: "statm",
282        };
283        let slice = PIDPROCS_STATM.update_with_pid(&self.base_path, &mut self.fb, pid);
284        if !slice.is_empty() {
285            Some(parser::pidstatm::PidStatmParser::default().parse(slice))
286        } else {
287            None
288        }
289    }
290    //
291    /// `/proc/<pid>/status`
292    pub fn get_pidentry_status(&mut self, pid: Pid) -> Option<pidentries::PidStatus> {
293        static PIDPROCS_STATUS: util::PidFb = util::PidFb {
294            capacity: 1100,
295            name: "status",
296        };
297        let slice = PIDPROCS_STATUS.update_with_pid(&self.base_path, &mut self.fb, pid);
298        if !slice.is_empty() {
299            Some(parser::pidstatus::PidStatusParser::default().parse(slice))
300        } else {
301            None
302        }
303    }
304    //
305    /// `/proc/<pid>/cmdline`
306    pub fn get_pidentry_cmdline(&mut self, pid: Pid) -> Option<pidentries::PidCmdline> {
307        static PIDPROCS_CMDLINE: util::PidFb = util::PidFb {
308            capacity: 600,
309            name: "cmdline",
310        };
311        let slice = PIDPROCS_CMDLINE.update_with_pid(&self.base_path, &mut self.fb, pid);
312        if !slice.is_empty() {
313            Some(parser::pidcmdline::PidCmdlineParser::default().parse(slice))
314        } else {
315            None
316        }
317    }
318    //
319    /// `/proc/<pid>/comm`
320    pub fn get_pidentry_comm(&mut self, pid: Pid) -> Option<pidentries::PidCmdline> {
321        static PIDPROCS_COMM: util::PidFb = util::PidFb {
322            capacity: 600,
323            name: "comm",
324        };
325        let slice = PIDPROCS_COMM.update_with_pid(&self.base_path, &mut self.fb, pid);
326        if !slice.is_empty() {
327            Some(parser::pidcmdline::PidCmdlineParser::default().parse(slice))
328        } else {
329            None
330        }
331    }
332}