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 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 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
137const 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}