1#[macro_use]
12extern crate enum_primitive;
13extern crate hex;
14extern crate num;
15
16use hex::FromHex;
17use num::FromPrimitive;
18
19use std::convert::Infallible;
20use std::default::Default;
21use std::fs::File;
22use std::io;
23use std::io::Read;
24use std::net::Ipv4Addr;
25use std::str::FromStr;
26
27#[derive(Debug, PartialEq, Clone, Default)]
29pub struct Stat {
30 pub cpu: Vec<u64>,
31 pub cpus: Vec<Vec<u64>>,
32 pub intr: Vec<u64>,
33 pub ctxt: u64,
34 pub btime: u32,
35 pub processes: u32,
36 pub procs_running: u32,
37 pub procs_blocked: u32,
38 pub softirq: Vec<u64>,
39}
40
41impl FromStr for Stat {
42 type Err = Infallible;
43
44 fn from_str(s: &str) -> Result<Stat, Infallible> {
45 let mut stat: Stat = Default::default();
46 for (line_num, line) in s.lines().enumerate() {
47 if line_num == 0 {
48 stat.cpu = to_vecu64(line);
49 }
50
51 if line.starts_with("cpu") && line_num > 0 {
52 stat.cpus.push(to_vecu64(line));
53 }
54
55 if line.starts_with("intr") {
56 stat.intr = to_vecu64(line);
57 }
58
59 if line.starts_with("ctxt") {
60 let mut chunks = line.split_whitespace();
61 chunks.next();
62
63 stat.ctxt = chunks.next().unwrap().parse::<u64>().unwrap();
64 }
65
66 if line.starts_with("btime") {
67 let mut chunks = line.split_whitespace();
68 chunks.next();
69
70 stat.btime = chunks.next().unwrap().parse::<u32>().unwrap();
71 }
72
73 if line.starts_with("processes") {
74 let mut chunks = line.split_whitespace();
75 chunks.next();
76
77 stat.processes = chunks.next().unwrap().parse::<u32>().unwrap();
78 }
79
80 if line.starts_with("procs_running") {
81 let mut chunks = line.split_whitespace();
82 chunks.next();
83
84 stat.procs_running = chunks.next().unwrap().parse::<u32>().unwrap();
85 }
86
87 if line.starts_with("procs_blocked") {
88 let mut chunks = line.split_whitespace();
89 chunks.next();
90
91 stat.procs_blocked = chunks.next().unwrap().parse::<u32>().unwrap();
92 }
93
94 if line.starts_with("softirq") {
95 stat.softirq = to_vecu64(line);
96 }
97 }
98
99 Ok(stat)
100 }
101}
102
103#[derive(Debug, PartialEq, Clone, Default)]
105pub struct MemInfo {
106 pub mem_total: u64,
107 pub mem_free: u64,
108 pub mem_available: u64,
109 pub bufers: u64,
110 pub cached: u64,
111 pub swap_cached: u64,
112 pub active: u64,
113 pub inactive: u64,
114 pub active_anon: u64,
115 pub inactive_anon: u64,
116 pub active_file: u64,
117 pub inactive_file: u64,
118 pub unevictable: u64,
119 pub mlocked: u64,
120 pub swap_total: u64,
121 pub swap_free: u64,
122 pub dirty: u64,
123 pub writeback: u64,
124 pub anon_pages: u64,
125 pub mapped: u64,
126 pub shmem: u64,
127 pub slab: u64,
128 pub s_reclaimable: u64,
129 pub s_unreclaim: u64,
130 pub kernel_stack: u64,
131 pub page_tables: u64,
132 pub nfs_unstable: u64,
133 pub bounce: u64,
134 pub writeback_tmp: u64,
135 pub commit_limit: u64,
136 pub committed_as: u64,
137 pub vmalloc_total: u64,
138 pub vmalloc_used: u64,
139 pub vmalloc_chunk: u64,
140 pub hardware_corrupted: u64,
141 pub anon_huge_pages: u64,
142 pub cma_total: u64,
143 pub cma_free: u64,
144 pub huge_pages_total: u64,
145 pub huge_pages_free: u64,
146 pub huge_pages_rsvd: u64,
147 pub huge_pages_surp: u64,
148 pub hugepagesize: u64,
149 pub direct_map_4k: u64,
150 pub direct_map_2m: u64,
151}
152
153impl FromStr for MemInfo {
154 type Err = Infallible;
155
156 fn from_str(s: &str) -> Result<MemInfo, Infallible> {
157 let mut meminfo: MemInfo = Default::default();
158
159 for line in s.lines() {
160 if line.starts_with("MemTotal") {
161 meminfo.mem_total = to_u64(line);
162 }
163
164 if line.starts_with("MemFree") {
165 meminfo.mem_free = to_u64(line);
166 }
167
168 if line.starts_with("MemAvailable") {
169 meminfo.mem_available = to_u64(line);
170 }
171
172 if line.starts_with("Buffers") {
173 meminfo.bufers = to_u64(line);
174 }
175
176 if line.starts_with("Cached") {
177 meminfo.cached = to_u64(line);
178 }
179
180 if line.starts_with("SwapCached") {
181 meminfo.swap_cached = to_u64(line);
182 }
183
184 if line.starts_with("Active") {
185 meminfo.active = to_u64(line);
186 }
187
188 if line.starts_with("Inactive") {
189 meminfo.inactive = to_u64(line);
190 }
191
192 if line.starts_with("Active(anon)") {
193 meminfo.active_anon = to_u64(line);
194 }
195
196 if line.starts_with("Inactive(anon)") {
197 meminfo.inactive_anon = to_u64(line);
198 }
199
200 if line.starts_with("Active(file)") {
201 meminfo.active_file = to_u64(line);
202 }
203
204 if line.starts_with("Inactive(file)") {
205 meminfo.inactive_file = to_u64(line);
206 }
207
208 if line.starts_with("Unevictable") {
209 meminfo.unevictable = to_u64(line);
210 }
211
212 if line.starts_with("Mlocked") {
213 meminfo.mlocked = to_u64(line);
214 }
215
216 if line.starts_with("SwapTotal") {
217 meminfo.swap_total = to_u64(line);
218 }
219
220 if line.starts_with("SwapFree") {
221 meminfo.swap_free = to_u64(line);
222 }
223
224 if line.starts_with("Dirty") {
225 meminfo.dirty = to_u64(line);
226 }
227
228 if line.starts_with("Writeback") {
229 meminfo.writeback = to_u64(line);
230 }
231
232 if line.starts_with("AnonPages") {
233 meminfo.anon_pages = to_u64(line);
234 }
235
236 if line.starts_with("Mapped") {
237 meminfo.mapped = to_u64(line);
238 }
239
240 if line.starts_with("Shmem") {
241 meminfo.shmem = to_u64(line);
242 }
243
244 if line.starts_with("Slab") {
245 meminfo.slab = to_u64(line);
246 }
247
248 if line.starts_with("SReclaimable") {
249 meminfo.s_reclaimable = to_u64(line);
250 }
251
252 if line.starts_with("SUnreclaim") {
253 meminfo.s_unreclaim = to_u64(line);
254 }
255
256 if line.starts_with("KernelStack") {
257 meminfo.kernel_stack = to_u64(line);
258 }
259
260 if line.starts_with("PageTables") {
261 meminfo.page_tables = to_u64(line);
262 }
263
264 if line.starts_with("NFS_Unstable") {
265 meminfo.nfs_unstable = to_u64(line);
266 }
267
268 if line.starts_with("Bounce") {
269 meminfo.bounce = to_u64(line);
270 }
271
272 if line.starts_with("WritebackTmp") {
273 meminfo.writeback_tmp = to_u64(line);
274 }
275
276 if line.starts_with("CommitLimit") {
277 meminfo.commit_limit = to_u64(line);
278 }
279
280 if line.starts_with("Committed_AS") {
281 meminfo.committed_as = to_u64(line);
282 }
283
284 if line.starts_with("VmallocTotal") {
285 meminfo.vmalloc_total = to_u64(line);
286 }
287
288 if line.starts_with("VmallocUsed") {
289 meminfo.vmalloc_used = to_u64(line);
290 }
291
292 if line.starts_with("VmallocChunk") {
293 meminfo.vmalloc_chunk = to_u64(line);
294 }
295
296 if line.starts_with("HardwareCorrupted") {
297 meminfo.hardware_corrupted = to_u64(line);
298 }
299
300 if line.starts_with("AnonHugePages") {
301 meminfo.anon_huge_pages = to_u64(line);
302 }
303
304 if line.starts_with("CmaTotal") {
305 meminfo.cma_total = to_u64(line);
306 }
307
308 if line.starts_with("CmaFree") {
309 meminfo.cma_free = to_u64(line);
310 }
311
312 if line.starts_with("HugePages_Total") {
313 meminfo.huge_pages_total = to_u64(line);
314 }
315
316 if line.starts_with("HugePages_Free") {
317 meminfo.huge_pages_free = to_u64(line);
318 }
319
320 if line.starts_with("HugePages_Rsvd") {
321 meminfo.huge_pages_rsvd = to_u64(line);
322 }
323
324 if line.starts_with("HugePages_Surp") {
325 meminfo.huge_pages_surp = to_u64(line);
326 }
327
328 if line.starts_with("Hugepagesize") {
329 meminfo.hugepagesize = to_u64(line);
330 }
331
332 if line.starts_with("DirectMap4k") {
333 meminfo.direct_map_4k = to_u64(line);
334 }
335
336 if line.starts_with("DirectMap2M") {
337 meminfo.direct_map_2m = to_u64(line);
338 }
339 }
340
341 Ok(meminfo)
342 }
343}
344
345enum_from_primitive! {
346 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
348 pub enum SocketState {
349 Established = 1,
350 SynSent = 2,
351 SynRecv = 3,
352 FinWait1 = 4,
353 FinWait2 = 5,
354 TimeWait = 6,
355 Close = 7,
356 CloseWait = 8,
357 LastAck = 9,
358 Listen = 10,
359 Closing = 11
360 }
361}
362
363#[derive(Clone, Debug, PartialEq)]
365pub enum SocketTimerState {
366 Inactive,
368 Active(u64),
369}
370
371#[derive(Clone)]
373pub struct Socket {
374 pub sl: u64,
375 pub local_address: Ipv4Addr,
376 pub local_port: u16,
377 pub remote_address: Ipv4Addr,
378 pub remote_port: u16,
379 pub state: SocketState,
380 pub tx_queue: u64,
381 pub rx_queue: u64,
382 pub timer: SocketTimerState,
383 pub uid: u32,
384 pub inode: u64,
385}
386
387pub fn stat() -> io::Result<Stat> {
388 read_file("/proc/stat")?
389 .parse()
390 .map_err(|_| panic!("Infallible result occured"))
391}
392
393pub fn meminfo() -> io::Result<MemInfo> {
394 read_file("/proc/meminfo")?
395 .parse()
396 .map_err(|_| panic!("Infallible result occured"))
397}
398
399pub fn tcp() -> io::Result<Vec<Socket>> {
400 net("/proc/net/tcp")
401}
402
403pub fn udp() -> io::Result<Vec<Socket>> {
404 net("/proc/net/udp")
405}
406
407fn read_file(path: &str) -> io::Result<String> {
408 let file = File::open(path);
409 let mut content = String::new();
410
411 file.map(|mut f| f.read_to_string(&mut content))
412 .and(Ok(content))
413}
414
415fn net(file: &str) -> io::Result<Vec<Socket>> {
416 let content = read_file(file);
417 match content {
418 Ok(c) => Ok(c.lines().skip(1).map(to_net_socket).collect()),
419 Err(e) => Err(e),
420 }
421}
422
423fn to_vecu64(line: &str) -> Vec<u64> {
424 let mut chunks = line.split_whitespace();
425 let mut buf = Vec::<u64>::new();
426
427 chunks.next();
429
430 for chunk in chunks {
431 buf.push(chunk.parse::<u64>().unwrap());
432 }
433
434 buf
435}
436
437fn to_u64(line: &str) -> u64 {
438 let mut chunks = line.split_whitespace();
439 chunks.next();
440
441 chunks.next().unwrap().parse::<u64>().unwrap()
442}
443
444fn to_net_socket(line: &str) -> Socket {
445 let mut chunks = line.split_whitespace();
446 let sl = chunks
447 .next()
448 .unwrap()
449 .split(':')
450 .next()
451 .unwrap()
452 .parse::<u64>()
453 .unwrap();
454
455 let local: Vec<&str> = chunks.next().unwrap().split(':').collect();
458 let remote: Vec<&str> = chunks.next().unwrap().split(':').collect();
459 let state = Vec::<u8>::from_hex(chunks.next().unwrap()).unwrap()[0];
460 let queues: Vec<&str> = chunks.next().unwrap().split(':').collect();
461 let timer: Vec<&str> = chunks.next().unwrap().split(':').collect();
462 chunks.next().unwrap();
464 let uid = chunks.next().unwrap().parse::<u32>().unwrap();
465 chunks.next().unwrap();
467 let inode = chunks.next().unwrap().parse::<u64>().unwrap();
468
469 Socket {
470 sl,
471 local_address: to_ipaddr(local[0]),
472 local_port: u16::from_str_radix(local[1], 16).unwrap(),
473 remote_address: to_ipaddr(remote[0]),
474 remote_port: u16::from_str_radix(remote[1], 16).unwrap(),
475 state: SocketState::from_u8(state).unwrap(),
476 tx_queue: u64::from_str_radix(queues[0], 16).unwrap(),
477 rx_queue: u64::from_str_radix(queues[1], 16).unwrap(),
478 timer: match timer[0].parse::<u8>().unwrap() {
479 0 => SocketTimerState::Inactive,
480 _ => SocketTimerState::Active(u64::from_str_radix(timer[1], 16).unwrap()),
481 },
482 uid,
483 inode,
484 }
485}
486
487fn to_ipaddr(hex: &str) -> Ipv4Addr {
488 let bytes = Vec::<u8>::from_hex(hex).unwrap();
489 Ipv4Addr::from([bytes[3], bytes[2], bytes[1], bytes[0]])
490}
491
492#[test]
493fn test_to_ipaddr() {
494 let addr = to_ipaddr("0100007F");
495 assert_eq!(addr.octets(), [127, 0, 0, 1]);
496}
497
498#[test]
499fn test_to_net_socket() {
500 let sock = to_net_socket(" 49: 0100007F:1132 5B41EE2E:0050 0A 0000000A:00000002 01:0000000B 00000000 1001 0 2796814 1 ffff938ed0741080 20 4 29 10 -1");
501 assert_eq!(sock.local_address.octets(), [127, 0, 0, 1]);
502 assert_eq!(sock.local_port, 4402);
503 assert_eq!(sock.remote_address.octets(), [46, 238, 65, 91]);
504 assert_eq!(sock.remote_port, 80);
505 assert_eq!(sock.state, SocketState::Listen);
506 assert_eq!(sock.tx_queue, 0xA);
507 assert_eq!(sock.rx_queue, 2);
508 assert_eq!(sock.timer, SocketTimerState::Active(0xB));
509 assert_eq!(sock.uid, 1001);
510 assert_eq!(sock.inode, 2796814);
511}