procfs_core/
devices.rs

1use std::io::BufRead;
2
3use super::ProcResult;
4use std::str::FromStr;
5
6#[cfg(feature = "serde1")]
7use serde::{Deserialize, Serialize};
8
9/// Device entries under `/proc/devices`
10#[derive(Debug, Clone)]
11#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
12pub struct Devices {
13    /// Character devices
14    pub char_devices: Vec<CharDeviceEntry>,
15    /// Block devices, which can be empty if the kernel doesn't support block devices (without `CONFIG_BLOCK`)
16    pub block_devices: Vec<BlockDeviceEntry>,
17}
18
19/// A charcter device entry under `/proc/devices`
20#[derive(Debug, Clone)]
21#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
22pub struct CharDeviceEntry {
23    /// Device major number
24    pub major: u32,
25    /// Device name
26    pub name: String,
27}
28
29/// A block device entry under `/proc/devices`
30#[derive(Debug, Clone)]
31#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
32pub struct BlockDeviceEntry {
33    /// Device major number
34    pub major: i32,
35    /// Device name
36    pub name: String,
37}
38
39impl super::FromBufRead for Devices {
40    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
41        enum State {
42            Char,
43            Block,
44        }
45        let mut state = State::Char; // Always start with char devices
46        let mut devices = Devices {
47            char_devices: vec![],
48            block_devices: vec![],
49        };
50
51        for line in r.lines() {
52            let line = expect!(line);
53
54            if line.is_empty() {
55                continue;
56            } else if line.starts_with("Character devices:") {
57                state = State::Char;
58                continue;
59            } else if line.starts_with("Block devices:") {
60                state = State::Block;
61                continue;
62            }
63
64            let mut s = line.split_whitespace();
65
66            match state {
67                State::Char => {
68                    let major = expect!(u32::from_str(expect!(s.next())));
69                    let name = expect!(s.next()).to_string();
70
71                    let char_device_entry = CharDeviceEntry { major, name };
72
73                    devices.char_devices.push(char_device_entry);
74                }
75                State::Block => {
76                    let major = expect!(i32::from_str(expect!(s.next())));
77                    let name = expect!(s.next()).to_string();
78
79                    let block_device_entry = BlockDeviceEntry { major, name };
80
81                    devices.block_devices.push(block_device_entry);
82                }
83            }
84        }
85
86        Ok(devices)
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    #[test]
94    fn test_devices() {
95        use crate::FromBufRead;
96        use std::io::Cursor;
97
98        let s = "Character devices:
99  1 mem
100  4 /dev/vc/0
101  4 tty
102  4 ttyS
103  5 /dev/tty
104  5 /dev/console
105  5 /dev/ptmx
106  7 vcs
107 10 misc
108 13 input
109 29 fb
110 90 mtd
111136 pts
112180 usb
113188 ttyUSB
114189 usb_device
115
116Block devices:
117  7 loop
118  8 sd
119 65 sd
120 71 sd
121128 sd
122135 sd
123254 device-mapper
124259 blkext
125";
126
127        let cursor = Cursor::new(s);
128        let devices = Devices::from_buf_read(cursor).unwrap();
129        let (chrs, blks) = (devices.char_devices, devices.block_devices);
130
131        assert_eq!(chrs.len(), 16);
132
133        assert_eq!(chrs[1].major, 4);
134        assert_eq!(chrs[1].name, "/dev/vc/0");
135
136        assert_eq!(chrs[8].major, 10);
137        assert_eq!(chrs[8].name, "misc");
138
139        assert_eq!(chrs[15].major, 189);
140        assert_eq!(chrs[15].name, "usb_device");
141
142        assert_eq!(blks.len(), 8);
143
144        assert_eq!(blks[0].major, 7);
145        assert_eq!(blks[0].name, "loop");
146
147        assert_eq!(blks[7].major, 259);
148        assert_eq!(blks[7].name, "blkext");
149    }
150
151    #[test]
152    fn test_devices_without_block() {
153        use crate::FromBufRead;
154        use std::io::Cursor;
155
156        let s = "Character devices:
157  1 mem
158  4 /dev/vc/0
159  4 tty
160  4 ttyS
161  5 /dev/tty
162  5 /dev/console
163  5 /dev/ptmx
164  7 vcs
165 10 misc
166 13 input
167 29 fb
168 90 mtd
169136 pts
170180 usb
171188 ttyUSB
172189 usb_device
173";
174
175        let cursor = Cursor::new(s);
176        let devices = Devices::from_buf_read(cursor).unwrap();
177        let (chrs, blks) = (devices.char_devices, devices.block_devices);
178
179        assert_eq!(chrs.len(), 16);
180
181        assert_eq!(chrs[1].major, 4);
182        assert_eq!(chrs[1].name, "/dev/vc/0");
183
184        assert_eq!(chrs[8].major, 10);
185        assert_eq!(chrs[8].name, "misc");
186
187        assert_eq!(chrs[15].major, 189);
188        assert_eq!(chrs[15].name, "usb_device");
189
190        assert_eq!(blks.len(), 0);
191    }
192}