1use anyhow::{Result, anyhow};
36use serde::Serialize;
37use std::fs::read_to_string;
38
39use crate::traits::ToJson;
40use crate::utils::Size;
41
42#[derive(Debug, Serialize, Default, Clone)]
44pub struct RAM {
45 pub total: Size,
47
48 pub free: Size,
51
52 pub available: Size,
55
56 pub buffers: Size,
59
60 pub cached: Size,
63
64 pub swap_cached: Size,
67
68 pub active: Size,
70
71 pub inactive: Size,
73
74 pub active_anon: Size,
76
77 pub inactive_anon: Size,
79
80 pub active_file: Size,
82
83 pub inactive_file: Size,
85
86 pub unevictable: Size,
88
89 pub mlocked: Size,
91
92 pub swap_total: Size,
94
95 pub swap_free: Size,
97
98 pub zswap: Size,
99 pub zswapped: Size,
100
101 pub dirty: Size,
103
104 pub writeback: Size,
106
107 pub anon_pages: Size,
109
110 pub mapped: Size,
113
114 pub shmem: Size,
116
117 pub kreclaimable: Size,
121
122 pub slab: Size,
125
126 pub sreclaimable: Size,
129
130 pub sunreclaim: Size,
132
133 pub kernel_stack: Size,
135
136 pub page_tables: Size,
139
140 pub sec_page_tables: Size,
141
142 pub nfs_unstable: Size,
145
146 pub bounce: Size,
149
150 pub writeback_tmp: Size,
152
153 pub commit_limit: Size,
156
157 pub commited_as: Size,
160
161 pub vmalloc_total: Size,
163
164 pub vmalloc_used: Size,
166
167 pub vmalloc_chunk: Size,
169
170 pub percpu: Size,
173
174 pub hardware_corrupted: Size,
177
178 pub anon_huge_pages: Size,
181
182 pub shmem_huge_pages: Size,
184
185 pub shmem_pmd_mapped: Size,
188
189 pub cma_total: Option<Size>,
191
192 pub cma_free: Option<Size>,
194
195 pub file_huge_pages: Size,
196 pub file_pmd_mapped: Size,
197 pub unaccepted: Size,
198 pub huge_pages_total: u32,
199 pub huge_pages_free: u32,
200 pub huge_pages_rsvd: u32,
201 pub huge_pages_surp: u32,
202 pub huge_page_size: Size,
203 pub huge_tlb: Size,
204 pub direct_map_4k: Size,
205 pub direct_map_2m: Size,
206 pub direct_map_1g: Size,
207}
208
209impl RAM {
210 pub fn new() -> Result<Self> {
211 let chunks = read_to_string("/proc/meminfo")?;
212 let chunks = chunks
213 .lines()
214 .map(|item| {
215 let mut items = item.split(':').map(|item| item.trim());
216 (items.next(), items.next())
217 })
218 .collect::<Vec<_>>();
219 let mut ram = RAM::default();
220 for chunk in chunks {
221 match chunk {
222 (Some(key), Some(val)) => match key {
223 "MemTotal" => ram.total = Size::try_from(val)?,
224 "MemFree" => ram.free = Size::try_from(val)?,
225 "MemAvailable" => ram.available = Size::try_from(val)?,
226 "Buffers" => ram.buffers = Size::try_from(val)?,
227 "Cached" => ram.cached = Size::try_from(val)?,
228 "SwapCached" => ram.swap_cached = Size::try_from(val)?,
229 "Active" => ram.active = Size::try_from(val)?,
230 "Inactive" => ram.inactive = Size::try_from(val)?,
231 "Active(anon)" => ram.active_anon = Size::try_from(val)?,
232 "Inactive(anon)" => ram.inactive_anon = Size::try_from(val)?,
233 "Active(file)" => ram.active_file = Size::try_from(val)?,
234 "Inactive(file)" => ram.inactive_file = Size::try_from(val)?,
235 "Unevictable" => ram.unevictable = Size::try_from(val)?,
236 "Mlocked" => ram.mlocked = Size::try_from(val)?,
237 "SwapTotal" => ram.swap_total = Size::try_from(val)?,
238 "SwapFree" => ram.swap_free = Size::try_from(val)?,
239 "Zswap" => ram.zswap = Size::try_from(val)?,
240 "Zswapped" => ram.zswapped = Size::try_from(val)?,
241 "Dirty" => ram.dirty = Size::try_from(val)?,
242 "Writeback" => ram.writeback = Size::try_from(val)?,
243 "AnonPages" => ram.anon_pages = Size::try_from(val)?,
244 "Mapped" => ram.mapped = Size::try_from(val)?,
245 "Shmem" => ram.shmem = Size::try_from(val)?,
246 "KReclaimable" => ram.kreclaimable = Size::try_from(val)?,
247 "Slab" => ram.slab = Size::try_from(val)?,
248 "SReclaimable" => ram.sreclaimable = Size::try_from(val)?,
249 "SUnreclaim" => ram.sunreclaim = Size::try_from(val)?,
250 "KernelStack" => ram.kernel_stack = Size::try_from(val)?,
251 "PageTables" => ram.page_tables = Size::try_from(val)?,
252 "SecPageTables" => ram.sec_page_tables = Size::try_from(val)?,
253 "NFS_Unstable" => ram.nfs_unstable = Size::try_from(val)?,
254 "Bounce" => ram.bounce = Size::try_from(val)?,
255 "WritebackTmp" => ram.writeback_tmp = Size::try_from(val)?,
256 "CommitLimit" => ram.commit_limit = Size::try_from(val)?,
257 "Committed_AS" => ram.commited_as = Size::try_from(val)?,
258 "VmallocTotal" => ram.vmalloc_total = Size::try_from(val)?,
259 "VmallocUsed" => ram.vmalloc_used = Size::try_from(val)?,
260 "VmallocChunk" => ram.vmalloc_chunk = Size::try_from(val)?,
261 "Percpu" => ram.percpu = Size::try_from(val)?,
262 "HardwareCorrupted" => ram.hardware_corrupted = Size::try_from(val)?,
263 "AnonHugePages" => ram.anon_huge_pages = Size::try_from(val)?,
264 "ShmemHugePages" => ram.shmem_huge_pages = Size::try_from(val)?,
265 "ShmemPmdMapped" => ram.shmem_pmd_mapped = Size::try_from(val)?,
266 "CmaTotal" => ram.cma_total = Size::try_from(val).ok(),
267 "CmaFree" => ram.cma_free = Size::try_from(val).ok(),
268 "FileHugePages" => ram.file_huge_pages = Size::try_from(val)?,
269 "FilePmdMapped" => ram.file_pmd_mapped = Size::try_from(val)?,
270 "Unaccepted" => ram.unaccepted = Size::try_from(val)?,
271 "HugePages_Total" => ram.huge_pages_total = val.parse()?,
272 "HugePages_Free" => ram.huge_pages_free = val.parse()?,
273 "HugePages_Rsvd" => ram.huge_pages_rsvd = val.parse()?,
274 "HugePages_Surp" => ram.huge_pages_surp = val.parse()?,
275 "Hugepagesize" => ram.huge_page_size = Size::try_from(val)?,
276 "Hugetlb" => ram.huge_tlb = Size::try_from(val)?,
277 "DirectMap4k" => ram.direct_map_4k = Size::try_from(val)?,
278 "DirectMap2M" => ram.direct_map_2m = Size::try_from(val)?,
279 "DirectMap1G" => ram.direct_map_1g = Size::try_from(val)?,
280 _ => continue,
281 },
282 _ => {}
283 }
284 }
285 Ok(ram)
286 }
287
288 pub fn used_ram(&self, base: u8) -> Size {
289 if base != 2 && base != 10 {
290 panic!("Unknown base: {base} (supported values: 2 or 10)");
291 }
292
293 let total = if base == 2 {
294 self.total.get_bytes2().unwrap_or(0) as f32 / 1024. / 1024. / 1024.
295 } else
296 {
298 self.total.get_bytes10().unwrap_or(0) as f32 / 1000. / 1000. / 1000.
299 };
300
301 let avail = if base == 2 {
302 self.available.get_bytes2().unwrap_or(0) as f32 / 1024. / 1024. / 1024.
303 } else
304 {
306 self.available.get_bytes10().unwrap_or(0) as f32 / 1000. / 1000. / 1000.
307 };
308 let used = total - avail;
309
310 Size::GB(used)
311 }
312
313 pub fn usage_percentage(&self) -> Option<f64> {
314 let total_ram = self.total.get_bytes2()?;
315 let avail_ram = self.available.get_bytes2()?;
316 let used_ram = total_ram - avail_ram;
317
318 Some(used_ram as f64 / total_ram as f64 * 100.)
319 }
320}
321
322impl ToJson for RAM {}
323
324#[derive(Debug, Serialize, Clone)]
326pub struct Swaps {
327 pub swaps: Vec<Swap>,
328}
329
330impl Swaps {
331 pub fn new() -> Result<Self> {
332 let mut swaps = vec![];
333
334 let data = read_to_string("/proc/swaps")?;
335 let items = data.lines().skip(1);
336
337 for swap in items {
338 swaps.push(Swap::try_from(swap)?)
339 }
340
341 Ok(Self { swaps })
342 }
343}
344
345impl ToJson for Swaps {}
346
347#[derive(Debug, Serialize, Clone)]
348pub struct Swap {
349 pub filename: String,
351
352 pub swap_type: String,
354
355 pub size: Size,
357
358 pub used: Size,
360
361 pub priority: i8,
363}
364
365impl Swap {
366 pub fn usage_percentage(&self) -> Option<f64> {
367 let total_swap = self.size.get_bytes2()?;
368 let used_swap = self.used.get_bytes2()?;
369
370 Some(used_swap as f64 / total_swap as f64 * 100.)
371 }
372}
373
374impl TryFrom<&str> for Swap {
375 type Error = anyhow::Error;
376
377 fn try_from(value: &str) -> Result<Self> {
378 let data = value.split_whitespace().collect::<Vec<_>>();
379 if data.len() != 5 {
380 return Err(anyhow!("Format of the \"{value}\" string is incorrect!"));
381 }
382
383 Ok(Self {
384 filename: data[0].to_string(),
385 swap_type: data[1].to_string(),
386 size: Size::KB(data[2].parse()?),
387 used: Size::KB(data[3].parse()?),
388 priority: data[4].parse()?,
389 })
390 }
391}
392impl ToJson for Swap {}