cyfs_util/util/
system_info.rs

1use serde::{Deserialize, Serialize};
2use std::sync::{Arc, Mutex};
3use std::time::{Duration, Instant};
4use sysinfo::{CpuExt, DiskExt, DiskType, NetworkExt, RefreshKind, System, SystemExt};
5
6#[derive(Clone, Debug, Serialize, Deserialize)]
7pub struct SystemInfo {
8    pub name: String,
9
10    // How long the system has been running since it was last booted, in microseconds
11    pub uptime: u64,
12
13    // The time the system was last booted, in bucky time
14    pub boot_time: u64,
15
16    pub cpu_usage: f32,
17
18    // memory size in bytes
19    pub total_memory: u64,
20    pub used_memory: u64,
21
22    // 每个刷新周期之间的传输的bytes
23    pub received_bytes: u64,
24    pub transmitted_bytes: u64,
25
26    // total bytes of all networks since last booted
27    pub total_received_bytes: u64,
28    pub total_transmitted_bytes: u64,
29
30    // SSD硬盘容量和可用容量,包括Unknown, in bytes
31    pub ssd_disk_total: u64,
32    pub ssd_disk_avail: u64,
33
34    // HDD硬盘容量和可用容量, in bytes
35    pub hdd_disk_total: u64,
36    pub hdd_disk_avail: u64,
37}
38
39impl Default for SystemInfo {
40    fn default() -> Self {
41        let sys = System::new();
42        let uptime = sys.uptime() * 1000 * 1000;
43        let boot_time = cyfs_base::unix_time_to_bucky_time(sys.boot_time() * 1000 * 1000);
44        
45        Self {
46            name: "".to_owned(),
47            uptime,
48            boot_time,
49            cpu_usage: 0.0,
50            total_memory: 0,
51            used_memory: 0,
52            received_bytes: 0,
53            transmitted_bytes: 0,
54            total_received_bytes: 0,
55            total_transmitted_bytes: 0,
56            ssd_disk_total: 0,
57            ssd_disk_avail: 0,
58            hdd_disk_total: 0,
59            hdd_disk_avail: 0,
60        }
61    }
62}
63
64struct SystemInfoManagerInner {
65    running: bool,
66    last_access_time: Instant,
67    max_idle_time: Duration,
68
69    info_inner: SystemInfo,
70    handler: System,
71}
72
73impl SystemInfoManagerInner {
74    pub fn new() -> Self {
75        let r = RefreshKind::new()
76            .with_networks()
77            .with_networks_list()
78            .with_memory()
79            .with_cpu(sysinfo::CpuRefreshKind::new().with_cpu_usage())
80            .with_disks()
81            .with_disks_list();
82        let handler = System::new_with_specifics(r);
83
84        let mut info_inner = SystemInfo::default();
85
86        let s = System::new();
87        info_inner.name = match s.host_name() {
88            Some(name) => {
89                let trim = '\0';
90                if name.ends_with(trim) {
91                    name[..name.len() - 1].to_owned()
92                } else {
93                    name
94                }
95            }
96            None => "MY PC".to_owned(),
97        };
98
99        info!("os name: {:?}", info_inner.name);
100
101        Self {
102            running: false,
103
104            last_access_time: Instant::now(),
105            max_idle_time: Duration::from_secs(15),
106
107            info_inner,
108            handler,
109        }
110    }
111
112    pub fn check_idle(&mut self) {
113        let now = Instant::now();
114        if now - self.last_access_time >= self.max_idle_time {
115            info!(
116                "system info extend max idle duration, now will stop: last_access={:?}",
117                self.last_access_time
118            );
119            self.running = false;
120        }
121    }
122
123    pub fn refresh(&mut self) {
124        self.handler.refresh_all();
125        self.update_memory();
126        self.update_cpu();
127        self.update_network();
128        self.update_disks();
129    }
130
131    fn update_memory(&mut self) {
132        self.info_inner.total_memory = self.handler.total_memory();
133        self.info_inner.used_memory = self.handler.used_memory();
134    }
135
136    fn update_disks(&mut self) {
137        self.info_inner.hdd_disk_total = 0;
138        self.info_inner.hdd_disk_avail = 0;
139        self.info_inner.ssd_disk_total = 0;
140        self.info_inner.ssd_disk_avail = 0;
141        for disk in self.handler.disks() {
142            if disk.is_removable() {
143                continue;
144            }
145            match disk.type_() {
146                DiskType::HDD => {
147                    self.info_inner.hdd_disk_total += disk.total_space();
148                    self.info_inner.hdd_disk_avail += disk.available_space();
149                }
150                DiskType::SSD => {
151                    self.info_inner.ssd_disk_total += disk.total_space();
152                    self.info_inner.ssd_disk_avail += disk.available_space();
153                }
154                // 在linux+docker环境下,每个docker container挂载的路径会被认作一个单独的磁盘,导致OODsystem info返回错误
155                // 这里先保证OOD的正确性,不把Unknown的磁盘认为是ssd
156                // 影响:在WSL1下运行的OOD,磁盘大小是0,移动端的协议栈,磁盘大小是0,这些原来都是Unknown的
157                DiskType::Unknown(_) => {
158                    // self.info_inner.ssd_disk_total += disk.total_space();
159                    // self.info_inner.ssd_disk_avail += disk.available_space();
160                }
161            }
162        }
163    }
164
165    fn update_cpu(&mut self) {
166        self.info_inner.cpu_usage = self.handler.global_cpu_info().cpu_usage();
167    }
168
169    fn update_network(&mut self) {
170        let networks = self.handler.networks();
171        let mut received_bytes = 0;
172        let mut transmitted_bytes = 0;
173        let mut total_received_bytes = 0;
174        let mut total_transmitted_bytes = 0;
175
176
177        for (interface_name, network) in networks {
178            if interface_name
179                .find("Hyper-V Virtual Ethernet Adapter")
180                .is_some()
181            {
182                //info!("will ignore as Hyper-V Virtual Ethernet Adapter addr: {}", description);
183                continue;
184            }
185
186            if interface_name.find("VMware").is_some() {
187                //info!("will ignore as VMware addr: {}", description);
188                continue;
189            }
190
191            if network.mac_address().is_unspecified() {
192                warn!("will ignore unspecified addr network interface: {}", interface_name);
193                continue;
194            }
195
196            // info!("in: {}, total_received_bytes={}, total_transmitted_bytes={}, addr={:?}", 
197            //    interface_name, network.total_received(), network.total_transmitted(), network.mac_address());
198
199            received_bytes += network.received();
200            transmitted_bytes += network.transmitted();
201
202            total_received_bytes += network.total_received();
203            total_transmitted_bytes += network.total_transmitted();
204        }
205
206        self.info_inner.received_bytes = received_bytes;
207        self.info_inner.transmitted_bytes = transmitted_bytes;
208
209        self.info_inner.total_received_bytes = total_received_bytes;
210        self.info_inner.total_transmitted_bytes = total_transmitted_bytes;
211    }
212}
213
214#[derive(Clone)]
215pub struct SystemInfoManager(Arc<Mutex<SystemInfoManagerInner>>);
216
217impl SystemInfoManager {
218    fn new() -> Self {
219        Self(Arc::new(Mutex::new(SystemInfoManagerInner::new())))
220    }
221
222    pub async fn get_system_info(&self) -> SystemInfo {
223        if !self.0.lock().unwrap().running {
224            self.start();
225            async_std::task::sleep(Duration::from_secs(2)).await;
226        }
227
228        let mut item = self.0.lock().unwrap();
229        item.last_access_time = Instant::now();
230        item.info_inner.clone()
231    }
232
233    pub fn start(&self) {
234        let start = {
235            let mut item = self.0.lock().unwrap();
236            if !item.running {
237                item.running = true;
238                true
239            } else {
240                false
241            }
242        };
243
244        if !start {
245            info!("system info already in refreshing!");
246            return;
247        }
248
249        info!("start refresh system info...");
250
251        let this = self.clone();
252        async_std::task::spawn(async move { this.run_refresh().await });
253    }
254
255    async fn run_refresh(&self) {
256        loop {
257            {
258                let mut item = self.0.lock().unwrap();
259                item.check_idle();
260                if !item.running {
261                    break;
262                }
263
264                item.refresh();
265            }
266
267            async_std::task::sleep(std::time::Duration::from_secs(1)).await;
268        }
269    }
270
271    pub fn stop(&self) {
272        let mut item = self.0.lock().unwrap();
273        if item.running {
274            item.running = false;
275            info!("will stop refresh system info!");
276        } else {
277            info!("refresh system info stopped already!");
278        }
279    }
280}
281
282// 这里使用全局单例模式,避免runtime和non-stack不同的接口分别持有两个实例
283lazy_static::lazy_static! {
284    pub static ref SYSTEM_INFO_MANAGER: SystemInfoManager = SystemInfoManager::new();
285}