Skip to main content

flowey_lib_common/
system_info.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Print information about the current system to the log
5
6use flowey::node::prelude::*;
7use std::collections::BTreeMap;
8
9new_simple_flow_node!(struct Node);
10
11flowey_request! {
12    pub struct Request {
13        pub done: WriteVar<SideEffect>,
14    }
15}
16
17impl SimpleFlowNode for Node {
18    type Request = Request;
19
20    fn imports(_dep: &mut ImportCtx<'_>) {
21        // no deps
22    }
23
24    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
25        ctx.emit_rust_step("print system info", |ctx| {
26            request.done.claim(ctx);
27            |rt| {
28                print_system_info(rt);
29                Ok(())
30            }
31        });
32
33        Ok(())
34    }
35}
36
37fn bytes_to_gibibytes(bytes: u64) -> f64 {
38    bytes as f64 / (1024 * 1024 * 1024) as f64
39}
40
41fn print_system_info(rt: &mut RustRuntimeServices<'_>) {
42    use sysinfo::{Disks, Networks, System};
43    let sys = System::new_all();
44
45    log::info!(
46        "Memory: {:.1} GB / {:.1} GB",
47        bytes_to_gibibytes(sys.used_memory()),
48        bytes_to_gibibytes(sys.total_memory())
49    );
50
51    let cpu_list = sys
52        .cpus()
53        .iter()
54        .map(|cpu| (cpu.vendor_id(), cpu.brand(), cpu.frequency()))
55        .collect::<Vec<_>>();
56
57    let mut cpus = BTreeMap::new();
58    for key in cpu_list {
59        let count = cpus.entry(key).or_insert(0u64);
60        *count += 1;
61    }
62
63    for ((vendor, brand, freq), count) in cpus {
64        log::info!("CPU: {vendor} [{brand}] @ {freq} MHz × {count}");
65    }
66
67    let os_info = [
68        System::name(),
69        System::os_version(),
70        System::kernel_version(),
71    ]
72    .into_iter()
73    .flatten()
74    .collect::<Vec<_>>()
75    .join(" ");
76    log::info!("OS: {}", os_info);
77    log::info!("Hostname: {}", System::host_name().unwrap_or_default());
78
79    let disks = Disks::new_with_refreshed_list();
80    for disk in &disks {
81        let used_space = disk.total_space() - disk.available_space();
82        log::info!(
83            "Disk: {} {:.1} GB / {:.1} GB",
84            disk.mount_point().display(),
85            bytes_to_gibibytes(used_space),
86            bytes_to_gibibytes(disk.total_space())
87        );
88    }
89
90    let networks = Networks::new_with_refreshed_list();
91    for (interface_name, data) in &networks {
92        let ip_addresses = data
93            .ip_networks()
94            .iter()
95            .map(|ip| ip.to_string())
96            .collect::<Vec<_>>()
97            .join(" ");
98        log::info!("Network: {interface_name} {ip_addresses}");
99    }
100
101    let is_uefi = match rt.platform() {
102        FlowPlatform::Windows => std::process::Command::new("bcdedit")
103            .output()
104            .ok()
105            .and_then(|o| o.status.success().then(|| String::from_utf8(o.stdout).ok()))
106            .flatten()
107            .map(|o| o.to_lowercase().contains(".efi")),
108        FlowPlatform::Linux(_) => Path::new("/sys/firmware/efi").try_exists().ok(),
109        _ => None,
110    };
111
112    match is_uefi {
113        Some(true) => log::info!("Using UEFI firmware"),
114        Some(false) => log::info!("Not using UEFI firmware"),
115        None => log::info!("Unknown firmware"),
116    }
117}