sysinfo_report/
lib.rs

1use ::serde::{Deserialize, Serialize};
2use local_ip_address::list_afinet_netifas;
3
4pub const KIBIBYTE: u64 = 1024;
5use std::{
6    collections::HashMap,
7    net::{IpAddr, ToSocketAddrs},
8};
9
10//pub type Bytes = u64;
11
12use bytes::Bytes;
13use sysinfo::{CpuExt, DiskExt, System, SystemExt, UserExt};
14
15#[derive(Serialize, Deserialize, Debug)]
16pub struct Disk {
17    pub kind: DiskKind,
18    pub name: String,
19    pub file_system: Bytes,
20    pub mount_point: String,
21    pub total_space: u64,
22    pub available_space: u64,
23    pub is_removable: bool,
24}
25
26pub type DnsResult = Result<Vec<IpAddr>, String>;
27
28#[derive(Serialize, Deserialize, Debug)]
29pub struct Report {
30    pub host_name: Option<String>,
31    pub disks: Vec<Disk>,
32    pub memory: MemoryReport,
33    pub processor: Processor,
34    pub processors: Vec<Processor>,
35    pub uptime: u64,
36    pub users: Vec<User>,
37    pub networks: Vec<String>,
38    pub os: OperatingSystem,
39    pub kernel: Kernel,
40    pub dns_test: HashMap<String, DnsResult>,
41    pub network_interfaces: Result<Vec<NetworkInterface>, String>,
42}
43
44#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
45pub struct NetworkInterface {
46    pub name: String,
47    pub ip: IpAddr,
48}
49
50impl Default for Report {
51    fn default() -> Self {
52        get_report()
53    }
54}
55
56#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
57pub enum DiskKind {
58    /// HDD type.
59    HardDiskDrive,
60    /// SSD type.
61    SolidStateDrive,
62    /// Unknown type.
63    Unknown(isize),
64}
65
66#[derive(Serialize, Deserialize, Debug)]
67pub struct MemoryReport {
68    pub memory: Memory,
69    pub swap: Memory,
70}
71
72#[derive(Serialize, Deserialize, Debug)]
73pub struct Memory {
74    pub total: u64,
75    pub used: u64,
76    pub free: u64,
77}
78
79#[cfg(test)]
80mod tests {
81    use crate::get_report;
82
83    #[test]
84    fn get_local_report() {
85        let report = get_report();
86        println!("{:#?}", report);
87    }
88}
89
90#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
91pub struct Processor {
92    pub name: String,
93    pub vendor_id: String,
94    pub brand: String,
95}
96
97pub type Uid = u32;
98pub type Gid = u32;
99
100#[derive(Serialize, Deserialize, Debug)]
101pub struct User {
102    pub uid: Uid,
103    pub gid: Gid,
104    pub name: String,
105    pub groups: Vec<String>,
106}
107#[derive(Serialize, Deserialize, Debug)]
108
109pub struct OperatingSystem {
110    pub name: Option<String>,
111    pub version: Option<String>,
112    pub long_version: Option<String>,
113}
114
115#[derive(Serialize, Deserialize, Debug)]
116pub struct Kernel {
117    pub version: Option<String>,
118}
119
120pub const TEST_DNS_FOR: &[&str] = &["localhost", "ghcr.io", "docker.io", "mcr.microsoft.com"];
121
122fn convert_user(#[allow(unused_variables)] user: &sysinfo::User) -> User {
123    #[cfg(unix)]
124    return User {
125        groups: user.groups().into(),
126        name: user.name().into(),
127        gid: (*user.group_id()),
128        uid: (**user.id()),
129    };
130
131    #[cfg(windows)]
132    return User {
133        groups: user.groups().into(),
134        name: user.name().into(),
135        gid: 1000,
136        uid: 1000,
137    };
138
139    #[allow(unreachable_code)]
140    {
141        unimplemented!("cannot convert user on this platform");
142    }
143}
144
145pub fn get_report() -> Report {
146    let sys = System::new_all();
147
148    let os = OperatingSystem {
149        name: sys.name(),
150        version: sys.os_version(),
151        long_version: sys.long_os_version(),
152    };
153
154    let kernel = Kernel {
155        version: sys.kernel_version(),
156    };
157
158    let host_name = sys.host_name();
159
160    let memory = MemoryReport {
161        memory: Memory {
162            total: sys.total_memory() * KIBIBYTE,
163            used: sys.used_memory() * KIBIBYTE,
164            free: sys.free_memory() * KIBIBYTE,
165        },
166        swap: Memory {
167            total: sys.total_swap() * KIBIBYTE,
168            used: sys.used_swap() * KIBIBYTE,
169            free: sys.free_swap() * KIBIBYTE,
170        },
171    };
172
173    let processor = sys.global_cpu_info();
174
175    let processor = Processor {
176        brand: processor.brand().into(),
177        name: processor.name().into(),
178        vendor_id: processor.vendor_id().into(),
179    };
180
181    let processors: Vec<_> = sys
182        .cpus()
183        .iter()
184        .map(|processor| Processor {
185            brand: processor.brand().into(),
186            name: processor.name().into(),
187            vendor_id: processor.vendor_id().into(),
188        })
189        .collect();
190
191    let uptime = sys.uptime();
192
193    let users = sys.users();
194
195    let users: Vec<_> = users.iter().map(convert_user).collect();
196
197    let networks: Vec<_> = sys
198        .networks()
199        .into_iter()
200        .map(|(interface_name, _)| interface_name.to_owned())
201        .collect();
202
203    let dns_test = TEST_DNS_FOR
204        .iter()
205        .map(|host_name| {
206            (
207                host_name.to_string(),
208                (*host_name, 443)
209                    .to_socket_addrs()
210                    .map(|x| x.map(|socket_addr| socket_addr.ip()).collect())
211                    .map_err(|err| err.to_string()),
212            )
213        })
214        .collect();
215
216    // println!("{:#?}", dns_test);
217
218    let disks = sys
219        .disks()
220        .iter()
221        .map(|disk| Disk {
222            available_space: disk.available_space(),
223            kind: match disk.kind() {
224                ::sysinfo::DiskKind::HDD => DiskKind::HardDiskDrive,
225                ::sysinfo::DiskKind::SSD => DiskKind::SolidStateDrive,
226                ::sysinfo::DiskKind::Unknown(kind) => DiskKind::Unknown(kind),
227            },
228            name: disk.name().to_string_lossy().into(),
229            file_system: Bytes::copy_from_slice(disk.file_system()),
230            mount_point: disk.mount_point().to_string_lossy().into(),
231            total_space: disk.total_space(),
232            is_removable: disk.is_removable(),
233        })
234        .collect();
235
236    let network_interfaces = list_afinet_netifas()
237        .map_err(|err| err.to_string())
238        .map(|ok| {
239            ok.into_iter()
240                .map(|(name, ip)| NetworkInterface { ip, name })
241                .collect()
242        });
243
244    Report {
245        disks,
246        host_name,
247        memory,
248        processor,
249        processors,
250        uptime,
251        users,
252        networks,
253        os,
254        kernel,
255        dns_test,
256        network_interfaces,
257    }
258}