Skip to main content

simple/
simple.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3#![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                    // Note: you should refresh a few times before using this, so that usage statistics
144                    // can be ascertained
145                    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            // The string is empty, meaning there is no '\n', meaning
530            // that the user used CTRL+D so we can just quit!
531            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}