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