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