batuta_common/sys.rs
1//! System information utilities.
2//!
3//! Provides cross-platform detection for cgroups, CPU info, and other
4//! system-level queries used across the Batuta stack.
5
6/// Check if cgroup v1 or v2 CPU controllers are available.
7///
8/// Returns `true` on Linux when either `/sys/fs/cgroup/cpu` (v1) or
9/// `/sys/fs/cgroup/unified` (v2) exists. Always returns `false` on non-Linux.
10///
11/// # Examples
12/// ```
13/// use batuta_common::sys::is_cgroup_available;
14/// // Returns true on Linux with cgroup support
15/// let _available = is_cgroup_available();
16/// ```
17#[must_use]
18pub fn is_cgroup_available() -> bool {
19 #[cfg(target_os = "linux")]
20 {
21 std::path::Path::new("/sys/fs/cgroup/cpu").exists()
22 || std::path::Path::new("/sys/fs/cgroup/unified").exists()
23 }
24
25 #[cfg(not(target_os = "linux"))]
26 {
27 false
28 }
29}
30
31/// Get CPU model name (best effort).
32///
33/// On Linux, reads `/proc/cpuinfo` for the "model name" field.
34/// Returns `"Unknown CPU"` on other platforms or if parsing fails.
35///
36/// # Examples
37/// ```
38/// use batuta_common::sys::get_cpu_info;
39/// let info = get_cpu_info();
40/// assert!(!info.is_empty());
41/// ```
42#[must_use]
43pub fn get_cpu_info() -> String {
44 #[cfg(target_os = "linux")]
45 {
46 if let Ok(content) = std::fs::read_to_string("/proc/cpuinfo") {
47 for line in content.lines() {
48 if line.starts_with("model name")
49 && let Some(name) = line.split(':').nth(1)
50 {
51 return name.trim().to_string();
52 }
53 }
54 }
55 }
56 "Unknown CPU".to_string()
57}
58
59/// Get the number of available CPU cores.
60///
61/// Uses `std::thread::available_parallelism()`, falling back to 1.
62#[must_use]
63pub fn cpu_count() -> usize {
64 std::thread::available_parallelism()
65 .map(std::num::NonZero::get)
66 .unwrap_or(1)
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn test_is_cgroup_available_returns_bool() {
75 // Just verify it doesn't panic
76 let _ = is_cgroup_available();
77 }
78
79 #[test]
80 fn test_get_cpu_info_non_empty() {
81 let info = get_cpu_info();
82 assert!(!info.is_empty());
83 }
84
85 #[test]
86 fn test_cpu_count_positive() {
87 assert!(cpu_count() >= 1);
88 }
89
90 #[cfg(target_os = "linux")]
91 #[test]
92 fn test_cgroup_on_linux() {
93 // On Linux, cgroup should typically be available
94 // but we don't assert true since containers may not have it
95 let _ = is_cgroup_available();
96 }
97}