Skip to main content

scx_pandemonium/cli/
check.rs

1use std::io::Read;
2use std::path::Path;
3use std::process::Command;
4
5use anyhow::Result;
6
7fn check_tool(name: &str) -> bool {
8    Command::new("which")
9        .arg(name)
10        .output()
11        .map(|o| o.status.success())
12        .unwrap_or(false)
13}
14
15fn check_kernel_version() -> bool {
16    let release = std::fs::read_to_string("/proc/sys/kernel/osrelease")
17        .unwrap_or_default()
18        .trim()
19        .to_string();
20    let parts: Vec<&str> = release.split('.').collect();
21    if parts.len() >= 2 {
22        if let (Ok(major), Ok(minor)) = (parts[0].parse::<u32>(), parts[1].parse::<u32>()) {
23            if major > 6 || (major == 6 && minor >= 12) {
24                log_info!("Kernel {} (>= 6.12)", release);
25                return true;
26            }
27            log_error!(
28                "Kernel {}.{} is too old. PANDEMONIUM requires 6.12+.",
29                major,
30                minor
31            );
32            log_error!("sched_ext (CONFIG_SCHED_CLASS_EXT) was merged in Linux 6.12.");
33            return false;
34        }
35    }
36    log_warn!("Cannot parse kernel version from '{}'", release);
37    false
38}
39
40fn check_vmlinux_cache() -> bool {
41    let cache = Path::new("/tmp/pandemonium-vmlinux.h");
42    if cache.exists() && cache.metadata().map(|m| m.len() > 1000).unwrap_or(false) {
43        let size = cache.metadata().map(|m| m.len() / 1024).unwrap_or(0);
44        log_info!("vmlinux.h cached ({size} KB)");
45        return true;
46    }
47    log_warn!("vmlinux.h not cached (will be downloaded on first build)");
48    true
49}
50
51fn check_kernel_config() -> bool {
52    let file = match std::fs::File::open("/proc/config.gz") {
53        Ok(f) => f,
54        Err(_) => {
55            log_warn!("/proc/config.gz not found (skipped)");
56            return true;
57        }
58    };
59    let mut decoder = flate2::read::GzDecoder::new(file);
60    let mut config = String::new();
61    if decoder.read_to_string(&mut config).is_err() {
62        log_warn!("/proc/config.gz unreadable (skipped)");
63        return true;
64    }
65    let found = config.contains("CONFIG_SCHED_CLASS_EXT=y");
66    if found {
67        log_info!("CONFIG_SCHED_CLASS_EXT=y found");
68    } else {
69        log_error!("CONFIG_SCHED_CLASS_EXT not found -- sched_ext may not be available");
70    }
71    found
72}
73
74pub fn run_check() -> Result<()> {
75    log_info!("PANDEMONIUM dependency check");
76
77    let mut ok = true;
78    let tools = ["cargo", "rustc", "clang", "sudo"];
79    for tool in &tools {
80        if check_tool(tool) {
81            log_info!("  {:<24}OK", tool);
82        } else {
83            log_error!("  {:<24}MISSING", tool);
84            ok = false;
85        }
86    }
87
88    log_info!("Kernel version:");
89    if !check_kernel_version() {
90        ok = false;
91    }
92
93    log_info!("Kernel config:");
94    if !check_kernel_config() {
95        ok = false;
96    }
97
98    log_info!("Build cache:");
99    check_vmlinux_cache();
100
101    let scx_path = Path::new("/sys/kernel/sched_ext/root/ops");
102    if scx_path.exists() {
103        let active = std::fs::read_to_string(scx_path).unwrap_or_default();
104        let active = active.trim();
105        if active.is_empty() {
106            log_info!("sched_ext available (no scheduler active)");
107        } else {
108            log_info!("sched_ext active ({})", active);
109        }
110    } else {
111        log_error!("sched_ext not available (sysfs path missing)");
112        ok = false;
113    }
114
115    if ok {
116        log_info!("All checks passed");
117    } else {
118        log_error!("Some checks failed");
119        if !check_tool("cargo") || !check_tool("rustc") {
120            log_info!("  Install Rust: https://rustup.rs");
121        }
122        if !check_tool("clang") {
123            log_info!("  Install clang: pacman -S clang");
124        }
125        std::process::exit(1);
126    }
127
128    Ok(())
129}