diff --git a/CHANGELOG.md b/CHANGELOG.md index 03bdc2da..8eca4a56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Better handling of `/etc/asusd` not existing - Better handling of non-existant config files +- Move all config handling to generic traits for better consistency +- Re-parse all configs to RON format +- Move fan-curve config to own config file - Added option to set `disable_nvidia_powerd_on_battery` - Add short log entry to throttle_thermal_policy change detection - ROGCC: Don't notify user if changing to same mux mode diff --git a/Cargo.lock b/Cargo.lock index 969dc3d2..ba35b021 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -790,6 +790,7 @@ dependencies = [ "rog_dbus", "rog_platform", "rog_profiles", + "ron", "serde", "serde_derive", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index a50fd449..42218fcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ serde = "^1.0" serde_derive = "^1.0" serde_json = "^1.0" toml = "^0.5.10" +ron = "*" log = "^0.4" env_logger = "^0.10.0" diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 476d9f6a..13f02d68 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -39,6 +39,7 @@ serde.workspace = true serde_derive.workspace = true serde_json.workspace = true toml.workspace = true +ron.workspace = true # Device control sysfs-class.workspace = true # used for backlight control and baord ID diff --git a/daemon/src/config.rs b/daemon/src/config.rs index f57786a0..70c02bd3 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -1,12 +1,8 @@ -use std::fs::{File, OpenOptions}; -use std::io::{Read, Write}; - -use log::{error, warn}; use serde_derive::{Deserialize, Serialize}; -use crate::config_file_open; +use crate::config_traits::{StdConfig, StdConfigLoad3}; -pub static CONFIG_FILE: &str = "/etc/asusd/asusd.conf"; +const CONFIG_FILE: &str = "asusd.conf"; #[derive(Deserialize, Serialize, Default)] pub struct Config { @@ -18,7 +14,7 @@ pub struct Config { pub bat_command: String, } -impl Config { +impl StdConfig for Config { fn new() -> Self { Config { bat_charge_limit: 100, @@ -29,65 +25,13 @@ impl Config { } } - /// `load` will attempt to read the config, and panic if the dir is missing - pub fn load() -> Self { - let mut file = config_file_open(CONFIG_FILE); - let mut buf = String::new(); - let config; - if let Ok(read_len) = file.read_to_string(&mut buf) { - if read_len == 0 { - config = Self::new(); - } else if let Ok(data) = serde_json::from_str(&buf) { - config = data; - } else if let Ok(data) = serde_json::from_str::(&buf) { - config = data.into(); - } else if let Ok(data) = serde_json::from_str::(&buf) { - config = data.into(); - } else { - warn!( - "Could not deserialise {}.\nWill rename to {}-old and recreate config", - CONFIG_FILE, CONFIG_FILE - ); - let cfg_old = CONFIG_FILE.to_owned() + "-old"; - std::fs::rename(CONFIG_FILE, cfg_old).unwrap_or_else(|err| { - panic!( - "Could not rename. Please remove {} then restart service: Error {}", - CONFIG_FILE, err - ) - }); - config = Self::new(); - } - } else { - config = Self::new(); - } - config.write(); - config - } - - pub fn read(&mut self) { - let mut file = OpenOptions::new() - .read(true) - .open(CONFIG_FILE) - .unwrap_or_else(|err| panic!("Error reading {}: {}", CONFIG_FILE, err)); - let mut buf = String::new(); - if let Ok(l) = file.read_to_string(&mut buf) { - if l == 0 { - warn!("File is empty {}", CONFIG_FILE); - } else { - *self = serde_json::from_str(&buf) - .unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_FILE)); - } - } - } - - pub fn write(&self) { - let mut file = File::create(CONFIG_FILE).expect("Couldn't overwrite config"); - let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); - file.write_all(json.as_bytes()) - .unwrap_or_else(|err| error!("Could not write config: {}", err)); + fn file_name() -> &'static str { + CONFIG_FILE } } +impl StdConfigLoad3 for Config {} + #[derive(Deserialize, Serialize, Default)] #[serde(default)] pub struct Config455 { diff --git a/daemon/src/config_traits.rs b/daemon/src/config_traits.rs new file mode 100644 index 00000000..fad109d7 --- /dev/null +++ b/daemon/src/config_traits.rs @@ -0,0 +1,191 @@ +use std::fs::{create_dir, File, OpenOptions}; +use std::io::{Read, Write}; +use std::path::PathBuf; + +use log::{error, warn}; +use ron::ser::PrettyConfig; +use serde::de::DeserializeOwned; +use serde::Serialize; + +const CONFIG_PATH_BASE: &str = "/etc/asusd/"; + +/// Config file helper traits. Only `new()` and `file_name()` are required to be +/// implemented, the rest are intended to be free methods. +pub trait StdConfig +where + Self: Serialize + DeserializeOwned, +{ + fn new() -> Self; + + fn file_name() -> &'static str; + + fn file_path() -> PathBuf { + let mut config = PathBuf::from(CONFIG_PATH_BASE); + if !config.exists() { + create_dir(config.as_path()) + .unwrap_or_else(|e| panic!("Could not create {CONFIG_PATH_BASE} {e}")); + } + config.push(Self::file_name()); + config + } + + fn file_open() -> File { + OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(Self::file_path()) + .unwrap_or_else(|e| panic!("Could not open {:?} {e}", Self::file_path())) + } + + fn read(&mut self) { + let mut file = match OpenOptions::new().read(true).open(Self::file_path()) { + Ok(data) => data, + Err(err) => { + error!("Error reading {:?}: {}", Self::file_path(), err); + return; + } + }; + let mut buf = String::new(); + if let Ok(l) = file.read_to_string(&mut buf) { + if l == 0 { + warn!("File is empty {:?}", Self::file_path()); + } else if let Ok(data) = ron::from_str(&buf) { + *self = data; + } else if let Ok(data) = serde_json::from_str(&buf) { + *self = data; + } else { + warn!("Could not deserialise {:?}", Self::file_path()); + } + } + } + + fn write(&self) { + let mut file = match File::create(Self::file_path()) { + Ok(data) => data, + Err(e) => { + error!( + "Couldn't overwrite config {:?}, error: {e}", + Self::file_path() + ); + return; + } + }; + let ron = match ron::ser::to_string_pretty(&self, PrettyConfig::new().depth_limit(4)) { + Ok(data) => data, + Err(e) => { + error!("Parse {:?} to RON failed, error: {e}", Self::file_path()); + return; + } + }; + file.write_all(ron.as_bytes()) + .unwrap_or_else(|err| error!("Could not write config: {}", err)); + } + + /// Renames the existing file to `-old` + fn rename_file_old() { + warn!( + "Renaming {} to {}-old and recreating config", + Self::file_name(), + Self::file_name() + ); + let cfg_old = Self::file_path().to_string_lossy().to_string() + "-old"; + std::fs::rename(Self::file_path(), cfg_old).unwrap_or_else(|err| { + error!( + "Could not rename. Please remove {} then restart service: Error {}", + Self::file_name(), + err + ) + }); + } +} + +pub trait StdConfigLoad1 +where + T: StdConfig + DeserializeOwned + Serialize, +{ + fn load() -> T { + let mut file = T::file_open(); + let mut buf = String::new(); + let config: T; + if let Ok(read_len) = file.read_to_string(&mut buf) { + if read_len == 0 { + config = T::new(); + } else if let Ok(data) = ron::from_str(&buf) { + config = data; + } else if let Ok(data) = serde_json::from_str(&buf) { + config = data; + } else { + T::rename_file_old(); + config = T::new(); + } + } else { + config = T::new(); + } + config.write(); + config + } +} + +pub trait StdConfigLoad2 +where + T1: StdConfig + DeserializeOwned + Serialize, + T2: DeserializeOwned + Into, +{ + fn load() -> T1 { + let mut file = T1::file_open(); + let mut buf = String::new(); + let config: T1; + if let Ok(read_len) = file.read_to_string(&mut buf) { + if read_len == 0 { + config = T1::new(); + } else if let Ok(data) = ron::from_str(&buf) { + config = data; + } else if let Ok(data) = serde_json::from_str(&buf) { + config = data; + } else if let Ok(data) = serde_json::from_str::(&buf) { + config = data.into(); + } else { + T1::rename_file_old(); + config = T1::new(); + } + } else { + config = T1::new(); + } + config.write(); + config + } +} + +pub trait StdConfigLoad3 +where + T1: StdConfig + DeserializeOwned + Serialize, + T2: DeserializeOwned + Into, + T3: DeserializeOwned + Into, +{ + fn load() -> T1 { + let mut file = T1::file_open(); + let mut buf = String::new(); + let config: T1; + if let Ok(read_len) = file.read_to_string(&mut buf) { + if read_len == 0 { + config = T1::new(); + } else if let Ok(data) = ron::from_str(&buf) { + config = data; + } else if let Ok(data) = serde_json::from_str(&buf) { + config = data; + } else if let Ok(data) = serde_json::from_str::(&buf) { + config = data.into(); + } else if let Ok(data) = serde_json::from_str::(&buf) { + config = data.into(); + } else { + T1::rename_file_old(); + config = T1::new(); + } + } else { + config = T1::new(); + } + config.write(); + config + } +} diff --git a/daemon/src/ctrl_anime/config.rs b/daemon/src/ctrl_anime/config.rs index f940cce6..3d03fdd3 100644 --- a/daemon/src/ctrl_anime/config.rs +++ b/daemon/src/ctrl_anime/config.rs @@ -1,16 +1,12 @@ -use std::fs::{File, OpenOptions}; -use std::io::{Read, Write}; use std::time::Duration; -use log::{error, info, warn}; use rog_anime::error::AnimeError; use rog_anime::{ActionData, ActionLoader, AnimTime, AnimeType, Fade, Vec2}; use serde_derive::{Deserialize, Serialize}; -use crate::{config_file_open, VERSION}; +use crate::config_traits::{StdConfig, StdConfigLoad3}; -pub static CONFIG_FILE: &str = "anime.conf"; -pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf"; +const CONFIG_FILE: &str = "anime.conf"; #[derive(Deserialize, Serialize)] pub struct AnimeConfigV341 { @@ -20,25 +16,25 @@ pub struct AnimeConfigV341 { pub shutdown: Option, } -impl AnimeConfigV341 { - pub(crate) fn into_current(self) -> AnimeConfig { +impl From for AnimeConfig { + fn from(c: AnimeConfigV341) -> AnimeConfig { AnimeConfig { - system: if let Some(ani) = self.system { + system: if let Some(ani) = c.system { vec![ani] } else { vec![] }, - boot: if let Some(ani) = self.boot { + boot: if let Some(ani) = c.boot { vec![ani] } else { vec![] }, - wake: if let Some(ani) = self.suspend { + wake: if let Some(ani) = c.suspend { vec![ani] } else { vec![] }, - shutdown: if let Some(ani) = self.shutdown { + shutdown: if let Some(ani) = c.shutdown { vec![ani] } else { vec![] @@ -59,13 +55,13 @@ pub struct AnimeConfigV352 { pub brightness: f32, } -impl AnimeConfigV352 { - pub(crate) fn into_current(self) -> AnimeConfig { +impl From for AnimeConfig { + fn from(c: AnimeConfigV352) -> AnimeConfig { AnimeConfig { - system: self.system, - boot: self.boot, - wake: self.wake, - shutdown: self.shutdown, + system: c.system, + boot: c.boot, + wake: c.wake, + shutdown: c.shutdown, brightness: 1.0, awake_enabled: true, boot_anim_enabled: true, @@ -140,58 +136,30 @@ impl Default for AnimeConfig { } } +impl StdConfig for AnimeConfig { + fn new() -> Self { + Self::create_default() + } + + fn file_name() -> &'static str { + CONFIG_FILE + } +} + +impl StdConfigLoad3 for AnimeConfig {} + impl AnimeConfig { - /// `load` will attempt to read the config, and panic if the dir is missing - pub fn load() -> Self { - let mut file = config_file_open(CONFIG_FILE); - let mut buf = String::new(); - if let Ok(read_len) = file.read_to_string(&mut buf) { - if read_len == 0 { - return AnimeConfig::create_default(&mut file); - } else { - if let Ok(mut data) = serde_json::from_str(&buf) { - Self::clamp_config_brightness(&mut data); - return data; - } else if let Ok(data) = serde_json::from_str::(&buf) { - let mut config = data.into_current(); - config.write(); - info!("Updated config version to: {}", VERSION); - Self::clamp_config_brightness(&mut config); - return config; - } else if let Ok(data) = serde_json::from_str::(&buf) { - let mut config = data.into_current(); - config.write(); - info!("Updated config version to: {}", VERSION); - Self::clamp_config_brightness(&mut config); - return config; - } - warn!( - "Could not deserialise {}.\nWill rename to {}-old and recreate config", - CONFIG_FILE, CONFIG_FILE - ); - let cfg_old = CONFIG_FILE.to_string() + "-old"; - std::fs::rename(CONFIG_FILE, cfg_old).unwrap_or_else(|err| { - panic!( - "Could not rename. Please remove {} then restart service: Error {}", - CONFIG_FILE, err - ) - }); - } - } - AnimeConfig::create_default(&mut file) - } + // fn clamp_config_brightness(mut config: &mut AnimeConfig) { + // if config.brightness < 0.0 || config.brightness > 1.0 { + // warn!( + // "Clamped brightness to [0.0 ; 1.0], was {}", + // config.brightness + // ); + // config.brightness = f32::max(0.0, f32::min(1.0, config.brightness)); + // } + // } - fn clamp_config_brightness(mut config: &mut AnimeConfig) { - if config.brightness < 0.0 || config.brightness > 1.0 { - warn!( - "Clamped brightness to [0.0 ; 1.0], was {}", - config.brightness - ); - config.brightness = f32::max(0.0, f32::min(1.0, config.brightness)); - } - } - - fn create_default(file: &mut File) -> Self { + fn create_default() -> Self { // create a default config here let config = AnimeConfig { system: vec![], @@ -231,34 +199,7 @@ impl AnimeConfig { awake_enabled: true, boot_anim_enabled: true, }; - // Should be okay to unwrap this as is since it is a Default - let json = serde_json::to_string_pretty(&config).unwrap(); - file.write_all(json.as_bytes()) - .unwrap_or_else(|_| panic!("Could not write {}", CONFIG_FILE)); + config.write(); config } - - pub fn read(&mut self) { - let mut file = OpenOptions::new() - .read(true) - .open(CONFIG_FILE) - .unwrap_or_else(|err| panic!("Error reading {}: {}", CONFIG_FILE, err)); - let mut buf = String::new(); - if let Ok(l) = file.read_to_string(&mut buf) { - if l == 0 { - warn!("File is empty {}", CONFIG_FILE); - } else { - let x: AnimeConfig = serde_json::from_str(&buf) - .unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_FILE)); - *self = x; - } - } - } - - pub fn write(&self) { - let mut file = File::create(CONFIG_FILE).expect("Couldn't overwrite config"); - let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); - file.write_all(json.as_bytes()) - .unwrap_or_else(|err| error!("Could not write config: {}", err)); - } } diff --git a/daemon/src/ctrl_anime/trait_impls.rs b/daemon/src/ctrl_anime/trait_impls.rs index 0da684c8..f43ace2b 100644 --- a/daemon/src/ctrl_anime/trait_impls.rs +++ b/daemon/src/ctrl_anime/trait_impls.rs @@ -9,6 +9,7 @@ use zbus::export::futures_util::lock::{Mutex, MutexGuard}; use zbus::{dbus_interface, Connection, SignalContext}; use super::CtrlAnime; +use crate::config_traits::StdConfig; use crate::error::RogError; pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Anime"; diff --git a/daemon/src/ctrl_aura/config.rs b/daemon/src/ctrl_aura/config.rs index f315e2c9..453317ea 100644 --- a/daemon/src/ctrl_aura/config.rs +++ b/daemon/src/ctrl_aura/config.rs @@ -1,8 +1,5 @@ use std::collections::{BTreeMap, HashSet}; -use std::fs::File; -use std::io::{Read, Write}; -use log::{error, warn}; use rog_aura::aura_detection::{LaptopLedData, ASUS_KEYBOARD_DEVICES}; use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev}; use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT}; @@ -10,9 +7,9 @@ use rog_platform::hid_raw::HidRaw; use rog_platform::keyboard_led::KeyboardLed; use serde_derive::{Deserialize, Serialize}; -use crate::config_file_open; +use crate::config_traits::{StdConfig, StdConfigLoad1}; -static CONFIG_FILE: &str = "aura.conf"; +const CONFIG_FILE: &str = "aura.conf"; /// Enable/disable LED control in various states such as /// when the device is awake, suspended, shutting down or @@ -189,35 +186,20 @@ impl Default for AuraConfig { } } -impl AuraConfig { - /// `load` will attempt to read the config, and panic if the dir is missing - pub fn load(supported_led_modes: &LaptopLedData) -> Self { - let mut file = config_file_open(CONFIG_FILE); - let mut buf = String::new(); - if let Ok(read_len) = file.read_to_string(&mut buf) { - if read_len == 0 { - return AuraConfig::create_default(&mut file, supported_led_modes); - } else { - if let Ok(data) = serde_json::from_str(&buf) { - return data; - } - warn!( - "Could not deserialise {}.\nWill rename to {}-old and recreate config", - CONFIG_FILE, CONFIG_FILE - ); - let cfg_old = CONFIG_FILE.to_string() + "-old"; - std::fs::rename(CONFIG_FILE, cfg_old).unwrap_or_else(|err| { - panic!( - "Could not rename. Please remove {} then restart service: Error {}", - CONFIG_FILE, err - ) - }); - } - } - AuraConfig::create_default(&mut file, supported_led_modes) +impl StdConfig for AuraConfig { + fn new() -> Self { + Self::create_default(&LaptopLedData::get_data()) } - fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self { + fn file_name() -> &'static str { + CONFIG_FILE + } +} + +impl StdConfigLoad1 for AuraConfig {} + +impl AuraConfig { + fn create_default(support_data: &LaptopLedData) -> Self { // create a default config here let mut config = AuraConfig::default(); @@ -247,35 +229,10 @@ impl AuraConfig { } } } - - // Should be okay to unwrap this as is since it is a Default - let json = serde_json::to_string(&config).unwrap(); - file.write_all(json.as_bytes()) - .unwrap_or_else(|_| panic!("Could not write {}", CONFIG_FILE)); + config.write(); config } - pub fn read(&mut self) { - let mut file = config_file_open(CONFIG_FILE); - let mut buf = String::new(); - if let Ok(l) = file.read_to_string(&mut buf) { - if l == 0 { - warn!("File is empty {}", CONFIG_FILE); - } else { - let x = serde_json::from_str(&buf) - .unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_FILE)); - *self = x; - } - } - } - - pub fn write(&self) { - let mut file = File::create(CONFIG_FILE).expect("Couldn't overwrite config"); - let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed"); - file.write_all(json.as_bytes()) - .unwrap_or_else(|err| error!("Could not write config: {}", err)); - } - /// Set the mode data, current mode, and if multizone enabled. /// /// Multipurpose, will accept `AuraEffect` with zones and put in the correct diff --git a/daemon/src/ctrl_aura/controller.rs b/daemon/src/ctrl_aura/controller.rs index 077063a8..7d43904a 100644 --- a/daemon/src/ctrl_aura/controller.rs +++ b/daemon/src/ctrl_aura/controller.rs @@ -10,6 +10,7 @@ use rog_platform::keyboard_led::KeyboardLed; use rog_platform::supported::LedSupportedFunctions; use super::config::{AuraConfig, AuraPowerConfig}; +use crate::config_traits::StdConfig; use crate::error::RogError; use crate::GetSupported; diff --git a/daemon/src/ctrl_aura/trait_impls.rs b/daemon/src/ctrl_aura/trait_impls.rs index edf0038b..3985afe8 100644 --- a/daemon/src/ctrl_aura/trait_impls.rs +++ b/daemon/src/ctrl_aura/trait_impls.rs @@ -11,6 +11,7 @@ use zbus::export::futures_util::StreamExt; use zbus::{dbus_interface, Connection, SignalContext}; use super::controller::CtrlKbdLed; +use crate::config_traits::StdConfig; use crate::error::RogError; use crate::CtrlTask; diff --git a/daemon/src/ctrl_platform.rs b/daemon/src/ctrl_platform.rs index 505ebd46..904d343f 100644 --- a/daemon/src/ctrl_platform.rs +++ b/daemon/src/ctrl_platform.rs @@ -12,6 +12,7 @@ use zbus::export::futures_util::lock::Mutex; use zbus::{dbus_interface, Connection, SignalContext}; use crate::config::Config; +use crate::config_traits::StdConfig; use crate::error::RogError; use crate::{task_watch_item, CtrlTask, GetSupported}; diff --git a/daemon/src/ctrl_power.rs b/daemon/src/ctrl_power.rs index 66a934b7..b78c7ecb 100644 --- a/daemon/src/ctrl_power.rs +++ b/daemon/src/ctrl_power.rs @@ -12,6 +12,7 @@ use zbus::export::futures_util::lock::Mutex; use zbus::{dbus_interface, Connection, SignalContext}; use crate::config::Config; +use crate::config_traits::StdConfig; use crate::error::RogError; use crate::{task_watch_item, CtrlTask, GetSupported}; diff --git a/daemon/src/ctrl_profiles/config.rs b/daemon/src/ctrl_profiles/config.rs index 92752a08..0bb1e25c 100644 --- a/daemon/src/ctrl_profiles/config.rs +++ b/daemon/src/ctrl_profiles/config.rs @@ -1,90 +1,39 @@ -use std::fs::{File, OpenOptions}; -use std::io::{Read, Write}; -use std::path::PathBuf; - -use log::{error, warn}; use rog_profiles::{FanCurveProfiles, Profile}; use serde_derive::{Deserialize, Serialize}; -use crate::{config_file, config_file_open}; +use crate::config_traits::{StdConfig, StdConfigLoad1}; -static CONFIG_FILE: &str = "profile.conf"; +const CONFIG_FILE: &str = "profile.conf"; +const CONFIG_FAN_FILE: &str = "fan_curves.conf"; #[derive(Deserialize, Serialize, Debug)] pub struct ProfileConfig { - #[serde(skip)] - config_path: PathBuf, /// For restore on boot pub active_profile: Profile, - /// States to restore - pub fan_curves: Option, } -impl ProfileConfig { - fn new(config_path: PathBuf) -> Self { +impl StdConfig for ProfileConfig { + fn new() -> Self { Self { - config_path, active_profile: Profile::Balanced, - fan_curves: None, } } - pub fn load() -> Self { - let config_path = config_file(CONFIG_FILE); - let mut file = config_file_open(CONFIG_FILE); - let mut buf = String::new(); - let mut config; - if let Ok(read_len) = file.read_to_string(&mut buf) { - if read_len == 0 { - config = Self::new(config_path); - } else if let Ok(data) = toml::from_str(&buf) { - config = data; - config.config_path = config_path; - } else { - warn!( - "Could not deserialise {config_path:?}.\nWill rename to {config_path:?}-old \ - and recreate config", - ); - let mut cfg_old = config_path.clone(); - cfg_old.push("-old"); - std::fs::rename(config_path.clone(), cfg_old).unwrap_or_else(|err| { - panic!( - "Could not rename. Please remove {config_path:?} then restart service: \ - Error {err}", - ) - }); - config = Self::new(config_path); - } - } else { - config = Self::new(config_path); - } - config - } - - pub fn read(&mut self) { - let mut file = OpenOptions::new() - .read(true) - .open(&self.config_path) - .unwrap_or_else(|err| panic!("Error reading {:?}: {}", self.config_path, err)); - - let mut buf = String::new(); - if let Ok(l) = file.read_to_string(&mut buf) { - if l == 0 { - warn!("File is empty {:?}", self.config_path); - } else { - let mut data: ProfileConfig = toml::from_str(&buf) - .unwrap_or_else(|_| panic!("Could not deserialise {:?}", self.config_path)); - // copy over serde skipped values - data.config_path = self.config_path.clone(); - *self = data; - } - } - } - - pub fn write(&self) { - let mut file = File::create(&self.config_path).expect("Couldn't overwrite config"); - let data = toml::to_string(self).expect("Parse config to toml failed"); - file.write_all(data.as_bytes()) - .unwrap_or_else(|err| error!("Could not write config: {}", err)); + fn file_name() -> &'static str { + CONFIG_FILE } } + +impl StdConfigLoad1 for ProfileConfig {} + +impl StdConfig for FanCurveProfiles { + fn new() -> Self { + Self::default() + } + + fn file_name() -> &'static str { + CONFIG_FAN_FILE + } +} + +impl StdConfigLoad1 for FanCurveProfiles {} diff --git a/daemon/src/ctrl_profiles/controller.rs b/daemon/src/ctrl_profiles/controller.rs index d113c405..3a8f0af9 100644 --- a/daemon/src/ctrl_profiles/controller.rs +++ b/daemon/src/ctrl_profiles/controller.rs @@ -5,11 +5,13 @@ use rog_profiles::error::ProfileError; use rog_profiles::{FanCurveProfiles, Profile}; use super::config::ProfileConfig; +use crate::config_traits::StdConfig; use crate::error::RogError; use crate::GetSupported; pub struct CtrlPlatformProfile { - pub config: ProfileConfig, + pub profile_config: ProfileConfig, + pub fan_config: Option, pub platform: AsusPlatform, } @@ -50,21 +52,26 @@ impl CtrlPlatformProfile { if platform.has_platform_profile() || platform.has_throttle_thermal_policy() { info!("Device has profile control available"); - let mut controller = CtrlPlatformProfile { config, platform }; + let mut controller = CtrlPlatformProfile { + profile_config: config, + fan_config: None, + platform, + }; if FanCurveProfiles::get_device().is_ok() { info!("Device has fan curves available"); - if controller.config.fan_curves.is_none() { - controller.config.fan_curves = Some(Default::default()); + if controller.fan_config.is_none() { + controller.fan_config = Some(Default::default()); for _ in [Profile::Balanced, Profile::Performance, Profile::Quiet] { controller.set_next_profile()?; controller.set_active_curve_to_defaults()?; let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); - if let Some(curves) = controller.config.fan_curves.as_ref() { + if let Some(curves) = controller.fan_config.as_ref() { info!( "{active:?}: {}", String::from(curves.get_fan_curves_for(active)) ); + curves.write(); } } } @@ -77,25 +84,28 @@ impl CtrlPlatformProfile { } pub fn save_config(&self) { - self.config.write(); + self.profile_config.write(); + if let Some(fans) = self.fan_config.as_ref() { + fans.write(); + } } /// Toggle to next profile in list. This will first read the config, switch, /// then write out pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> { // Read first just incase the user has modified the config before calling this - match self.config.active_profile { + match self.profile_config.active_profile { Profile::Balanced => { Profile::set_profile(Profile::Performance)?; - self.config.active_profile = Profile::Performance; + self.profile_config.active_profile = Profile::Performance; } Profile::Performance => { Profile::set_profile(Profile::Quiet)?; - self.config.active_profile = Profile::Quiet; + self.profile_config.active_profile = Profile::Quiet; } Profile::Quiet => { Profile::set_profile(Profile::Balanced)?; - self.config.active_profile = Profile::Balanced; + self.profile_config.active_profile = Profile::Balanced; } } self.write_profile_curve_to_platform()?; @@ -104,18 +114,24 @@ impl CtrlPlatformProfile { /// Set the curve for the active profile active pub(super) fn write_profile_curve_to_platform(&mut self) -> Result<(), RogError> { - if let Some(curves) = &mut self.config.fan_curves { + if let Some(curves) = &mut self.fan_config { if let Ok(mut device) = FanCurveProfiles::get_device() { - curves.write_profile_curve_to_platform(self.config.active_profile, &mut device)?; + curves.write_profile_curve_to_platform( + self.profile_config.active_profile, + &mut device, + )?; } } Ok(()) } pub(super) fn set_active_curve_to_defaults(&mut self) -> Result<(), RogError> { - if let Some(curves) = self.config.fan_curves.as_mut() { + if let Some(curves) = self.fan_config.as_mut() { if let Ok(mut device) = FanCurveProfiles::get_device() { - curves.set_active_curve_to_defaults(self.config.active_profile, &mut device)?; + curves.set_active_curve_to_defaults( + self.profile_config.active_profile, + &mut device, + )?; } } Ok(()) diff --git a/daemon/src/ctrl_profiles/trait_impls.rs b/daemon/src/ctrl_profiles/trait_impls.rs index e30a5a29..18685458 100644 --- a/daemon/src/ctrl_profiles/trait_impls.rs +++ b/daemon/src/ctrl_profiles/trait_impls.rs @@ -10,6 +10,7 @@ use zbus::fdo::Error; use zbus::{dbus_interface, Connection, SignalContext}; use super::controller::CtrlPlatformProfile; +use crate::config_traits::StdConfig; use crate::error::RogError; use crate::CtrlTask; @@ -40,7 +41,7 @@ impl ProfileZbus { .unwrap_or_else(|err| warn!("{}", err)); ctrl.save_config(); - Self::notify_profile(&ctxt, ctrl.config.active_profile) + Self::notify_profile(&ctxt, ctrl.profile_config.active_profile) .await .ok(); } @@ -48,8 +49,8 @@ impl ProfileZbus { /// Fetch the active profile name async fn active_profile(&mut self) -> zbus::fdo::Result { let mut ctrl = self.0.lock().await; - ctrl.config.read(); - Ok(ctrl.config.active_profile) + ctrl.profile_config.read(); + Ok(ctrl.profile_config.active_profile) } /// Set this platform_profile name as active @@ -60,18 +61,18 @@ impl ProfileZbus { ) { let mut ctrl = self.0.lock().await; // Read first just incase the user has modified the config before calling this - ctrl.config.read(); + ctrl.profile_config.read(); Profile::set_profile(profile) .map_err(|e| warn!("set_profile, {}", e)) .ok(); - ctrl.config.active_profile = profile; + ctrl.profile_config.active_profile = profile; ctrl.write_profile_curve_to_platform() .map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) .ok(); ctrl.save_config(); - Self::notify_profile(&ctxt, ctrl.config.active_profile) + Self::notify_profile(&ctxt, ctrl.profile_config.active_profile) .await .ok(); } @@ -79,8 +80,8 @@ impl ProfileZbus { /// Get a list of profiles that have fan-curves enabled. async fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result> { let mut ctrl = self.0.lock().await; - ctrl.config.read(); - if let Some(curves) = &ctrl.config.fan_curves { + ctrl.profile_config.read(); + if let Some(curves) = &ctrl.fan_config { return Ok(curves.get_enabled_curve_profiles()); } Err(Error::Failed(UNSUPPORTED_MSG.to_owned())) @@ -94,8 +95,8 @@ impl ProfileZbus { enabled: bool, ) -> zbus::fdo::Result<()> { let mut ctrl = self.0.lock().await; - ctrl.config.read(); - if let Some(curves) = &mut ctrl.config.fan_curves { + ctrl.profile_config.read(); + if let Some(curves) = &mut ctrl.fan_config { curves.set_profile_curve_enabled(profile, enabled); ctrl.write_profile_curve_to_platform() @@ -112,8 +113,8 @@ impl ProfileZbus { /// Get the fan-curve data for the currently active Profile async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result { let mut ctrl = self.0.lock().await; - ctrl.config.read(); - if let Some(curves) = &ctrl.config.fan_curves { + ctrl.profile_config.read(); + if let Some(curves) = &ctrl.fan_config { let curve = curves.get_fan_curves_for(profile); return Ok(curve.clone()); } @@ -124,8 +125,8 @@ impl ProfileZbus { /// Will also activate the fan curve if the user is in the same mode. async fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> { let mut ctrl = self.0.lock().await; - ctrl.config.read(); - if let Some(curves) = &mut ctrl.config.fan_curves { + ctrl.profile_config.read(); + if let Some(curves) = &mut ctrl.fan_config { curves .save_fan_curve(curve, profile) .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; @@ -147,7 +148,7 @@ impl ProfileZbus { /// read only for the currently active profile. async fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> { let mut ctrl = self.0.lock().await; - ctrl.config.read(); + ctrl.profile_config.read(); ctrl.set_active_curve_to_defaults() .map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e)) .ok(); @@ -162,7 +163,7 @@ impl ProfileZbus { /// read only for the currently active profile. async fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> { let mut ctrl = self.0.lock().await; - ctrl.config.read(); + ctrl.profile_config.read(); let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); Profile::set_profile(profile) @@ -215,14 +216,14 @@ impl CtrlTask for ProfileZbus { let mut lock = ctrl.lock().await; let new_thermal = lock.platform.get_throttle_thermal_policy().unwrap(); let new_profile = Profile::from_throttle_thermal_policy(new_thermal); - if new_profile != lock.config.active_profile { + if new_profile != lock.profile_config.active_profile { info!("throttle_thermal_policy changed to {new_profile}"); - lock.config.active_profile = new_profile; + lock.profile_config.active_profile = new_profile; lock.write_profile_curve_to_platform().unwrap(); lock.save_config(); - Profile::set_profile(lock.config.active_profile).unwrap(); + Profile::set_profile(lock.profile_config.active_profile).unwrap(); } - Self::notify_profile(&signal_ctxt, lock.config.active_profile) + Self::notify_profile(&signal_ctxt, lock.profile_config.active_profile) .await .ok(); }) @@ -238,14 +239,14 @@ impl crate::Reloadable for ProfileZbus { /// Fetch the active profile and use that to set all related components up async fn reload(&mut self) -> Result<(), RogError> { let mut ctrl = self.0.lock().await; - let active = ctrl.config.active_profile; - if let Some(curves) = &mut ctrl.config.fan_curves { + let active = ctrl.profile_config.active_profile; + if let Some(curves) = &mut ctrl.fan_config { if let Ok(mut device) = FanCurveProfiles::get_device() { // There is a possibility that the curve was default zeroed, so this call // initialises the data from system read and we need to save it // after curves.write_profile_curve_to_platform(active, &mut device)?; - ctrl.config.write(); + ctrl.profile_config.write(); } } Ok(()) diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index 562b1528..8ddfff80 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -7,6 +7,7 @@ use std::time::Duration; use ::zbus::export::futures_util::lock::Mutex; use ::zbus::Connection; use daemon::config::Config; +use daemon::config_traits::{StdConfigLoad1, StdConfigLoad3}; use daemon::ctrl_anime::config::AnimeConfig; use daemon::ctrl_anime::trait_impls::CtrlAnimeZbus; use daemon::ctrl_anime::CtrlAnime; @@ -123,7 +124,7 @@ async fn start_daemon() -> Result<(), Box> { } let laptop = LaptopLedData::get_data(); - let aura_config = AuraConfig::load(&laptop); + let aura_config = AuraConfig::load(); match CtrlKbdLed::new(laptop, aura_config) { Ok(ctrl) => { let zbus = CtrlKbdLedZbus(Arc::new(Mutex::new(ctrl))); diff --git a/daemon/src/error.rs b/daemon/src/error.rs index a70450e3..068a792d 100644 --- a/daemon/src/error.rs +++ b/daemon/src/error.rs @@ -34,6 +34,7 @@ pub enum RogError { SystemdUnitAction(String), SystemdUnitWaitTimeout(String), Command(String, std::io::Error), + ParseRon(ron::Error), } impl fmt::Display for RogError { @@ -82,6 +83,7 @@ impl fmt::Display for RogError { ) } RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error), + RogError::ParseRon(error) => write!(f, "Parse config error: {}", error), } } } @@ -118,6 +120,12 @@ impl From for RogError { } } +impl From for RogError { + fn from(err: ron::Error) -> Self { + RogError::ParseRon(err) + } +} + impl From for zbus::fdo::Error { #[inline] fn from(err: RogError) -> Self { diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 7a09d23e..2cde6dac 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -1,6 +1,8 @@ #![deny(unused_must_use)] /// Configuration loading, saving pub mod config; +/// Base traits for configuration file loading +pub mod config_traits; /// Control of anime matrix display pub mod ctrl_anime; /// Keyboard LED brightness control, RGB, and LED display modes @@ -17,9 +19,7 @@ pub mod ctrl_supported; pub mod error; -use std::fs::{create_dir, File, OpenOptions}; use std::future::Future; -use std::path::PathBuf; use async_trait::async_trait; use log::{debug, info, warn}; @@ -30,30 +30,6 @@ use zbus::{Connection, SignalContext}; use crate::error::RogError; -static CONFIG_PATH_BASE: &str = "/etc/asusd/"; - -/// Create a `PathBuf` for `file`. If the base config dir `CONFIG_PATH_BASE` -/// does not exist it is created. -fn config_file(file: &str) -> PathBuf { - let mut config = PathBuf::from(CONFIG_PATH_BASE); - if !config.exists() { - create_dir(config.as_path()).unwrap_or_else(|_| panic!("Could not create {config:?}")); - } - config.push(file); - config -} - -/// Open a config file as read/write. If the file or dir does not exist then -/// both are created. -pub fn config_file_open(file: &str) -> File { - OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(config_file(file)) - .unwrap_or_else(|_| panic!("The file {file} or directory {CONFIG_PATH_BASE} is missing")) -} - /// This macro adds a function which spawns an `inotify` task on the passed in /// `Executor`. ///