scxctl/
main.rs

1mod cli;
2
3use clap::Parser;
4use cli::{Cli, Commands};
5use colored::Colorize;
6use scx_loader::{dbus::LoaderClientProxyBlocking, SchedMode, SupportedSched};
7use std::process::exit;
8use zbus::blocking::Connection;
9
10fn cmd_get(scx_loader: LoaderClientProxyBlocking) -> Result<(), Box<dyn std::error::Error>> {
11    let current_scheduler: String = scx_loader.current_scheduler().unwrap();
12    let sched_mode: SchedMode = scx_loader.scheduler_mode().unwrap();
13    match current_scheduler.as_str() {
14        "unknown" => println!("no scx scheduler running"),
15        _ => {
16            let sched = SupportedSched::try_from(current_scheduler.as_str()).unwrap();
17            println!("running {sched:?} in {sched_mode:?} mode");
18        }
19    }
20    Ok(())
21}
22
23fn cmd_list(scx_loader: LoaderClientProxyBlocking) -> Result<(), Box<dyn std::error::Error>> {
24    match scx_loader.supported_schedulers() {
25        Ok(sl) => {
26            let supported_scheds = sl
27                .iter()
28                .map(|s| remove_scx_prefix(&s.to_string()))
29                .collect::<Vec<String>>();
30            println!("supported schedulers: {:?}", supported_scheds);
31            return Ok(());
32        }
33        Err(e) => {
34            eprintln!("scheduler list failed: {e}");
35            exit(1);
36        }
37    };
38}
39
40fn cmd_start(
41    scx_loader: LoaderClientProxyBlocking,
42    sched_name: String,
43    mode_name: Option<SchedMode>,
44    args: Option<Vec<String>>,
45) -> Result<(), Box<dyn std::error::Error>> {
46    // Verify scx_loader is not running a scheduler
47    if scx_loader.current_scheduler().unwrap() != "unknown" {
48        println!(
49            "{} scx scheduler already running, use '{}' instead of '{}'",
50            "error:".red().bold(),
51            "switch".bold(),
52            "start".bold()
53        );
54        println!("\nFor more information, try '{}'", "--help".bold());
55        exit(1);
56    }
57
58    let sched: SupportedSched = validate_sched(scx_loader.clone(), sched_name);
59    let mode: SchedMode = mode_name.unwrap_or(SchedMode::Auto);
60    match args {
61        Some(args) => {
62            scx_loader.start_scheduler_with_args(sched.clone(), &args.clone())?;
63            println!("started {sched:?} with arguments \"{}\"", args.join(" "));
64        }
65        None => {
66            scx_loader.start_scheduler(sched.clone(), mode.clone())?;
67            println!("started {sched:?} in {mode:?} mode");
68        }
69    }
70    Ok(())
71}
72
73fn cmd_switch(
74    scx_loader: LoaderClientProxyBlocking,
75    sched_name: Option<String>,
76    mode_name: Option<SchedMode>,
77    args: Option<Vec<String>>,
78) -> Result<(), Box<dyn std::error::Error>> {
79    // Verify scx_loader is running a scheduler
80    if scx_loader.current_scheduler().unwrap() == "unknown" {
81        println!(
82            "{} no scx scheduler running, use '{}' instead of '{}'",
83            "error:".red().bold(),
84            "start".bold(),
85            "switch".bold()
86        );
87        println!("\nFor more information, try '{}'", "--help".bold());
88        exit(1);
89    }
90
91    let sched: SupportedSched = match sched_name {
92        Some(sched_name) => validate_sched(scx_loader.clone(), sched_name),
93        None => SupportedSched::try_from(scx_loader.current_scheduler().unwrap().as_str()).unwrap(),
94    };
95    let mode: SchedMode = match mode_name {
96        Some(mode_name) => mode_name,
97        None => scx_loader.scheduler_mode().unwrap(),
98    };
99    match args {
100        Some(args) => {
101            scx_loader.switch_scheduler_with_args(sched.clone(), &args.clone())?;
102            println!(
103                "switched to {sched:?} with arguments \"{}\"",
104                args.join(" ")
105            );
106        }
107        None => {
108            scx_loader.switch_scheduler(sched.clone(), mode.clone())?;
109            println!("switched to {sched:?} in {mode:?} mode");
110        }
111    }
112    Ok(())
113}
114
115fn cmd_stop(scx_loader: LoaderClientProxyBlocking) -> Result<(), Box<dyn std::error::Error>> {
116    scx_loader.stop_scheduler()?;
117    println!("stopped");
118    Ok(())
119}
120
121fn main() -> Result<(), Box<dyn std::error::Error>> {
122    let cli = Cli::parse();
123    let conn = Connection::system()?;
124    let scx_loader = LoaderClientProxyBlocking::new(&conn)?;
125
126    match cli.command {
127        Commands::Get => cmd_get(scx_loader)?,
128        Commands::List => cmd_list(scx_loader)?,
129        Commands::Start { args } => cmd_start(scx_loader, args.sched, args.mode, args.args)?,
130        Commands::Switch { args } => cmd_switch(scx_loader, args.sched, args.mode, args.args)?,
131        Commands::Stop => cmd_stop(scx_loader)?,
132    }
133
134    Ok(())
135}
136
137/*
138 * Utilities
139 */
140
141const SCHED_PREFIX: &str = "scx_";
142
143fn ensure_scx_prefix(input: String) -> String {
144    if !input.starts_with(SCHED_PREFIX) {
145        return format!("{}{}", SCHED_PREFIX, input);
146    }
147    input
148}
149
150fn remove_scx_prefix(input: &String) -> String {
151    if let Some(strip_input) = input.strip_prefix(SCHED_PREFIX) {
152        return strip_input.to_string();
153    }
154    input.to_string()
155}
156
157fn validate_sched(scx_loader: LoaderClientProxyBlocking, sched: String) -> SupportedSched {
158    let raw_supported_scheds: Vec<String> = scx_loader.supported_schedulers().unwrap();
159    let supported_scheds: Vec<String> = raw_supported_scheds
160        .iter()
161        .map(|s| remove_scx_prefix(s))
162        .collect();
163    if !supported_scheds.contains(&sched) && !raw_supported_scheds.contains(&sched) {
164        println!(
165            "{} invalid value '{}' for '{}'",
166            "error:".red().bold(),
167            &sched.yellow(),
168            "--sched <SCHED>".bold()
169        );
170        println!("supported schedulers: {:?}", supported_scheds);
171        println!("\nFor more information, try '{}'", "--help".bold());
172        exit(1);
173    }
174
175    SupportedSched::try_from(ensure_scx_prefix(sched).as_str()).unwrap()
176}