session and seat management daemon
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(config): define daemon config schema

authored by

r0chd and committed by
Tangled
(Jul 14, 2026, 3:50 PM +0300) a2507aaf 2805f042

+168
+32
Cargo.lock
··· 524 524 "hyprwire-scanner", 525 525 "log", 526 526 "rustix", 527 + "serde", 528 + "toml", 527 529 "users", 528 530 "zbus", 529 531 ] ··· 1218 1220 ] 1219 1221 1220 1222 [[package]] 1223 + name = "serde_spanned" 1224 + version = "1.1.1" 1225 + source = "registry+https://github.com/rust-lang/crates.io-index" 1226 + checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" 1227 + dependencies = [ 1228 + "serde_core", 1229 + ] 1230 + 1231 + [[package]] 1221 1232 name = "signal-hook-registry" 1222 1233 version = "1.4.8" 1223 1234 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1314 1325 ] 1315 1326 1316 1327 [[package]] 1328 + name = "toml" 1329 + version = "1.1.2+spec-1.1.0" 1330 + source = "registry+https://github.com/rust-lang/crates.io-index" 1331 + checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" 1332 + dependencies = [ 1333 + "indexmap", 1334 + "serde_core", 1335 + "serde_spanned", 1336 + "toml_datetime", 1337 + "toml_parser", 1338 + "toml_writer", 1339 + "winnow", 1340 + ] 1341 + 1342 + [[package]] 1317 1343 name = "toml_datetime" 1318 1344 version = "1.1.1+spec-1.1.0" 1319 1345 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1342 1368 dependencies = [ 1343 1369 "winnow", 1344 1370 ] 1371 + 1372 + [[package]] 1373 + name = "toml_writer" 1374 + version = "1.1.1+spec-1.1.0" 1375 + source = "registry+https://github.com/rust-lang/crates.io-index" 1376 + checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" 1345 1377 1346 1378 [[package]] 1347 1379 name = "tracing"
+2
daemon/Cargo.toml
··· 23 23 users = "0.11.0" 24 24 zbus = { version = "5.16.0", optional = true } 25 25 bevy_ecs = { version = "0.19.0", default-features = false } 26 + serde = { version = "1.0.228", features = ["derive"] } 27 + toml = "1.1.2" 26 28 27 29 [build-dependencies] 28 30 hyprwire-scanner = "0.4.3"
+133
daemon/src/config.rs
··· 1 + use anyhow::Context; 2 + use std::collections::BTreeMap; 3 + use std::{fs, io, path}; 4 + 5 + #[derive(serde::Deserialize, Default)] 6 + #[serde(rename_all = "kebab-case")] 7 + pub enum SeatKind { 8 + #[default] 9 + VtBound, 10 + NonVt, 11 + } 12 + 13 + #[derive(serde::Deserialize)] 14 + #[serde(deny_unknown_fields)] 15 + pub struct SeatConfig { 16 + #[serde(default = "default_user")] 17 + pub user: String, 18 + #[serde(default = "default_group")] 19 + pub group: String, 20 + #[serde(default)] 21 + pub kind: SeatKind, 22 + } 23 + 24 + impl Default for SeatConfig { 25 + fn default() -> Self { 26 + Self { 27 + user: "root".to_string(), 28 + group: "root".to_string(), 29 + kind: SeatKind::VtBound, 30 + } 31 + } 32 + } 33 + 34 + fn default_user() -> String { 35 + "root".to_string() 36 + } 37 + 38 + fn default_group() -> String { 39 + "root".to_string() 40 + } 41 + 42 + #[derive(serde::Deserialize)] 43 + #[serde(deny_unknown_fields)] 44 + pub struct ManagementConfig { 45 + #[serde(default = "default_user")] 46 + pub user: String, 47 + #[serde(default = "default_group")] 48 + pub group: String, 49 + } 50 + 51 + impl Default for ManagementConfig { 52 + fn default() -> Self { 53 + Self { 54 + user: "root".to_string(), 55 + group: "root".to_string(), 56 + } 57 + } 58 + } 59 + 60 + #[derive(serde::Deserialize)] 61 + #[serde(deny_unknown_fields)] 62 + pub struct UserCgroupConfig { 63 + pub memory_max: Option<u64>, 64 + pub memory_high: Option<u64>, 65 + pub pids_max: Option<u64>, 66 + pub cpu_weight: Option<u16>, 67 + } 68 + 69 + #[derive(serde::Deserialize)] 70 + #[serde(deny_unknown_fields)] 71 + pub struct UserConfig { 72 + pub linger: bool, 73 + pub cgroup: UserCgroupConfig, 74 + } 75 + 76 + #[derive(serde::Deserialize)] 77 + #[serde(deny_unknown_fields)] 78 + pub struct Config { 79 + #[serde(default)] 80 + pub management: ManagementConfig, 81 + #[serde(default)] 82 + pub users: BTreeMap<String, UserConfig>, 83 + #[serde(default = "default_seats")] 84 + pub seats: BTreeMap<String, SeatConfig>, 85 + } 86 + 87 + impl Default for Config { 88 + fn default() -> Self { 89 + Self { 90 + management: ManagementConfig::default(), 91 + users: BTreeMap::new(), 92 + seats: default_seats(), 93 + } 94 + } 95 + } 96 + 97 + fn default_seats() -> BTreeMap<String, SeatConfig> { 98 + BTreeMap::from([("seat0".to_string(), SeatConfig::default())]) 99 + } 100 + 101 + impl Config { 102 + pub fn load(path: Option<path::PathBuf>) -> anyhow::Result<Self> { 103 + let explicit_path = path.is_some(); 104 + let path = path.unwrap_or_else(|| path::PathBuf::from("/etc/sessiond.toml")); 105 + 106 + let config = match fs::read_to_string(&path) { 107 + Ok(config) => config, 108 + Err(err) if !explicit_path && err.kind() == io::ErrorKind::NotFound => { 109 + log::info!(path = path.to_string_lossy().as_ref(); "config file not found, using defaults"); 110 + return Ok(Self::default()); 111 + } 112 + Err(err) => { 113 + log::error!( 114 + path = path.to_string_lossy().as_ref(), 115 + error = err.to_string().as_str(); 116 + "failed to read config file" 117 + ); 118 + return Err(err) 119 + .with_context(|| format!("failed to read config file {}", path.display())); 120 + } 121 + }; 122 + 123 + toml::from_str::<Self>(&config) 124 + .inspect_err(|err| { 125 + log::error!( 126 + path = path.to_string_lossy().as_ref(), 127 + error = err.to_string().as_str(); 128 + "failed to parse config file" 129 + ); 130 + }) 131 + .with_context(|| format!("failed to parse config file {}", path.display())) 132 + } 133 + }
+1
daemon/src/main.rs
··· 14 14 } 15 15 16 16 mod cgroup; 17 + mod config; 17 18 #[cfg(feature = "consolekit")] 18 19 mod consolekit; 19 20 mod device;