diff --git a/Cargo.lock b/Cargo.lock index 939d05d..d70009c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -278,6 +278,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -434,6 +440,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "heck" version = "0.5.0" @@ -470,6 +482,16 @@ dependencies = [ "cc", ] +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "intl-memoizer" version = "0.5.3" @@ -797,6 +819,7 @@ dependencies = [ "nix", "serde", "tokio", + "toml", "tracing", "tracing-subscriber", "uucore", @@ -883,6 +906,15 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1089,6 +1121,45 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.9.10+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + [[package]] name = "tracing" version = "0.1.44" @@ -1436,6 +1507,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" + [[package]] name = "zerofrom" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index 685fd30..42457c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ futures-util = "0.3.31" nix = { version = "0.30.1", features = ["hostname", "net"] } serde = { version = "1.0.228", features = ["derive"] } tokio = { version = "1.49.0", features = ["macros", "net", "rt-multi-thread", "signal", "sync", "time"] } +toml = "0.9.10" tracing = "0.1.44" tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } # onc-rpc = "0.3.2" diff --git a/src/bin/roowhod.rs b/src/bin/roowhod.rs index 6b9ee27..05a625f 100644 --- a/src/bin/roowhod.rs +++ b/src/bin/roowhod.rs @@ -19,6 +19,35 @@ async fn main() -> anyhow::Result<()> { .with(EnvFilter::from_default_env()) .init(); + let config = toml::from_str::( + &std::fs::read_to_string("/etc/roowho2/roowho2.toml") + .context("Failed to read configuration file /etc/roowho2/roowho2.toml")?, + )?; + + let mut join_set = tokio::task::JoinSet::new(); + + if config.rwhod.enable { + tracing::info!("Starting RWHOD server"); + + join_set.spawn(rwhod_server()); + } else { + tracing::debug!("RWHOD server is disabled in configuration"); + } + + join_set.spawn(ctrl_c_handler()); + + join_set.join_next().await.unwrap()??; + + Ok(()) +} + +async fn ctrl_c_handler() -> anyhow::Result<()> { + tokio::signal::ctrl_c() + .await + .map_err(|e| anyhow::anyhow!("Failed to listen for Ctrl-C: {}", e)) +} + +async fn rwhod_server() -> anyhow::Result<()> { let addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, RWHOD_BROADCAST_PORT); tracing::debug!("Binding RWHOD socket to {}", addr); let socket = tokio::net::UdpSocket::bind(addr) @@ -42,24 +71,9 @@ async fn main() -> anyhow::Result<()> { let client_server_task = rwhod_client_server_task(client_server_socket, status_store.clone()); tokio::select! { - res = sender_task => { - if let Err(err) = res { - eprintln!("RWHOD sender task error: {}", err); - } - } - res = receiver_task => { - if let Err(err) = res { - eprintln!("RWHOD receiver task error: {}", err); - } - } - res = client_server_task => { - if let Err(err) = res { - eprintln!("RWHOD client-server task error: {}", err); - } - } - _ = tokio::signal::ctrl_c() => { - println!("Received Ctrl-C, shutting down."); - } + res = sender_task => res?, + res = receiver_task => res?, + res = client_server_task => res?, } Ok(()) diff --git a/src/server.rs b/src/server.rs index 7f83c33..f785562 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1 +1,2 @@ pub mod rwhod; +pub mod config; diff --git a/src/server/config.rs b/src/server/config.rs new file mode 100644 index 0000000..1201305 --- /dev/null +++ b/src/server/config.rs @@ -0,0 +1,36 @@ +use std::net::SocketAddrV4; + +use serde::{Deserialize, Serialize}; + +pub const DEFAULT_CONFIG_PATH: &str = "/etc/roowho2/config.toml"; + +pub const DEFAULT_CLIENT_SOCKET_PATH: &str = "/run/roowho2/server_client.sock"; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Config { + /// Configuration for the rwhod server. + pub rwhod: RwhodConfig, + + /// Path to the Unix domain socket for client-server communication. + /// + /// If left as `None`, the server expects to be served a file descriptor to the socket named 'client'. + pub client_socket_path: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RwhodConfig { + /// Enable or disable the rwhod server functionality. + pub enable: bool, + + /// Network interfaces to listen on (e.g., ["eth0", "wlan0"]). + /// + /// If left as `None`, the server will automatically determine relevant interfaces. + /// + /// Note that if `broadcast_addresses` is specified, this field is ignored. + pub interfaces: Option>, + + /// Broadcast addresses to send rwhod packets to. + /// + /// If left as `None`, the server will automatically determine broadcast addresses for the selected interfaces. + pub broadcast_addresses: Option>, +} diff --git a/src/server/rwhod.rs b/src/server/rwhod.rs index 4af5a4b..6f04189 100644 --- a/src/server/rwhod.rs +++ b/src/server/rwhod.rs @@ -75,7 +75,7 @@ pub fn generate_rwhod_status_update() -> anyhow::Result { Ok(result) } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RwhodSendTarget { /// Name of the network interface. pub name: String,