Skip to content

Commit a0ee1b7

Browse files
committed
feat: implement configurable system-wide TTLs and blacklist support via /etc/contextd/config.toml
1 parent 6198b91 commit a0ee1b7

9 files changed

Lines changed: 167 additions & 11 deletions

File tree

Cargo.lock

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ serde_derive = "1.0"
2020
rusqlite = { version = "0.39", features = ["bundled"] }
2121
sysinfo = "0.30"
2222
libc = "0.2"
23+
toml = "1.1.2"
24+
once_cell = "1.21.4"
2325

2426
[build-dependencies]
2527
varlink_generator = "13.0"

docs/todo.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@
1919

2020
## Phase 7: Refinement & Security Hardening
2121
- [ ] **Systemd Peer Validation**: Use `SO_PEERCRED` to identify calling services and apply granular access control based on systemd units.
22-
- [ ] **Configuration Overrides**: Support `/etc/contextd/config.toml` for manual TTL tuning and process blacklisting.
22+
- [x] **Configuration Overrides**: Implemented `src/config.rs` with `/etc/contextd/config.toml` support for TTLs and blacklisting.
2323
- [ ] **Arch Linux PKGBUILD**: Finalize the AUR package for the 1.0 release.

examples/config.sample.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# contextd configuration file
2+
# Place this at /etc/contextd/config.toml
3+
4+
[ttls]
5+
# TTL for active game detection in seconds (default 5)
6+
games = 5
7+
# TTL for hardware inventory in seconds (default 10)
8+
hardware = 10
9+
# TTL for system diagnostics in seconds (default 300)
10+
diagnostics = 300
11+
12+
[blacklist]
13+
# Process names to ignore in game detection
14+
# processes = ["Calculator"]
15+
# Hardware paths (udev paths) to ignore
16+
# devices = ["/sys/devices/virtual/input/input231"]

src/config.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//! Configuration management for contextd
2+
//!
3+
//! Handles loading and parsing the /etc/contextd/config.toml file.
4+
5+
use once_cell::sync::Lazy;
6+
use serde::Deserialize;
7+
use std::fs;
8+
9+
/// Global configuration instance
10+
pub static CONFIG: Lazy<Config> = Lazy::new(Config::load);
11+
12+
/// Main configuration structure
13+
#[derive(Debug, Deserialize, Clone, Default)]
14+
pub struct Config {
15+
#[serde(default)]
16+
pub ttls: TtlConfig,
17+
#[serde(default)]
18+
pub blacklist: BlacklistConfig,
19+
}
20+
21+
/// TTL settings for various detectors (in seconds)
22+
#[derive(Debug, Deserialize, Clone)]
23+
pub struct TtlConfig {
24+
/// TTL for active game detection (default 5s)
25+
pub games: u64,
26+
/// TTL for hardware inventory (default 10s)
27+
pub hardware: u64,
28+
/// TTL for system diagnostics (default 300s / 5m)
29+
pub diagnostics: u64,
30+
}
31+
32+
impl Default for TtlConfig {
33+
fn default() -> Self {
34+
Self {
35+
games: 5,
36+
hardware: 10,
37+
diagnostics: 300,
38+
}
39+
}
40+
}
41+
42+
/// Blacklist settings for ignoring specific items
43+
#[derive(Debug, Deserialize, Clone, Default)]
44+
pub struct BlacklistConfig {
45+
/// Process names to ignore in game detection
46+
pub processes: Vec<String>,
47+
/// Hardware paths (udev paths) to ignore
48+
pub devices: Vec<String>,
49+
}
50+
51+
impl Config {
52+
/// Loads the configuration from /etc/contextd/config.toml,
53+
/// falling back to defaults if the file is missing or invalid.
54+
fn load() -> Self {
55+
let paths = ["/etc/contextd/config.toml", "config.toml"];
56+
57+
for path in paths {
58+
if let Ok(content) = fs::read_to_string(path) {
59+
if let Ok(config) = toml::from_str(&content) {
60+
log::info!("Loaded configuration from {}", path);
61+
return config;
62+
} else {
63+
log::warn!("Failed to parse configuration at {}, using defaults", path);
64+
}
65+
}
66+
}
67+
68+
log::debug!("No configuration file found, using defaults");
69+
Self::default()
70+
}
71+
}

src/detectors/diagnostics/manager.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ impl DiagnosticsManager {
3232
.unwrap()
3333
.as_secs() as i64;
3434

35+
let ttl = crate::config::CONFIG.ttls.diagnostics as i64;
3536
let needs_refresh = if let Some(ref diag) = self.cached_diagnostics {
36-
now - diag.last_updated > 300 // 5 minutes cache
37+
now - diag.last_updated > ttl
3738
} else {
3839
true
3940
};

src/detectors/games/manager.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use super::{Game, GameDetector};
2+
use crate::config::CONFIG;
23
use std::time::{Duration, Instant};
34

4-
const CACHE_DURATION: Duration = Duration::from_secs(5);
5-
65
pub struct GameManager {
76
detectors: Vec<Box<dyn GameDetector>>,
87
installed_cache: Option<(Vec<Game>, Instant)>,
@@ -24,8 +23,9 @@ impl GameManager {
2423
}
2524

2625
pub fn list_all_installed(&mut self) -> Vec<Game> {
26+
let ttl = Duration::from_secs(CONFIG.ttls.games);
2727
if let Some((cache, ts)) = &self.installed_cache
28-
&& ts.elapsed() < CACHE_DURATION
28+
&& ts.elapsed() < ttl
2929
{
3030
return cache.clone();
3131
}
@@ -34,20 +34,26 @@ impl GameManager {
3434
.detectors
3535
.iter()
3636
.flat_map(|d| d.list_installed())
37+
.filter(|g| !CONFIG.blacklist.processes.contains(&g.name))
3738
.collect();
3839

3940
self.installed_cache = Some((games.clone(), Instant::now()));
4041
games
4142
}
4243

4344
pub fn get_active_game(&mut self) -> Option<Game> {
45+
let ttl = Duration::from_secs(CONFIG.ttls.games);
4446
if let Some((cache, ts)) = &self.active_cache
45-
&& ts.elapsed() < CACHE_DURATION
47+
&& ts.elapsed() < ttl
4648
{
4749
return cache.clone();
4850
}
4951

50-
let game = self.detectors.iter().flat_map(|d| d.list_running()).next();
52+
let game = self
53+
.detectors
54+
.iter()
55+
.flat_map(|d| d.list_running())
56+
.find(|g| !CONFIG.blacklist.processes.contains(&g.name));
5157

5258
self.active_cache = Some((game.clone(), Instant::now()));
5359
game

src/detectors/hardware/manager.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use super::{Device, HardwareDetector};
2+
use crate::config::CONFIG;
23
use std::time::{Duration, Instant};
34

4-
const CACHE_DURATION: Duration = Duration::from_secs(10); // Hardware changes less often
5-
65
pub struct HardwareManager {
76
detectors: Vec<Box<dyn HardwareDetector>>,
87
cache: Option<(Vec<Device>, Instant)>,
@@ -32,8 +31,9 @@ impl HardwareManager {
3231
}
3332

3433
pub fn list_all_devices(&mut self) -> Vec<Device> {
34+
let ttl = Duration::from_secs(CONFIG.ttls.hardware);
3535
if let Some((cache, ts)) = &self.cache
36-
&& ts.elapsed() < CACHE_DURATION
36+
&& ts.elapsed() < ttl
3737
{
3838
return cache.clone();
3939
}
@@ -42,6 +42,7 @@ impl HardwareManager {
4242
.detectors
4343
.iter()
4444
.flat_map(|d| d.list_devices())
45+
.filter(|d| !CONFIG.blacklist.devices.contains(&d.path))
4546
.filter(|d| self.is_gaming_device(d))
4647
.collect();
4748

@@ -50,8 +51,9 @@ impl HardwareManager {
5051
}
5152

5253
pub fn list_rgb_devices(&mut self) -> Vec<Device> {
54+
let ttl = Duration::from_secs(CONFIG.ttls.hardware);
5355
if let Some((cache, ts)) = &self.rgb_cache
54-
&& ts.elapsed() < CACHE_DURATION
56+
&& ts.elapsed() < ttl
5557
{
5658
return cache.clone();
5759
}
@@ -60,6 +62,7 @@ impl HardwareManager {
6062
.detectors
6163
.iter()
6264
.flat_map(|d| d.list_devices())
65+
.filter(|d| !CONFIG.blacklist.devices.contains(&d.path))
6366
.filter(|d| self.is_rgb_device(d))
6467
.collect();
6568

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod contextd {
88
#![allow(clippy::all, non_snake_case, non_camel_case_types, unused_imports)]
99
include!(concat!(env!("OUT_DIR"), "/contextd.rs"));
1010
}
11+
mod config;
1112
mod detectors;
1213

1314
mod service;

0 commit comments

Comments
 (0)