auto_cpufreq/
power_helper.rs1use crate::core::CODEBERG;
4use crate::tlp_stat_parser::TLPStatusParser;
5use anyhow::{Context, Result};
6use std::fs;
7use std::path::Path;
8use std::process::{Command, Stdio};
9
10pub fn does_command_exist(cmd: &str) -> bool {
11 Command::new("which")
12 .arg(cmd)
13 .stdout(Stdio::null())
14 .stderr(Stdio::null())
15 .status()
16 .map(|status| status.success())
17 .unwrap_or(false)
18}
19
20lazy_static::lazy_static! {
21 pub static ref BLUETOOTHCTL_EXISTS: bool = does_command_exist("bluetoothctl");
22 pub static ref POWERPROFILESCTL_EXISTS: bool = does_command_exist("powerprofilesctl");
23 pub static ref SYSTEMCTL_EXISTS: bool = does_command_exist("systemctl");
24 pub static ref TLP_STAT_EXISTS: bool = does_command_exist("tlp-stat");
25 pub static ref TUNED_STAT_EXISTS: bool = does_command_exist("tuned");
26}
27
28pub fn header() {
29 println!("\n------------------------- auto-cpufreq: Power helper -------------------------\n");
30}
31
32pub fn warning() {
33 println!("\n----------------------------------- Warning -----------------------------------\n");
34}
35
36pub fn footer() {
37 println!("\n{}\n", "-".repeat(79));
38}
39
40pub fn gnome_power_status() -> Result<bool> {
41 if !*SYSTEMCTL_EXISTS {
42 return Ok(false);
43 }
44
45 let status = Command::new("systemctl")
46 .args(["is-active", "--quiet", "power-profiles-daemon"])
47 .status()
48 .context("Failed to check GNOME power profiles daemon status")?;
49
50 Ok(status.success())
51}
52
53pub fn tlp_service_detect() -> Result<()> {
54 if !*TLP_STAT_EXISTS {
55 return Ok(());
56 }
57
58 let output = Command::new("tlp-stat")
59 .arg("-s")
60 .output()
61 .context("Failed to run tlp-stat")?;
62
63 let status_output = String::from_utf8_lossy(&output.stdout);
64 let tlp_status = TLPStatusParser::new(&status_output);
65
66 if tlp_status.is_enabled() {
67 warning();
68 println!("Detected you are running a TLP service!");
69 println!(
70 "This daemon might interfere with auto-cpufreq which can lead to unexpected results."
71 );
72 println!(
73 "We strongly encourage you to remove TLP unless you really know what you are doing."
74 );
75 }
76
77 Ok(())
78}
79
80pub fn gnome_power_detect() -> Result<()> {
81 if !*SYSTEMCTL_EXISTS {
82 return Ok(());
83 }
84
85 if gnome_power_status()? {
86 warning();
87 println!("Detected running GNOME Power Profiles daemon service!");
88 println!("\nThis daemon might interfere with auto-cpufreq and will be automatically");
89 println!("disabled when auto-cpufreq daemon is installed and");
90 println!("it will be re-enabled after auto-cpufreq is removed.");
91 println!("Steps to perform this action using auto-cpufreq: power_helper script:");
92 println!("git clone {}.git", CODEBERG);
93 println!("python3 -m auto_cpufreq.power_helper --gnome_power_disable");
94 println!("\nReference: {}#configuring-auto-cpufreq", CODEBERG);
95 }
96
97 Ok(())
98}
99
100pub fn gnome_power_detect_install() -> Result<()> {
101 if !*SYSTEMCTL_EXISTS {
102 return Ok(());
103 }
104
105 if gnome_power_status()? {
106 warning();
107 println!("Detected running GNOME Power Profiles daemon service!");
108 println!("\nThis daemon might interfere with auto-cpufreq and has been disabled.\n");
109 println!("This daemon is not automatically disabled in \"monitor\" mode and");
110 println!("will be enabled after auto-cpufreq daemon is removed.");
111 }
112
113 Ok(())
114}
115
116pub fn gnome_power_stop_live() -> Result<()> {
117 if !*SYSTEMCTL_EXISTS {
118 return Ok(());
119 }
120
121 if gnome_power_status()? && *POWERPROFILESCTL_EXISTS {
122 Command::new("powerprofilesctl")
123 .args(["set", "balanced"])
124 .status()?;
125
126 Command::new("systemctl")
127 .args(["stop", "power-profiles-daemon"])
128 .status()?;
129 }
130
131 Ok(())
132}
133
134pub fn tuned_stop_live() -> Result<()> {
135 if *SYSTEMCTL_EXISTS && *TUNED_STAT_EXISTS {
136 Command::new("systemctl").args(["stop", "tuned"]).status()?;
137 }
138
139 Ok(())
140}
141
142pub fn gnome_power_start_live() -> Result<()> {
143 if *SYSTEMCTL_EXISTS {
144 Command::new("systemctl")
145 .args(["start", "power-profiles-daemon"])
146 .status()?;
147 }
148
149 Ok(())
150}
151
152pub fn tuned_start_live() -> Result<()> {
153 if *SYSTEMCTL_EXISTS && *TUNED_STAT_EXISTS {
154 Command::new("systemctl")
155 .args(["start", "tuned"])
156 .status()?;
157 }
158
159 Ok(())
160}
161
162pub fn gnome_power_svc_enable() -> Result<()> {
163 if !*SYSTEMCTL_EXISTS {
164 return Ok(());
165 }
166
167 println!("* Enabling GNOME power profiles\n");
168
169 Command::new("systemctl")
170 .args(["unmask", "power-profiles-daemon"])
171 .status()
172 .context("Failed to unmask power-profiles-daemon")?;
173
174 Command::new("systemctl")
175 .args(["enable", "--now", "power-profiles-daemon"])
176 .status()
177 .context("Failed to enable power-profiles-daemon")?;
178
179 Ok(())
180}
181
182pub fn tuned_svc_enable() -> Result<()> {
183 if !*SYSTEMCTL_EXISTS || !*TUNED_STAT_EXISTS {
184 return Ok(());
185 }
186
187 println!("* Enabling TuneD\n");
188
189 Command::new("systemctl")
190 .args(["unmask", "tuned"])
191 .status()
192 .context("Failed to unmask tuned")?;
193
194 Command::new("systemctl")
195 .args(["enable", "--now", "tuned"])
196 .status()
197 .context("Failed to enable tuned")?;
198
199 Ok(())
200}
201
202pub fn gnome_power_svc_status() -> Result<()> {
203 if !*SYSTEMCTL_EXISTS {
204 return Ok(());
205 }
206
207 println!("* GNOME power profiles status");
208 Command::new("systemctl")
209 .args(["status", "power-profiles-daemon"])
210 .status()
211 .context("Failed to get GNOME power profiles status")?;
212
213 Ok(())
214}
215
216pub fn set_bluetooth_auto_enable(value: bool) -> Result<bool> {
217 let btconf = Path::new("/etc/bluetooth/main.conf");
218 let setting = format!("AutoEnable={}", if value { "true" } else { "false" });
219
220 let content = fs::read_to_string(btconf).context("Failed to read bluetooth config")?;
221
222 let lines: Vec<&str> = content.lines().collect();
223 let mut new_lines = Vec::new();
224 let mut in_policy_section = false;
225 let mut found_and_set = false;
226
227 for line in lines {
228 let stripped = line.trim();
229
230 if stripped.starts_with('[') {
231 if in_policy_section && !found_and_set {
232 new_lines.push(setting.clone());
233 found_and_set = true;
234 }
235 in_policy_section = stripped.to_lowercase() == "[policy]";
236 new_lines.push(line.to_string());
237 continue;
238 }
239
240 if in_policy_section {
241 if !stripped.starts_with('#') && stripped.starts_with("AutoEnable=") {
242 new_lines.push(setting.clone());
243 found_and_set = true;
244 continue;
245 }
246 if stripped.starts_with('#') {
247 let uncommented = stripped.trim_start_matches('#').trim();
248 if uncommented.starts_with("AutoEnable=") {
249 new_lines.push(setting.clone());
250 found_and_set = true;
251 continue;
252 }
253 }
254 }
255
256 new_lines.push(line.to_string());
257 }
258
259 if in_policy_section && !found_and_set {
260 new_lines.push(setting.clone());
261 found_and_set = true;
262 }
263
264 if !found_and_set {
265 new_lines.push(String::new());
266 new_lines.push("[Policy]".to_string());
267 new_lines.push(setting);
268 }
269
270 fs::write(btconf, new_lines.join("\n")).context("Failed to write bluetooth config")?;
271
272 Ok(true)
273}
274
275pub fn bluetooth_disable() -> Result<()> {
276 if !*BLUETOOTHCTL_EXISTS {
277 println!("* Turn off bluetooth on boot [skipping] (package providing bluetooth access is not present)");
278 return Ok(());
279 }
280
281 println!("* Turn off Bluetooth on boot (only)!");
282 println!(" If you want bluetooth enabled on boot run: auto-cpufreq --bluetooth_boot_on");
283
284 if !set_bluetooth_auto_enable(false)? {
285 println!("\nERROR:\nWas unable to turn off bluetooth on boot");
286 }
287
288 Ok(())
289}
290
291pub fn bluetooth_enable() -> Result<()> {
292 if !*BLUETOOTHCTL_EXISTS {
293 println!("* Turn on bluetooth on boot [skipping] (package providing bluetooth access is not present)");
294 return Ok(());
295 }
296
297 println!("* Turn on bluetooth on boot");
298
299 if !set_bluetooth_auto_enable(true)? {
300 println!("\nERROR:\nWas unable to turn on bluetooth on boot");
301 }
302
303 Ok(())
304}
305
306pub fn gnome_power_rm_reminder() -> Result<()> {
307 if !*SYSTEMCTL_EXISTS {
308 return Ok(());
309 }
310
311 if !gnome_power_status()? {
312 warning();
313 println!("Detected GNOME Power Profiles daemon service is stopped!");
314 println!("This service will now be enabled and started again.\n");
315 }
316
317 Ok(())
318}