1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! <pre>
//! > 5.2.21.  /proc/modules
//! > This file displays a list of all modules loaded into the kernel. Its contents vary based on the configuration and use of your system, but it should be organized in a similar manner to this sample /proc/modules file output:
//! > Note
//! > 
//! > This example has been reformatted into a readable format. Most of this information can also be viewed via the /sbin/lsmod command.
//! > nfs      170109  0 -          Live 0x129b0000
//! > lockd    51593   1 nfs,       Live 0x128b0000
//! > nls_utf8 1729    0 -          Live 0x12830000
//! > vfat     12097   0 -          Live 0x12823000
//! > fat      38881   1 vfat,      Live 0x1287b000
//! > autofs4  20293   2 -          Live 0x1284f000
//! > sunrpc   140453  3 nfs,lockd, Live 0x12954000
//! > 3c59x    33257   0 -          Live 0x12871000
//! > uhci_hcd 28377   0 -          Live 0x12869000
//! > md5      3777    1 -          Live 0x1282c000
//! > ipv6     211845 16 -          Live 0x128de000
//! > ext3     92585   2 -          Live 0x12886000
//! > jbd      65625   1 ext3,      Live 0x12857000
//! > dm_mod   46677   3 -          Live 0x12833000
//! > The first column contains the name of the module.
//! > The second column refers to the memory size of the module, in bytes.
//! > The third column lists how many instances of the module are currently loaded. A value of zero represents an unloaded module.
//! > The fourth column states if the module depends upon another module to be present in order to function, and lists those other modules.
//! > The fifth column lists what load state the module is in: Live, Loading, or Unloading are the only possible values.
//! > The sixth column lists the current kernel memory offset for the loaded module. This information can be useful for debugging purposes, or for profiling tools such as oprofile.
//! </pre>
//! -- https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/deployment_guide/s1-proc-topfiles#s2-proc-modules
//! 

define_struct! {
    pub struct Module {
        name: String,
        mem_size: usize,
        instance_nums: usize,
        deps: Vec<String>,
        state: State,
        offset: usize,
    }
}

use std::str::FromStr;
impl FromStr for Module {
    type Err = crate::ProcErr;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let columns: Vec<&str> = s.split_ascii_whitespace().collect();
        if columns.len() != 6 {
            return Err("require 6 items to parse module".into());
        }
        let name = columns[0].to_string();
        let mem_size = columns[1].parse::<usize>()?;
        let instance_nums = columns[2].parse::<usize>()?;
        let deps: Vec<String> = if columns[3] == "-" {
            Vec::new()
        } else {
            let mut v: Vec<String> = columns[3].split(',').map(|s| s.to_string()).collect();
            v.pop();
            v
        };
        let state = State::from_str(columns[4])?;
        let offset = usize::from_str_radix(columns[5].trim_start_matches("0x"), 16)?;
        Ok(Module {
            name,
            mem_size,
            instance_nums,
            deps,
            state,
            offset,
        })
    }
}


#[derive(Debug, PartialEq, Clone, Copy)]
pub enum State {
    Live,
    Loading,
    Unloading,
}

impl FromStr for State {
    type Err = crate::ProcErr;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s == "Live" {
            Ok(State::Live)
        } else if s == "Loading" {
            Ok(State::Loading)
        } else if s == "Unloading" {
            Ok(State::Unloading)
        } else {
            Err("unknow stat".into())
        }
    }
}

list_impl! {
    modules, "/proc/modules", Module, '\n', 0
}


#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parse_state() {
        assert_eq!(State::Live, "Live".parse().unwrap());
        assert_eq!(State::Loading, "Loading".parse().unwrap());
        assert_eq!(State::Unloading, "Unloading".parse().unwrap());
        assert!("XYZ".parse::<State>().is_err())
    }

    #[test]
    fn test_parse_module1() {
        let source = "sbhid 53248 0 - Live 0x0000000000000000";
        let correct = Module{
            name: "sbhid".to_string(),
            mem_size: 53248,
            instance_nums: 0,
            deps: vec![],
            state: State::Live,
            offset: 0,
        };
        assert_eq!(correct, source.parse().unwrap());
    }

    #[test]
    fn test_parse_module2() {
        let source = "hid 110592 2 hid_generic,usbhid, Live 0x0000000000000000";
        let correct = Module {
            name: "hid".to_string(),
            mem_size: 110592,
            instance_nums: 2,
            deps: vec!["hid_generic".to_string(), "usbhid".to_string()],
            state: State::Live,
            offset: 0
        };
        assert_eq!(correct, source.parse().unwrap());
    }
}