proc_modules/
lib.rs

1//! Provides easy access to active kernel modules in `/proc/modules`.
2//!
3//! ```rust,no_run
4//! extern crate proc_modules;
5//!
6//! use proc_modules::ModuleIter;
7//! use std::io;
8//!
9//! fn main() -> io::Result<()> {
10//!     for module in ModuleIter::new()? {
11//!         println!("{:#?}", module?);
12//!     }
13//!
14//!     Ok(())
15//! }
16//! ```
17
18use std::fs::File;
19use std::io::{self, BufRead, BufReader};
20
21/// A Linux kernel module.
22#[derive(Debug, PartialEq)]
23pub struct Module {
24    /// The name of the module.
25    pub module: String,
26    /// The size of the module.
27    pub size: u64,
28    /// What is using this module.
29    pub used_by: Vec<String>
30}
31
32impl Module {
33    /// Parse an individual /proc/modules-like line.
34    pub fn parse(line: &str) -> io::Result<Module> {
35        let mut parts = line.split(' ');
36
37        let name = parts.next().ok_or_else(|| io::Error::new(
38            io::ErrorKind::InvalidData,
39            "module name not found"
40        ))?;
41
42        let size = parts.next().ok_or_else(|| io::Error::new(
43            io::ErrorKind::InvalidData,
44            "size not found"
45        ))?;
46
47        let used_by = parts.nth(1).ok_or_else(|| io::Error::new(
48            io::ErrorKind::InvalidData,
49            "used_by not found"
50        ))?;
51
52        Ok(Module {
53            module: name.to_string(),
54            size: size.parse::<u64>().map_err(|_| io::Error::new(
55                io::ErrorKind::InvalidData,
56                "module size is not a number"
57            ))?,
58            used_by: if used_by == "-" {
59                vec![]
60            } else {
61                used_by.split(',')
62                    .map(String::from)
63                    .filter(|x| !x.is_empty())
64                    .collect()
65            }
66        })
67    }
68
69    /// Iteratively parse lines from a /proc/modules-like source.
70    pub fn parse_from<'a, I: Iterator<Item = &'a str>>(lines: I) -> io::Result<Vec<Module>> {
71        lines.map(Self::parse).collect()
72    }
73
74    /// Collect a list of modules active on the system
75    pub fn all() -> io::Result<Vec<Module>> {
76        ModuleIter::new()?.collect()
77    }
78}
79
80/// Read module entries iteratively.
81pub struct ModuleIter {
82    file: BufReader<File>,
83    buffer: String,
84}
85
86impl ModuleIter {
87    pub fn new() -> io::Result<Self> {
88        Ok(Self {
89            file: BufReader::new(File::open("/proc/modules")?),
90            buffer: String::with_capacity(512),
91        })
92    }
93}
94
95impl Iterator for ModuleIter {
96    type Item = io::Result<Module>;
97
98    fn next(&mut self) -> Option<Self::Item> {
99        self.buffer.clear();
100        match self.file.read_line(&mut self.buffer) {
101            Ok(read) if read == 0 => None,
102            Ok(_) => Some(Module::parse(&self.buffer)),
103            Err(why) => Some(Err(why))
104        }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    const SAMPLE: &str = r#"snd_hda_intel 40960 9 - Live 0x0000000000000000
113snd_hda_codec 126976 4 snd_hda_codec_hdmi,snd_hda_codec_realtek,snd_hda_codec_generic,snd_hda_intel, Live 0x0000000000000000
114snd_hda_core 81920 5 snd_hda_codec_hdmi,snd_hda_codec_realtek,snd_hda_codec_generic,snd_hda_intel,snd_hda_codec, Live 0x0000000000000000
115nvidia_drm 40960 11 - Live 0x0000000000000000 (POE)"#;
116
117    #[test]
118    fn modules() {
119        assert_eq!(
120            Module::parse_from(SAMPLE.lines()).unwrap(),
121            vec![
122                Module {
123                    module: "snd_hda_intel".into(),
124                    size: 40960,
125                    used_by: vec![]
126                },
127                Module {
128                    module: "snd_hda_codec".into(),
129                    size: 126_976,
130                    used_by: vec![
131                        "snd_hda_codec_hdmi".into(),
132                        "snd_hda_codec_realtek".into(),
133                        "snd_hda_codec_generic".into(),
134                        "snd_hda_intel".into(),
135                    ]
136                },
137                Module {
138                    module: "snd_hda_core".into(),
139                    size: 81920,
140                    used_by: vec![
141                        "snd_hda_codec_hdmi".into(),
142                        "snd_hda_codec_realtek".into(),
143                        "snd_hda_codec_generic".into(),
144                        "snd_hda_intel".into(),
145                        "snd_hda_codec".into(),
146                    ]
147                },
148                Module {
149                    module: "nvidia_drm".into(),
150                    size: 40960,
151                    used_by: vec![]
152                },
153            ]
154        )
155    }
156}