referrerpolicy=no-referrer-when-downgrade

sc_sysinfo/
sysinfo_linux.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use regex::Regex;
20use sc_telemetry::SysInfo;
21use std::collections::HashSet;
22
23fn read_file(path: &str) -> Option<String> {
24	match std::fs::read_to_string(path) {
25		Ok(data) => Some(data),
26		Err(error) => {
27			log::warn!("Failed to read '{}': {}", path, error);
28			None
29		},
30	}
31}
32
33fn extract<T>(data: &str, regex: &str) -> Option<T>
34where
35	T: std::str::FromStr,
36{
37	Regex::new(regex)
38		.expect("regex is correct; qed")
39		.captures(&data)?
40		.get(1)?
41		.as_str()
42		.parse()
43		.ok()
44}
45
46const LINUX_REGEX_CPU: &str = r#"(?m)^model name\s*:\s*([^\n]+)"#;
47const LINUX_REGEX_PHYSICAL_ID: &str = r#"(?m)^physical id\s*:\s*(\d+)"#;
48const LINUX_REGEX_CORE_ID: &str = r#"(?m)^core id\s*:\s*(\d+)"#;
49const LINUX_REGEX_HYPERVISOR: &str = r#"(?m)^flags\s*:.+?\bhypervisor\b"#;
50const LINUX_REGEX_MEMORY: &str = r#"(?m)^MemTotal:\s*(\d+) kB"#;
51const LINUX_REGEX_DISTRO: &str = r#"(?m)^PRETTY_NAME\s*=\s*"?(.+?)"?$"#;
52
53pub fn gather_linux_sysinfo(sysinfo: &mut SysInfo) {
54	if let Some(data) = read_file("/proc/cpuinfo") {
55		sysinfo.cpu = extract(&data, LINUX_REGEX_CPU);
56		sysinfo.is_virtual_machine =
57			Some(Regex::new(LINUX_REGEX_HYPERVISOR).unwrap().is_match(&data));
58
59		// The /proc/cpuinfo returns a list of all of the hardware threads.
60		//
61		// Here we extract all of the unique {CPU ID, core ID} pairs to get
62		// the total number of cores.
63		let mut set: HashSet<(u32, u32)> = HashSet::new();
64		for chunk in data.split("\n\n") {
65			let pid = extract(chunk, LINUX_REGEX_PHYSICAL_ID);
66			let cid = extract(chunk, LINUX_REGEX_CORE_ID);
67			if let (Some(pid), Some(cid)) = (pid, cid) {
68				set.insert((pid, cid));
69			}
70		}
71
72		if !set.is_empty() {
73			sysinfo.core_count = Some(set.len() as u32);
74		}
75	}
76
77	if let Some(data) = read_file("/proc/meminfo") {
78		sysinfo.memory = extract(&data, LINUX_REGEX_MEMORY).map(|memory: u64| memory * 1024);
79	}
80
81	if let Some(data) = read_file("/etc/os-release") {
82		sysinfo.linux_distro = extract(&data, LINUX_REGEX_DISTRO);
83	}
84
85	// NOTE: We don't use the `nix` crate to call this since it doesn't
86	//       currently check for errors.
87	unsafe {
88		// SAFETY: The `utsname` is full of byte arrays, so this is safe.
89		let mut uname: libc::utsname = std::mem::zeroed();
90		if libc::uname(&mut uname) < 0 {
91			log::warn!("uname failed: {}", std::io::Error::last_os_error());
92		} else {
93			let length =
94				uname.release.iter().position(|&byte| byte == 0).unwrap_or(uname.release.len());
95			let release = std::slice::from_raw_parts(uname.release.as_ptr().cast(), length);
96			if let Ok(release) = std::str::from_utf8(release) {
97				sysinfo.linux_kernel = Some(release.into());
98			}
99		}
100	}
101}