From 5428d063a72aa41b9109a90ee1c952790f67c1b0 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Sat, 28 Feb 2026 11:09:44 +0100 Subject: [PATCH] [fix](trx-client): require per-rig rigctl listeners Remove the shared rigctl listener path so rigctl only\nruns as per-rig listeners configured through\nfrontends.rigctl.rig_ports.\n\nTighten client config validation to require at least one\nper-rig rigctl port when the rigctl frontend is enabled.\n\nCo-authored-by: Codex Signed-off-by: Stan Grams --- src/trx-client/src/config.rs | 26 ++++++++++++++++++++------ src/trx-client/src/main.rs | 10 ++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/trx-client/src/config.rs b/src/trx-client/src/config.rs index ad8f27f..e1b9cad 100644 --- a/src/trx-client/src/config.rs +++ b/src/trx-client/src/config.rs @@ -248,11 +248,11 @@ pub struct RigctlFrontendConfig { pub enabled: bool, /// Listen address pub listen: IpAddr, - /// Listen port (used for single-rig setups or as the fallback base port) + /// Legacy shared-listener port. Ignored; per-rig ports must be configured. pub port: u16, - /// Per-rig port overrides for multi-rig servers. - /// Maps rig ID → local rigctl port. When non-empty, one rigctl listener - /// is spawned per entry, each routing commands to its assigned rig. + /// Per-rig rigctl listener ports. + /// Maps rig ID -> local rigctl port. One rigctl listener is spawned per + /// entry, each routing commands to its assigned rig. pub rig_ports: HashMap, } @@ -326,8 +326,22 @@ impl ClientConfig { if self.frontends.http.enabled && self.frontends.http.port == 0 { return Err("[frontends.http].port must be > 0 when enabled".to_string()); } - if self.frontends.rigctl.enabled && self.frontends.rigctl.port == 0 { - return Err("[frontends.rigctl].port must be > 0 when enabled".to_string()); + if self.frontends.rigctl.enabled && self.frontends.rigctl.rig_ports.is_empty() { + return Err( + "[frontends.rigctl].rig_ports must contain at least one rig when enabled" + .to_string(), + ); + } + for (rig_id, port) in &self.frontends.rigctl.rig_ports { + if rig_id.trim().is_empty() { + return Err("[frontends.rigctl].rig_ports keys must not be empty".to_string()); + } + if *port == 0 { + return Err(format!( + "[frontends.rigctl].rig_ports[\"{}\"] must be > 0", + rig_id + )); + } } if self.frontends.audio.enabled && self.frontends.audio.server_port == 0 { return Err("[frontends.audio].server_port must be > 0 when enabled".to_string()); diff --git a/src/trx-client/src/main.rs b/src/trx-client/src/main.rs index 7239605..f92c567 100644 --- a/src/trx-client/src/main.rs +++ b/src/trx-client/src/main.rs @@ -337,8 +337,8 @@ async fn async_init() -> DynResult { for frontend in &frontends { let frontend_state_rx = state_rx.clone(); - // rigctl with per-rig port mapping: spawn one listener per rig entry. - if frontend == "rigctl" && !cfg.frontends.rigctl.rig_ports.is_empty() { + // rigctl: always spawn one listener per configured rig entry. + if frontend == "rigctl" { let mut first = true; for (rig_id, &port) in &cfg.frontends.rigctl.rig_ports { let addr = SocketAddr::from((rigctl_listen, port)); @@ -378,17 +378,11 @@ async fn async_init() -> DynResult { let addr = match frontend.as_str() { "http" => SocketAddr::from((http_listen, http_port)), - "rigctl" => SocketAddr::from((rigctl_listen, rigctl_port)), "httpjson" => SocketAddr::from((http_json_listen, http_json_port)), other => { return Err(format!("Frontend missing listen configuration: {}", other).into()); } }; - if frontend == "rigctl" { - if let Ok(mut listen_addr) = frontend_runtime_ctx.rigctl_listen_addr.lock() { - *listen_addr = Some(addr); - } - } frontend_reg_ctx.spawn_frontend( frontend, frontend_state_rx,