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