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
10use 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 HardDiskDrive,
60 SolidStateDrive,
62 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 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}