linapi/system/
modules.rs

1//! Interface to Dynamically Loaded Linux Kernel Modules.
2//!
3//! # Examples
4//!
5//! Print all currently loaded system modules
6//!
7//! ```rust
8//! # use linapi::system::modules::*;
9//!
10//! let mods = LoadedModule::get_loaded().unwrap();
11//!
12//! for m in mods {
13//!     println!("Module: {}", m.name());
14//! }
15//! ```
16//!
17//! Load a module
18//!
19//! ```rust,no_run
20//! # use linapi::system::modules::*;
21//!
22//! let m = ModuleFile::from_name("MyModule").unwrap();
23//! let loaded = m.load("my_param=1").unwrap();
24//! println!(
25//!     "Loaded module {}. my_param={}",
26//!     loaded.name(),
27//!     std::str::from_utf8(&loaded.parameters()["my_param"]).unwrap()
28//! );
29//! ```
30//!
31//! # Implementation
32//!
33//! This uses the sysfs interface, documented [here][1] and [here][2], and
34//! various undocumented interfaces where noted.
35//!
36//! [1]: https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-module
37//! [2]: https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-module
38use crate::{
39    error::{text::*, ModuleError},
40    extensions::FileExt,
41    system::{UEvent, UEventAction},
42    util::{read_uevent, write_uevent, MODULE_PATH, SYSFS_PATH},
43};
44#[cfg(feature = "gz")]
45use flate2::bufread::GzDecoder;
46use nix::{
47    kmod::{delete_module, finit_module, init_module, DeleteModuleFlags, ModuleInitFlags},
48    sys::utsname::uname,
49};
50use std::{
51    collections::HashMap,
52    ffi::CString,
53    fs,
54    fs::DirEntry,
55    io::{prelude::*, BufRead},
56    path::{Path, PathBuf},
57};
58use walkdir::WalkDir;
59use xmas_elf::ElfFile;
60#[cfg(feature = "xz")]
61use xz2::bufread::XzDecoder;
62#[cfg(feature = "zst")]
63use zstd::stream::read::Decoder as ZstDecoder;
64
65const SIGNATURE_MAGIC: &[u8] = b"~Module signature appended~\n";
66
67pub type Result<T, E = ModuleError> = std::result::Result<T, E>;
68
69/// Kernel modules can be "tainted", which serve as a marker for debugging
70/// purposes.
71#[derive(Debug, Clone, Copy)]
72pub enum Taint {
73    /// Proprietary Module.
74    Proprietary,
75
76    /// Out of tree, third party Module.
77    OutOfTree,
78
79    /// Module was force loaded.
80    Forced,
81
82    /// Unstable Staging Module.
83    Staging,
84
85    /// Unsigned Module.
86    Unsigned,
87}
88
89/// Module type
90#[derive(Debug, Clone, Copy)]
91pub enum Type {
92    /// Built in to the kernel.
93    ///
94    /// These only show up if they have a version or one run-time parameter, and
95    /// are missing most values.
96    ///
97    /// # Note
98    ///
99    /// The fact these show up isn't intentional and technically may change, or
100    /// so claim the kernel docs.
101    BuiltIn,
102
103    /// Dynamically loaded.
104    Dynamic,
105}
106
107/// Module Init Status
108#[derive(Debug, Clone)]
109pub enum Status {
110    /// Normal state, fully loaded.
111    Live,
112
113    /// Running module init
114    Coming,
115
116    /// Going away, running module exit?
117    Going,
118
119    /// Unknown
120    Unknown(String),
121}
122
123/// Describes a loaded Linux kernel Module
124#[derive(Debug)]
125pub struct LoadedModule {
126    /// The name of the Module
127    name: String,
128
129    /// Type of module
130    module_type: Type,
131
132    /// Path to the module
133    path: PathBuf,
134
135    /// Module parameters and their contents
136    parameters: HashMap<String, Vec<u8>>,
137
138    /// Module ref count
139    ref_count: Option<u32>,
140
141    /// Module taint
142    taint: Option<Taint>,
143
144    /// Module status
145    status: Option<Status>,
146
147    /// Module size in bytes
148    size: u64,
149
150    /// Module users
151    holders: Vec<Self>,
152}
153
154// Public
155impl LoadedModule {
156    /// Refresh information on the module
157    ///
158    /// # Errors
159    ///
160    /// - If any expected module attribute couldn't be read
161    /// - If any expected module attribute was invalid
162    pub fn refresh(&mut self) -> Result<()> {
163        let mut map = HashMap::new();
164        let par = self.path.join("parameters");
165        if par.exists() {
166            for entry in fs::read_dir(par)? {
167                let entry: DirEntry = entry?;
168                map.insert(
169                    entry
170                        .file_name()
171                        .into_string()
172                        .map_err(|_| ModuleError::InvalidModule(PARAMETER.into()))?,
173                    fs::read(entry.path()).unwrap_or_default(),
174                );
175            }
176        }
177        self.parameters = map;
178        self.ref_count = fs::read_to_string(self.path.join("refcnt"))
179            .map(|s| s.trim().parse())?
180            .ok();
181        self.taint = match fs::read_to_string(self.path.join("taint"))?.trim() {
182            "P" => Some(Taint::Proprietary),
183            "O" => Some(Taint::OutOfTree),
184            "F" => Some(Taint::Forced),
185            "C" => Some(Taint::Staging),
186            "E" => Some(Taint::Unsigned),
187            _ => None,
188        };
189        self.status = Some(
190            match fs::read_to_string(self.path.join("initstate"))?.trim() {
191                "live" => Status::Live,
192                "coming" => Status::Coming,
193                "going" => Status::Going,
194                s => Status::Unknown(s.into()),
195            },
196        );
197        self.size = fs::read_to_string(self.path.join("coresize"))
198            .map(|s| s.trim().parse())?
199            .map_err(|_| ModuleError::InvalidModule(PARAMETER.into()))?;
200        let mut v = Vec::new();
201        for re in fs::read_dir(self.path.join("holders"))? {
202            let re: DirEntry = re?;
203            v.push(Self::from_dir(&re.path())?)
204        }
205        self.holders = v;
206        //
207        Ok(())
208    }
209
210    /// Get an already loaded module by name
211    ///
212    /// # Errors
213    ///
214    /// - If no such module exists
215    /// - If the module is invalid in some way
216    pub fn from_name(name: &str) -> Result<Self> {
217        Self::from_dir(&Path::new(SYSFS_PATH).join("module").join(name))
218    }
219
220    /// Get currently loaded dynamic kernel modules.
221    ///
222    /// # Errors
223    ///
224    /// - IO
225    /// - If any modules couldn't be read
226    pub fn get_loaded() -> Result<Vec<Self>> {
227        let dir = Path::new(SYSFS_PATH).join("module");
228        let mut mods = Vec::new();
229        //
230        for module in fs::read_dir(dir)? {
231            let module: DirEntry = module?;
232            let m = Self::from_dir(&module.path())?;
233            if let Type::BuiltIn = m.module_type() {
234                continue;
235            }
236            mods.push(m);
237        }
238        Ok(mods)
239    }
240
241    /// Unload the module.
242    ///
243    /// # Errors
244    ///
245    /// - On failure
246    pub fn unload(self) -> Result<()> {
247        delete_module(
248            // This unwrap should be okay, `name` is from the file path which shouldn't have nul
249            // bytes
250            &CString::new(self.name.as_str()).unwrap(),
251            DeleteModuleFlags::O_NONBLOCK,
252        )
253        .map_err(|e| ModuleError::UnloadError(self.name, e.to_string()))?;
254        //
255        Ok(())
256    }
257
258    /// Forcefully unload a kernel module.
259    ///
260    /// # Safety
261    ///
262    /// Force unloading is wildly dangerous and will taint your kernel.
263    ///
264    /// It can cause modules to be unloaded while still in use, or unload
265    /// modules not designed to be unloaded.
266    ///
267    /// # Errors
268    ///
269    /// - On failure
270    pub unsafe fn force_unload(self) -> Result<()> {
271        delete_module(
272            // This unwrap should be okay, `name` is from the file path which shouldn't have nul
273            // bytes
274            &CString::new(self.name.as_str()).unwrap(),
275            DeleteModuleFlags::O_NONBLOCK | DeleteModuleFlags::O_TRUNC,
276        )
277        .map_err(|e| ModuleError::UnloadError(self.name, e.to_string()))?;
278        //
279        Ok(())
280    }
281
282    /// Name of the module
283    pub fn name(&self) -> &str {
284        &self.name
285    }
286
287    /// Module type, Builtin or Dynamic
288    pub fn module_type(&self) -> Type {
289        self.module_type
290    }
291
292    /// Module parameters.
293    ///
294    /// The kernel exposes these as files in a directory, and their contents are
295    /// entirely module specific, hence `HashMap<String, Vec<u8>>`, which can
296    /// be [`std::io::Read`].
297    ///
298    /// The key will be the parameter name and the value is it's data
299    ///
300    /// # Stability
301    ///
302    /// The stability of parameters depends entirely on the specific module.
303    pub fn parameters(&self) -> &HashMap<String, Vec<u8>> {
304        &self.parameters
305    }
306
307    /// Module reference count.
308    ///
309    /// If the module is built-in, or if the kernel was not built with
310    /// `CONFIG_MODULE_UNLOAD`, this will be [`None`]
311    pub fn ref_count(&self) -> Option<u32> {
312        self.ref_count
313    }
314
315    /// Module size in bytes
316    pub fn size(&self) -> u64 {
317        self.size
318    }
319
320    /// Module taint, or [`None`] if untainted.
321    ///
322    /// See [`Taint`] for details.
323    pub fn taint(&self) -> Option<Taint> {
324        self.taint
325    }
326
327    /// List of other modules that use/reference this one.
328    ///
329    /// # Note
330    ///
331    /// This uses the `holders` sysfs folder, which is completely undocumented
332    /// by the kernel, beware.
333    pub fn holders(&self) -> &Vec<Self> {
334        &self.holders
335    }
336
337    /// Get a [`ModuleFile`] from a [`LoadedModule`]
338    ///
339    /// This can be useful to get information, such as parameter types, about a
340    /// module.
341    ///
342    /// # Note
343    ///
344    /// There is no guarantee the returned path is the same module. The file may
345    /// have changed on disk, or been removed.
346    ///
347    /// This is equivalent to `ModuleFile::from_name(&self.name)`
348    pub fn module_file(&self) -> Result<ModuleFile> {
349        ModuleFile::from_name(&self.name)
350    }
351
352    /// Module status.
353    ///
354    /// # Note
355    ///
356    /// This uses the undocumented `initstate` file, which is probably
357    /// `module_state` from `linux/module.h`.
358    pub fn status(&self) -> &Status {
359        // Should be fine, refresh sets it to `Some`.
360        self.status.as_ref().unwrap()
361    }
362}
363
364// Private
365impl LoadedModule {
366    /// Create from module directory
367    ///
368    /// # Errors
369    ///
370    /// - If module doesn't exist
371    /// - If module is invalid
372    ///
373    /// # Note
374    ///
375    /// Built-in modules may appear in `/sys/modules` and they are ill-formed,
376    /// missing required files.
377    ///
378    /// In this case `refcnt` is [`None`], `coresize` is 0, and `taint` is
379    /// [`None`]
380    ///
381    /// This method performs automatic underscore conversion.
382    fn from_dir(path: &Path) -> Result<Self> {
383        let name = path
384            .file_name()
385            .expect("Missing module name")
386            .to_str()
387            .expect("Invalid module name");
388        // `/sys/modules` seems to always use `_` in paths?
389        let path = path.with_file_name(name.replace('-', "_"));
390        if !path.exists() {
391            return Err(ModuleError::Io(std::io::Error::new(
392                std::io::ErrorKind::NotFound,
393                format!("Couldn't find loaded module at {}", path.display()),
394            )));
395        }
396        let module_type = if path.join("coresize").exists() {
397            Type::Dynamic
398        } else {
399            Type::BuiltIn
400        };
401        let mut s = Self {
402            name: path
403                .file_stem()
404                .and_then(|s| s.to_str())
405                .map(|s| s.trim().to_owned())
406                .ok_or_else(|| ModuleError::InvalidModule(NAME.into()))?,
407            module_type,
408            path,
409            parameters: HashMap::new(),
410            ref_count: None,
411            taint: None,
412            status: None,
413            size: 0,
414            holders: Vec::new(),
415        };
416        if let Type::Dynamic = s.module_type {
417            s.refresh()?;
418        }
419        Ok(s)
420    }
421}
422
423impl UEvent for LoadedModule {
424    fn write(&self, action: UEventAction, uuid: Option<String>, args: HashMap<String, String>) {
425        write_uevent(&self.path.join("uevent"), action, uuid, args)
426    }
427    fn read(&self) -> HashMap<String, String> {
428        read_uevent(&self.path.join("uevent"))
429    }
430}
431
432/// A Linux Kernel Module file on disk.
433///
434/// On construction information about the module is read and saved.
435///
436/// But the file may change on disk or even be removed, so you can use
437/// `ModuleFile::refresh` to update the information or show an error if it's
438/// been removed.
439#[derive(Debug)]
440pub struct ModuleFile {
441    name: String,
442    path: PathBuf,
443    //
444    info: Option<ModInfo>,
445    signature: bool,
446}
447
448// Public methods
449impl ModuleFile {
450    /// Refresh information on the module
451    ///
452    /// # Errors
453    ///
454    /// - If the file no longer exists
455    /// - If the module or any of it's information is invalid
456    pub fn refresh(&mut self) -> Result<()> {
457        let img = self.read()?;
458        self.info = Some(self._info(&img)?);
459        self.signature = img.ends_with(SIGNATURE_MAGIC);
460        //
461        Ok(())
462    }
463
464    /// Search `/lib/modules/(uname -r)` for the module `name`.
465    ///
466    /// # Errors
467    ///
468    /// - If the module couldn't be found
469    /// - See [`ModuleFile::refresh`]
470    pub fn from_name(name: &str) -> Result<Self> {
471        Self::from_name_with_uname(name, uname().release())
472    }
473
474    /// Search `lib/modules/<uname>` for the module `name`.
475    ///
476    /// See [`ModuleFile::from_name`] for more details.
477    pub fn from_name_with_uname(name: &str, uname: &str) -> Result<Self> {
478        let path = Path::new(MODULE_PATH).join(uname);
479        for entry in WalkDir::new(path) {
480            let entry = entry.map_err(|e| ModuleError::Io(e.into()))?;
481            if !entry.file_type().is_file() {
482                continue;
483            }
484            // Get the module filename without any extensions.
485            // Modules are `.ko` but can be compressed, `.ko.xz`.
486            let m_name = entry
487                .path()
488                .file_stem()
489                .and_then(|s| s.to_str())
490                .and_then(|s| s.splitn(2, '.').next())
491                .ok_or_else(|| ModuleError::InvalidModule(INVALID_EXTENSION.into()))?;
492            if m_name == name {
493                let mut s = Self {
494                    name: name.into(),
495                    path: entry.into_path(),
496                    info: None,
497                    signature: false,
498                };
499                s.refresh()?;
500                return Ok(s);
501            }
502        }
503        Err(ModuleError::LoadError(name.into(), NOT_FOUND.into()))
504    }
505
506    /// Use the file at `path` as a module.
507    ///
508    /// # Errors
509    ///
510    /// - if `path` does not exist
511    /// - if `path` is not a valid module.
512    pub fn from_path(path: &Path) -> Result<Self> {
513        let mut s = Self {
514            name: path
515                .file_stem()
516                .and_then(|s| s.to_str())
517                .ok_or_else(|| {
518                    ModuleError::LoadError(path.display().to_string(), NOT_FOUND.into())
519                })?
520                .into(),
521            path: path.into(),
522            info: None,
523            signature: false,
524        };
525        s.refresh()?;
526        //
527        Ok(s)
528    }
529
530    /// Load this kernel module, and return the [`LoadedModule`] describing it.
531    ///
532    /// # Arguments
533    ///
534    /// - `param`eters for the kernel module. See module documentation for
535    ///   details, and `init_module(2)` for details on formatting.
536    ///
537    /// # Errors
538    ///
539    /// - If the file no longer exists
540    /// - If the file can't be decompressed
541    /// - If the module fails to load
542    ///
543    /// # Panics
544    ///
545    /// - if `param` has any `0` bytes.
546    ///
547    /// # Note
548    ///
549    /// Kernel modules may be compressed, and depending on crate features this
550    /// function may automatically decompress it.
551    pub fn load(&self, param: &str) -> Result<LoadedModule> {
552        let img = self.read()?;
553        // FIXME: ModuleError::AlreadyLoaded
554        init_module(
555            &img,
556            &CString::new(param).expect("param can't have internal null bytes"),
557        )
558        .map_err(|e| ModuleError::LoadError(self.name.clone(), e.to_string()))?;
559
560        Ok(LoadedModule::from_dir(
561            &Path::new(SYSFS_PATH).join("module").join(&self.name),
562        )?)
563    }
564
565    /// Force load this kernel module, and return the [`LoadedModule`]
566    /// describing it.
567    ///
568    /// See [`ModuleFile::load`] for more details.
569    ///
570    /// # Safety
571    ///
572    /// Force loading a kernel module is dangerous, it skips important safety
573    /// checks that help ensure module compatibility with your kernel.
574    pub unsafe fn force_load(&self, param: &str) -> Result<LoadedModule> {
575        let mut file = fs::File::create_memory("decompressed module");
576        file.write_all(&self.read()?)?;
577        //
578        finit_module(
579            &file,
580            &CString::new(param).expect("param can't have internal null bytes"),
581            ModuleInitFlags::MODULE_INIT_IGNORE_MODVERSIONS
582                | ModuleInitFlags::MODULE_INIT_IGNORE_VERMAGIC,
583        )
584        .map_err(|e| ModuleError::LoadError(self.name.clone(), e.to_string()))?;
585        //
586        Ok(LoadedModule::from_dir(
587            &Path::new(SYSFS_PATH).join("module").join(&self.name),
588        )?)
589    }
590
591    pub fn path(&self) -> &Path {
592        &self.path
593    }
594
595    pub fn name(&self) -> &str {
596        &self.name
597    }
598
599    /// Get information embedded in the module file.
600    pub fn info(&self) -> &ModInfo {
601        // This unwrap should be okay, as `refresh` should be called by all constructors
602        // and ensure this is `Some`
603        self.info.as_ref().unwrap()
604    }
605
606    /// Whether the module has a signature.
607    ///
608    /// This does not check if it's valid.
609    ///
610    /// # Note
611    ///
612    /// This is a temporary API, as `rust-openssl` does not expose the APIs
613    /// required for properly reading module signatures.
614    // FIXME: rust-openssl does not expose the APIs we need, so this isn't possible.
615    // When/if they do, see `module_signature.h` for details on structure.
616    pub fn has_signature(&self) -> bool {
617        self.signature
618    }
619}
620
621// Private methods
622impl ModuleFile {
623    fn read(&self) -> Result<Vec<u8>> {
624        self.decompress(fs::read(&self.path)?)
625    }
626
627    fn _info(&self, img: &[u8]) -> Result<ModInfo> {
628        let elf = ElfFile::new(img).map_err(|e| ModuleError::InvalidModule(e.to_string()))?;
629        let sect = elf
630            .find_section_by_name(".modinfo")
631            .ok_or_else(|| ModuleError::InvalidModule(MODINFO.into()))?;
632        let data = sect.raw_data(&elf);
633        //
634        let mut map = HashMap::new();
635        for kv in BufRead::split(data, b'\0') {
636            let kv = kv?;
637            let s = String::from_utf8(kv).map_err(|e| ModuleError::InvalidModule(e.to_string()))?;
638            let mut s = s.splitn(2, '=');
639            //
640            let key = s
641                .next()
642                .map(|s| s.to_string())
643                .ok_or_else(|| ModuleError::InvalidModule(MODINFO.into()))?;
644            let value = s
645                .next()
646                .map(|s| s.to_string())
647                .ok_or_else(|| ModuleError::InvalidModule(MODINFO.into()))?;
648            let vec = map.entry(key).or_insert_with(Vec::new);
649            if !value.is_empty() {
650                vec.push(value);
651            }
652        }
653        fn y_n(s: &str) -> bool {
654            s == "Y" || s == "y"
655        }
656        fn one(map: &mut HashMap<String, Vec<String>>, key: &str) -> String {
657            map.remove(key).map(|mut v| v.remove(0)).unwrap_or_default()
658        }
659        fn more(map: &mut HashMap<String, Vec<String>>, key: &str) -> Vec<String> {
660            map.remove(key).unwrap_or_default()
661        }
662        //
663        let mut x = HashMap::new();
664        for (name, typ) in map
665            .remove("parmtype")
666            .unwrap_or_default()
667            .into_iter()
668            .map(|s| {
669                let mut i = s.splitn(2, ':').map(|s| s.trim().to_owned());
670                (i.next(), i.next())
671            })
672        {
673            let name: Option<String> = name;
674            let typ: Option<String> = typ;
675
676            // Types are reasonably guaranteed to exist because
677            // `linux/moduleparam.h` adds them for all the `module_param`
678            // macros, which define parameters.
679            // FIXME: loop module param hw_queue_depth has no type
680            let name = name.ok_or_else(|| ModuleError::InvalidModule(MODINFO.into()))?;
681            let typ = typ.ok_or_else(|| ModuleError::InvalidModule(MODINFO.into()))?;
682
683            // Parameters should not have multiple types.
684            if x.insert(name, (typ, None)).is_some() {
685                return Err(ModuleError::InvalidModule(MODINFO.into()));
686            };
687        }
688
689        for (name, desc) in map.remove("parm").unwrap_or_default().into_iter().map(|s| {
690            let mut i = s.splitn(2, ':').map(|s| s.trim().to_owned());
691            (i.next(), i.next())
692        }) {
693            let name: Option<String> = name;
694            let desc: Option<String> = desc;
695
696            let name = name.ok_or_else(|| ModuleError::InvalidModule(MODINFO.into()))?;
697
698            // Add parameter descriptions
699            x.entry(name).or_insert(("Unknown".into(), None)).1 = desc;
700        }
701        let mut parameters = Vec::new();
702        for (name, (type_, description)) in x {
703            parameters.push(ModParam {
704                name,
705                type_,
706                description,
707            })
708        }
709        //
710        Ok(ModInfo {
711            alias: more(&mut map, "alias"),
712            soft_dependencies: more(&mut map, "softdep"),
713            license: one(&mut map, "license"),
714            authors: more(&mut map, "author"),
715            description: one(&mut map, "description"),
716            version: one(&mut map, "version"),
717            firmware: more(&mut map, "firmware"),
718            version_magic: one(&mut map, "vermagic"),
719            name: one(&mut map, "name"),
720            in_tree: y_n(&one(&mut map, "intree")),
721            retpoline: y_n(&one(&mut map, "retpoline")),
722            staging: y_n(&one(&mut map, "staging")),
723            dependencies: more(&mut map, "depends"),
724            source_checksum: one(&mut map, "srcversion"),
725            parameters,
726        })
727    }
728
729    /// Decompresses a kernel module
730    ///
731    /// Returns `data` unchanged if not compressed.
732    fn decompress(&self, data: Vec<u8>) -> Result<Vec<u8>> {
733        #[cfg(any(feature = "xz", feature = "gz", feature = "zst"))]
734        let mut v = Vec::new();
735        let ext = self
736            .path
737            .extension()
738            .and_then(|e| e.to_str())
739            .ok_or_else(|| ModuleError::InvalidModule(INVALID_EXTENSION.into()))?;
740        match ext {
741            #[cfg(feature = "xz")]
742            "xz" => {
743                let mut data = XzDecoder::new(data.as_slice());
744                data.read_to_end(&mut v)
745                    .map_err(|e| ModuleError::InvalidModule(e.to_string()))?;
746                Ok(v)
747            }
748            #[cfg(feature = "gz")]
749            "gz" => {
750                let mut data = GzDecoder::new(data.as_slice());
751                data.read_to_end(&mut v)
752                    .map_err(|e| ModuleError::InvalidModule(e.to_string()))?;
753                Ok(v)
754            }
755            #[cfg(feature = "zst")]
756            "zst" => {
757                let mut data = ZstDecoder::new(data.as_slice())
758                    .map_err(|_| ModuleError::InvalidModule(COMPRESSION.into()))?;
759                data.read_to_end(&mut v)
760                    .map_err(|e| ModuleError::InvalidModule(e.to_string()))?;
761                Ok(v)
762            }
763            "ko" => Ok(data),
764            _ => Err(ModuleError::InvalidModule(COMPRESSION.into())),
765        }
766    }
767}
768
769#[derive(Debug, Clone)]
770pub struct ModParam {
771    /// Parameter name
772    pub name: String,
773
774    /// Parameter name
775    ///
776    /// See `module_param` in `linux/moduleparam.h` for details
777    // TODO: Replace with enum for standard types
778    pub type_: String,
779
780    pub description: Option<String>,
781}
782
783/// Information on a [`ModuleFile`]
784///
785/// # Notes
786///
787/// This uses the `.modinfo` ELF section, which is semi-documented in
788/// `linux/modules.h` and `MODULE_INFO`.
789#[derive(Debug)]
790pub struct ModInfo {
791    /// Module Aliases. Alternative names for this module.
792    pub alias: Vec<String>,
793
794    /// Soft Dependencies. Not required, but may provide additional features.
795    pub soft_dependencies: Vec<String>,
796
797    /// Module License
798    ///
799    /// See `MODULE_LICENSE` for details on this value.
800    pub license: String,
801
802    /// Module Author and email
803    pub authors: Vec<String>,
804
805    /// What the module does
806    pub description: String,
807
808    /// Module version
809    pub version: String,
810
811    /// Optional firmware file(s) needed by the module
812    pub firmware: Vec<String>,
813
814    /// Version magic string, used by the kernel for compatibility checking.
815    pub version_magic: String,
816
817    /// Module name, self-reported.
818    pub name: String,
819
820    /// Whether the module is from the kernel source tree.
821    pub in_tree: bool,
822
823    /// The retpoline security feature
824    pub retpoline: bool,
825
826    /// If the module is staging
827    pub staging: bool,
828
829    /// Other modules this one depends on
830    pub dependencies: Vec<String>,
831
832    /// Source Checksum.
833    pub source_checksum: String,
834
835    /// Module Parameters
836    pub parameters: Vec<ModParam>,
837}