mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
feat: automatically reload configuration when the config file is changed
This commit is contained in:
87
Cargo.lock
generated
87
Cargo.lock
generated
@@ -1241,6 +1241,26 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"inotify-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify-sys"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
@@ -1256,6 +1276,26 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
|
||||
dependencies = [
|
||||
"kqueue-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue-sys"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lact"
|
||||
version = "0.5.4"
|
||||
@@ -1302,6 +1342,7 @@ dependencies = [
|
||||
"libdrm_amdgpu_sys",
|
||||
"libflate",
|
||||
"nix",
|
||||
"notify",
|
||||
"os-release",
|
||||
"pciid-parser",
|
||||
"serde",
|
||||
@@ -1514,6 +1555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
@@ -1531,6 +1573,23 @@ dependencies = [
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "6.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"filetime",
|
||||
"inotify",
|
||||
"kqueue",
|
||||
"libc",
|
||||
"log",
|
||||
"mio",
|
||||
"walkdir",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
@@ -1927,6 +1986,15 @@ version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
@@ -2472,6 +2540,16 @@ dependencies = [
|
||||
"vk-parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
@@ -2548,6 +2626,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
||||
@@ -38,3 +38,4 @@ tar = "0.4.40"
|
||||
libflate = "2.0.0"
|
||||
chrono = "0.4.31"
|
||||
os-release = "0.1.0"
|
||||
notify = { version = "6.1.1", default-features = false }
|
||||
|
||||
@@ -3,10 +3,12 @@ use amdgpu_sysfs::gpu_handle::{PerformanceLevel, PowerLevelKind};
|
||||
use anyhow::Context;
|
||||
use lact_schema::{default_fan_curve, request::SetClocksCommand, FanControlMode, PmfwOptions};
|
||||
use nix::unistd::getuid;
|
||||
use notify::{RecommendedWatcher, Watcher};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use std::{collections::HashMap, env, fs, path::PathBuf};
|
||||
use tracing::debug;
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{debug, error};
|
||||
|
||||
const FILE_NAME: &str = "config.yaml";
|
||||
const DEFAULT_ADMIN_GROUPS: [&str; 2] = ["wheel", "sudo"];
|
||||
@@ -158,6 +160,55 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_watcher() -> mpsc::UnboundedReceiver<Config> {
|
||||
let (config_tx, config_rx) = mpsc::unbounded_channel();
|
||||
let (event_tx, event_rx) = std::sync::mpsc::channel();
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut watcher = RecommendedWatcher::new(event_tx, notify::Config::default())
|
||||
.expect("Could not create config file watcher");
|
||||
|
||||
let config_path = get_path();
|
||||
let watch_path = config_path
|
||||
.parent()
|
||||
.expect("Config path always has a parent");
|
||||
watcher
|
||||
.watch(watch_path, notify::RecursiveMode::Recursive)
|
||||
.expect("Could not subscribe to config file changes");
|
||||
|
||||
for res in event_rx {
|
||||
debug!("got config file event {res:?}");
|
||||
match res {
|
||||
Ok(event) => {
|
||||
use notify::EventKind;
|
||||
|
||||
if !event.paths.contains(&config_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match event.kind {
|
||||
EventKind::Modify(_) | EventKind::Create(_) | EventKind::Remove(_) => {
|
||||
match Config::load() {
|
||||
Ok(Some(new_config)) => config_tx.send(new_config).unwrap(),
|
||||
Ok(None) => error!("config was removed!"),
|
||||
Err(err) => {
|
||||
error!("could not read config after it was changed: {err:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Err(err) => error!("filesystem event error: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
debug!("registered config file event listener at path {watch_path:?}");
|
||||
});
|
||||
|
||||
config_rx
|
||||
}
|
||||
|
||||
fn get_path() -> PathBuf {
|
||||
let uid = getuid();
|
||||
if uid.is_root() {
|
||||
|
||||
@@ -54,6 +54,7 @@ pub fn run() -> anyhow::Result<()> {
|
||||
let server = Server::new(config).await?;
|
||||
let handler = server.handler.clone();
|
||||
|
||||
tokio::task::spawn_local(listen_config_changes(handler.clone()));
|
||||
tokio::task::spawn_local(listen_exit_signals(handler.clone()));
|
||||
tokio::task::spawn_local(suspend::listen_events(handler));
|
||||
server.run().await;
|
||||
@@ -102,6 +103,16 @@ async fn listen_exit_signals(handler: Handler) {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
async fn listen_config_changes(handler: Handler) {
|
||||
let mut rx = config::start_watcher();
|
||||
while let Some(new_config) = rx.recv().await {
|
||||
info!("config file was changed, reloading");
|
||||
handler.config.replace(new_config);
|
||||
handler.apply_current_config().await;
|
||||
info!("configuration reloaded");
|
||||
}
|
||||
}
|
||||
|
||||
async fn ensure_sufficient_uptime() {
|
||||
match get_uptime() {
|
||||
Ok(current_uptime) => {
|
||||
|
||||
@@ -102,7 +102,7 @@ impl<'a> Handler {
|
||||
config: Rc::new(RefCell::new(config)),
|
||||
confirm_config_tx: Rc::new(RefCell::new(None)),
|
||||
};
|
||||
handler.load_config().await;
|
||||
handler.apply_current_config().await;
|
||||
|
||||
// Eagerly release memory
|
||||
// `load_controllers` allocates and deallocates the entire PCI ID database,
|
||||
@@ -114,7 +114,7 @@ impl<'a> Handler {
|
||||
Ok(handler)
|
||||
}
|
||||
|
||||
pub async fn load_config(&self) {
|
||||
pub async fn apply_current_config(&self) {
|
||||
let config = self.config.borrow().clone(); // Clone to avoid locking the RwLock on an await point
|
||||
|
||||
for (id, gpu_config) in &config.gpus {
|
||||
|
||||
@@ -10,7 +10,7 @@ pub async fn listen_events(handler: Handler) {
|
||||
Ok(mut stream) => {
|
||||
while stream.next().await.is_some() {
|
||||
info!("suspend/resume event detected, reloading config");
|
||||
handler.load_config().await;
|
||||
handler.apply_current_config().await;
|
||||
}
|
||||
}
|
||||
Err(err) => error!("could not subscribe to suspend events: {err:#}"),
|
||||
|
||||
Reference in New Issue
Block a user