1#![crate_type = "bin"]
4#![allow(unused_must_use, non_upper_case_globals)]
5#![allow(clippy::manual_range_contains)]
6
7use std::io::{self, BufRead, Write};
8use std::str::FromStr;
9use sysinfo::{
10 Components, Disks, Error, Gpus, Groups, Motherboard, Networks, Pid, ProcessRefreshKind,
11 Product, SUPPORTED_SIGNALS, System, Users,
12};
13
14fn print_help() {
15 println!(
16 "\
17== Help menu ==
18
19help : shows this menu
20quit : exits the program
21
22= Refresh commands =
23
24refresh : reloads all processes information
25refresh [pid] : reloads corresponding process information
26refresh_components : reloads components information
27refresh_cpu : reloads CPU information
28refresh_disks : reloads disks information
29refresh_networks : reloads networks information
30refresh_users : reloads users information
31
32= Process commands =
33
34all : displays all process name and pid
35kill [pid] [signal]: sends [signal] to the process with this [pid]. To get the [signal] number, use the `signals` command.
36show [pid | name] : shows information of the given process corresponding to [pid | name]
37signals : shows the available signals
38pid : displays this example's PID
39
40= CPU commands =
41
42brand : displays CPU brand
43cpus : displays CPUs state
44frequency : displays CPU frequency
45vendor_id : displays CPU vendor id
46
47= GPU commands =
48
49gpus : displays all GPUs
50
51= Users and groups commands =
52
53groups : displays all groups
54users : displays all users and their groups
55
56= System commands =
57
58boot_time : displays system boot time
59load_avg : displays system load average
60system : displays system information (such as name, version and hostname)
61uptime : displays system uptime
62motherboard : displays motherboard information
63product : displays product information
64
65= Extra components commands =
66
67disks : displays disks' information
68memory : displays memory state
69network : displays network' information
70temperature : displays components' temperature"
71 );
72}
73
74fn interpret_input(
75 input: &str,
76 sys: Result<&mut System, &mut Error>,
77 gpus: Result<&mut Gpus, &mut Error>,
78 networks: Result<&mut Networks, &mut Error>,
79 disks: Result<&mut Disks, &mut Error>,
80 components: Result<&mut Components, &mut Error>,
81 users: Result<&mut Users, &mut Error>,
82) -> bool {
83 match input.trim() {
84 "help" => print_help(),
85 "refresh_disks" => match disks {
86 Ok(disks) => {
87 println!("Refreshing disk list...");
88 disks.refresh(true);
89 println!("Done.");
90 }
91 Err(error) => {
92 println!("Disks information cannot be retrieved: {error}");
93 }
94 },
95 "refresh_users" => match users {
96 Ok(users) => {
97 println!("Refreshing user list...");
98 users.refresh();
99 println!("Done.");
100 }
101 Err(error) => {
102 println!("Users information cannot be retrieved: {error}");
103 }
104 },
105 "refresh_networks" => match networks {
106 Ok(networks) => {
107 println!("Refreshing network list...");
108 networks.refresh(true);
109 println!("Done.");
110 }
111 Err(error) => {
112 println!("Networks information cannot be retrieved: {error}");
113 }
114 },
115 "refresh_components" => match components {
116 Ok(components) => {
117 println!("Refreshing component list...");
118 components.refresh(true);
119 println!("Done.");
120 }
121 Err(error) => {
122 println!("Components information cannot be retrieved: {error}");
123 }
124 },
125 "refresh_cpu" => match sys {
126 Ok(sys) => {
127 println!("Refreshing CPUs...");
128 sys.refresh_cpu_all();
129 println!("Done.");
130 }
131 Err(error) => {
132 println!("System information cannot be retrieved: {error}");
133 }
134 },
135 "signals" => {
136 for (nb, sig) in SUPPORTED_SIGNALS.iter().enumerate() {
137 println!("{:2}:{sig:?}", nb + 1);
138 }
139 }
140 "cpus" => {
141 match sys {
142 Ok(sys) => {
143 println!(
146 "number of physical cores: {}",
147 System::physical_core_count()
148 .map(|c| c.to_string())
149 .unwrap_or_else(|_| "Unknown".to_owned()),
150 );
151 println!("total CPU usage: {}%", sys.global_cpu_usage());
152 for cpu in sys.cpus() {
153 println!("{cpu:?}");
154 }
155 }
156 Err(error) => {
157 println!("System information cannot be retrieved: {error}");
158 }
159 }
160 }
161 "gpus" => match gpus {
162 Ok(gpus) => {
163 gpus.refresh(true);
164 for gpu in &*gpus {
165 println!(
166 "GPU (PCI: {}): Vendor: {}",
167 gpu.pci(),
168 gpu.vendor().unwrap_or("Unknown")
169 );
170 if let Some(model) = gpu.model() {
171 println!(" model: {model}");
172 }
173 match gpu.usage() {
174 Some(usage) => println!(" usage: {usage}%"),
175 None => println!(" usage: N/A"),
176 }
177 if let (Some(used), Some(total)) = (gpu.used_memory(), gpu.total_memory()) {
178 println!(" memory: {}/{} KB", used / 1_000, total / 1_000);
179 } else {
180 println!(" memory: N/A");
181 }
182 }
183 }
184 Err(error) => {
185 println!("GPU information cannot be retrieved on this system: {error}");
186 }
187 },
188 "memory" => match sys {
189 Ok(sys) => {
190 println!("total memory: {: >10} KB", sys.total_memory() / 1_000);
191 println!(
192 "available memory: {: >10} KB",
193 sys.available_memory() / 1_000
194 );
195 println!("used memory: {: >10} KB", sys.used_memory() / 1_000);
196 println!("total swap: {: >10} KB", sys.total_swap() / 1_000);
197 println!("used swap: {: >10} KB", sys.used_swap() / 1_000);
198 }
199 Err(error) => {
200 println!("System information cannot be retrieved: {error}");
201 }
202 },
203 "quit" | "exit" => return true,
204 "all" => match sys {
205 Ok(sys) => {
206 for (pid, proc_) in sys.processes() {
207 println!(
208 "{}:{} status={:?}",
209 pid,
210 proc_.name().to_string_lossy(),
211 proc_.status()
212 );
213 }
214 }
215 Err(error) => {
216 println!("System information cannot be retrieved: {error}");
217 }
218 },
219 "frequency" => match sys {
220 Ok(sys) => {
221 for cpu in sys.cpus() {
222 println!("[{}] {} MHz", cpu.name(), cpu.frequency());
223 }
224 }
225 Err(error) => {
226 println!("System information cannot be retrieved: {error}");
227 }
228 },
229 "vendor_id" => match sys {
230 Ok(sys) => {
231 println!("vendor ID: {}", sys.cpus()[0].vendor_id());
232 }
233 Err(error) => {
234 println!("System information cannot be retrieved: {error}");
235 }
236 },
237 "brand" => match sys {
238 Ok(sys) => {
239 println!("brand: {}", sys.cpus()[0].brand());
240 }
241 Err(error) => {
242 println!("System information cannot be retrieved: {error}");
243 }
244 },
245 "load_avg" => match System::load_average() {
246 Ok(load_avg) => {
247 println!("one minute : {}%", load_avg.one);
248 println!("five minutes : {}%", load_avg.five);
249 println!("fifteen minutes: {}%", load_avg.fifteen);
250 }
251 Err(error) => {
252 eprintln!("Failed to get `load_average`: {error}");
253 }
254 },
255 e if e.starts_with("show ") => {
256 let tmp: Vec<&str> = e.split(' ').filter(|s| !s.is_empty()).collect();
257
258 if tmp.len() != 2 {
259 println!("show command takes a pid or a name in parameter!");
260 println!("example: show 1254");
261 } else {
262 match sys {
263 Ok(sys) => {
264 if let Ok(pid) = Pid::from_str(tmp[1]) {
265 match sys.process(pid) {
266 Some(p) => {
267 println!("{:#?}", *p);
268 println!(
269 "Files open/limit: {:?}/{:?}",
270 p.open_files(),
271 p.open_files_limit(),
272 );
273 }
274 None => {
275 println!("pid \"{pid:?}\" not found");
276 }
277 }
278 } else {
279 let proc_name = tmp[1];
280 for proc_ in sys.processes_by_name(proc_name.as_ref()) {
281 println!("==== {} ====", proc_.name().to_string_lossy());
282 println!("{proc_:?}");
283 }
284 }
285 }
286 Err(error) => {
287 println!("System information cannot be retrieved: {error}");
288 }
289 }
290 }
291 }
292 "temperature" => match components {
293 Ok(components) => {
294 for component in components.iter() {
295 println!("{component:?}");
296 }
297 }
298 Err(error) => {
299 println!("Components information cannot be retrieved: {error}");
300 }
301 },
302 "network" => match networks {
303 Ok(networks) => {
304 for (interface_name, data) in networks.iter() {
305 println!(
306 "\
307 {}:
308 operational state {}
309 ether {}
310 input data (new / total): {} / {} B
311 output data (new / total): {} / {} B",
312 interface_name.display(),
313 data.operational_state(),
314 data.mac_address(),
315 data.received(),
316 data.total_received(),
317 data.transmitted(),
318 data.total_transmitted(),
319 );
320 }
321 }
322 Err(error) => {
323 println!("Networks information cannot be retrieved: {error}");
324 }
325 },
326 "show" => {
327 println!("'show' command expects a pid number or a process name");
328 }
329 e if e.starts_with("kill ") => {
330 let tmp: Vec<&str> = e
331 .split(' ')
332 .map(|s| s.trim())
333 .filter(|s| !s.is_empty())
334 .collect();
335
336 if tmp.len() != 3 {
337 println!("kill command takes the pid and a signal number in parameter!");
338 println!("example: kill 1254 9");
339 } else {
340 let Ok(pid) = Pid::from_str(tmp[1]) else {
341 eprintln!("Expected a number for the PID, found {:?}", tmp[1]);
342 return false;
343 };
344 let Ok(signal) = usize::from_str(tmp[2]) else {
345 eprintln!("Expected a number for the signal, found {:?}", tmp[2]);
346 return false;
347 };
348 let Some(signal) = SUPPORTED_SIGNALS.get(signal) else {
349 eprintln!(
350 "No signal matching {signal}. Use the `signals` command to get the \
351 list of signals.",
352 );
353 return false;
354 };
355
356 match sys {
357 Ok(sys) => match sys.process(pid) {
358 Some(p) => {
359 if let Some(res) = p.kill_with(*signal) {
360 println!("kill: {res}");
361 } else {
362 eprintln!("kill: signal not supported on this platform");
363 }
364 }
365 None => {
366 eprintln!("pid not found");
367 }
368 },
369 Err(error) => {
370 println!("System information cannot be retrieved: {error}");
371 }
372 }
373 }
374 }
375 "disks" => match disks {
376 Ok(disks) => {
377 for disk in disks {
378 println!("{disk:?}");
379 }
380 }
381 Err(error) => {
382 println!("Disks information cannot be retrieved: {error}");
383 }
384 },
385 "users" => match users {
386 Ok(users) => {
387 for user in users {
388 println!("{:?} => {:?}", user.name(), user.groups());
389 }
390 }
391 Err(error) => {
392 println!("Users information cannot be retrieved: {error}");
393 }
394 },
395 "groups" => match Groups::new_with_refreshed_list() {
396 Ok(groups) => {
397 for group in &groups {
398 println!("{group:?}");
399 }
400 }
401 Err(error) => {
402 println!("Groups information cannot be retrieved: {error}");
403 }
404 },
405 "boot_time" => match System::boot_time() {
406 Ok(boot_time) => println!("{boot_time} seconds"),
407 Err(error) => eprintln!("Failed to get `boot_time`: {error}"),
408 },
409 "uptime" => match System::uptime() {
410 Ok(up) => {
411 let mut uptime = up;
412 let days = uptime / 86400;
413 uptime -= days * 86400;
414 let hours = uptime / 3600;
415 uptime -= hours * 3600;
416 let minutes = uptime / 60;
417 println!("{days} days {hours} hours {minutes} minutes ({up} seconds in total)");
418 }
419 Err(error) => eprintln!("Failed to get `uptime`: {error}"),
420 },
421 x if x.starts_with("refresh") => {
422 if x == "refresh" {
423 match sys {
424 Ok(sys) => {
425 println!("Getting processes' information...");
426 sys.refresh_all();
427 println!("Done.");
428 }
429 Err(error) => {
430 println!("System information cannot be retrieved: {error}");
431 }
432 }
433 } else if x.starts_with("refresh ") {
434 println!("Getting process' information...");
435 if let Some(pid) = x
436 .split(' ')
437 .filter_map(|pid| pid.parse().ok())
438 .take(1)
439 .next()
440 {
441 match sys {
442 Ok(sys) => {
443 if sys.refresh_processes_specifics(
444 sysinfo::ProcessesToUpdate::Some(&[pid]),
445 true,
446 ProcessRefreshKind::everything(),
447 ) != 0
448 {
449 println!("Process `{pid}` updated successfully");
450 } else {
451 println!("Process `{pid}` couldn't be updated...");
452 }
453 }
454 Err(error) => {
455 println!("System information cannot be retrieved: {error}");
456 }
457 }
458 } else {
459 println!("Invalid [pid] received...");
460 }
461 } else {
462 println!(
463 "\"{x}\": Unknown command. Enter 'help' if you want to get the commands' \
464 list.",
465 );
466 }
467 }
468 "pid" => {
469 println!(
470 "PID: {}",
471 sysinfo::get_current_pid().expect("failed to get PID")
472 );
473 }
474 "system" => {
475 println!(
476 "System name: {}\n\
477 System kernel version: {}\n\
478 System OS version: {}\n\
479 System OS (long) version: {}\n\
480 System host name: {}\n\
481 System kernel: {}",
482 System::name().unwrap_or_else(|_| "<unknown>".to_owned()),
483 System::kernel_version().unwrap_or_else(|_| "<unknown>".to_owned()),
484 System::os_version().unwrap_or_else(|_| "<unknown>".to_owned()),
485 System::long_os_version().unwrap_or_else(|_| "<unknown>".to_owned()),
486 System::host_name().unwrap_or_else(|_| "<unknown>".to_owned()),
487 System::kernel_long_version().unwrap_or_else(|_| "<unknown>".to_owned()),
488 );
489 }
490 "motherboard" => match Motherboard::new() {
491 Ok(m) => println!("{m:#?}"),
492 Err(error) => println!("Cannot retrieve motherboard information: {error:?}"),
493 },
494 "product" => {
495 println!("{:#?}", Product);
496 }
497 e => {
498 println!(
499 "\"{e}\": Unknown command. Enter 'help' if you want to get the commands' \
500 list.",
501 );
502 }
503 }
504 false
505}
506
507fn main() {
508 println!("Getting system information...");
509 let mut system = System::new_all();
510 let mut networks = Networks::new_with_refreshed_list();
511 let mut disks = Disks::new_with_refreshed_list();
512 let mut components = Components::new_with_refreshed_list();
513 let mut users = Users::new_with_refreshed_list();
514 let mut gpus = Gpus::new();
515
516 println!("Done.");
517 let t_stin = io::stdin();
518 let mut stin = t_stin.lock();
519 let mut done = false;
520
521 println!("To get the commands' list, enter 'help'.");
522 while !done {
523 let mut input = String::new();
524 write!(&mut io::stdout(), "> ");
525 io::stdout().flush();
526
527 stin.read_line(&mut input);
528 if input.is_empty() {
529 println!("\nLeaving, bye!");
532 break;
533 }
534 if (&input as &str).ends_with('\n') {
535 input.pop();
536 }
537 done = interpret_input(
538 input.as_ref(),
539 system.as_mut(),
540 gpus.as_mut(),
541 networks.as_mut(),
542 disks.as_mut(),
543 components.as_mut(),
544 users.as_mut(),
545 );
546 }
547}