scxctl/
main.rs

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