From 9bc700e0f698516fe9420f6148155ce6df0630f2 Mon Sep 17 00:00:00 2001 From: Fabian Stamm Date: Sat, 13 Apr 2024 23:57:46 +0200 Subject: [PATCH] Handle the case, that the bind ip address is only available after start --- src/config.rs | 36 ++++++++++++++++++++++++++---------- src/main.rs | 4 ++-- src/tcp.rs | 30 ++++++++++++++++++++++++++++-- src/udp.rs | 24 ++++++++++++++++++++++-- 4 files changed, 78 insertions(+), 16 deletions(-) diff --git a/src/config.rs b/src/config.rs index c15333f..3ec53a2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -41,38 +41,54 @@ impl FileConfigProvider { fn load_yaml(&self, path: &Path) -> Result { let file = File::open(path)?; - let config: Config = serde_yaml::from_reader(file).expect("Failed to parse!"); //TODO: Print path + let config: Config = serde_yaml::from_reader(file)?; //TODO: Print path return Ok(config); } fn load_json(&self, path: &Path) -> Result { let file = File::open(path)?; - let config: Config = serde_json::from_reader(file).expect("Failed to parse!"); //TODO: Print path + let config: Config = serde_json::from_reader(file)?; //TODO: Print path return Ok(config); } pub fn load_config(&self) -> Result { - for path in [ + let configs = [ "config.yaml", + "config.yml", "config.json", "/etc/rustocat.yaml", "/etc/rustocat.json", ] .iter() - { - // if(p) - let config = if path.ends_with(".yaml") { + .map(|path| { + if std::path::Path::new(path).exists() { + return Some(path); + } else { + return None; + } + }) + .filter(|p| p.is_some()) + .map(|p| p.unwrap()) + .map(|path| { + if path.ends_with(".yaml") { self.load_yaml(Path::new(path)) } else { self.load_json(Path::new(path)) - }; - if config.is_ok() { - return config; + } + }); + + for config in configs { + match config { + Ok(c) => return Ok(c), + Err(e) => { + info!("Error loading config: {}", e); + } } } - Err("No config file found".into()) + + Err("No (valid) config file found".into()) } } diff --git a/src/main.rs b/src/main.rs index 93a7ecd..442df27 100644 --- a/src/main.rs +++ b/src/main.rs @@ -128,9 +128,9 @@ async fn run_loop( let (notify_targets, _) = broadcast::channel(1); let listener = ActiveListener { - notify_shutdown: notify_shutdown, + notify_shutdown, targets: Arc::new(RwLock::new(target.targets)), - notify_targets: notify_targets, + notify_targets, }; let l = listener::Listener { diff --git a/src/tcp.rs b/src/tcp.rs index 7de4e96..db76552 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -1,5 +1,5 @@ use crate::listener::Listener; -use log::{info, trace, warn}; +use log::{error, info, trace, warn}; use rand::seq::SliceRandom; use std::error::Error; use tokio::net::{TcpListener, TcpStream}; @@ -24,7 +24,33 @@ pub(crate) async fn start_tcp_listener( mut listener_config: Listener, ) -> Result<(), Box> { info!("start listening on {}", &listener_config.source); - let listener = TcpListener::bind(&listener_config.source).await?; + let listener = loop { + match TcpListener::bind(&listener_config.source).await { + Ok(listener) => break listener, + Err(e) => match e.kind() { + std::io::ErrorKind::AddrInUse => { + warn!("Address in use: {}", &listener_config.source); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + } + std::io::ErrorKind::AddrNotAvailable => { + warn!("Address not available: {}", &listener_config.source); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + } + _ => { + error!( + "Error binding to {}: {} ({})", + &listener_config.source, + e, + e.kind() + ); + trace!("Error: {}", e); + return Err(Box::new(e)); + } + }, + } + }; + + info!("listening on {}", listener.local_addr()?); loop { let (next_socket, _) = tokio::select! { diff --git a/src/udp.rs b/src/udp.rs index 010bf7e..762b722 100644 --- a/src/udp.rs +++ b/src/udp.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::sync::atomic::AtomicI32; use std::sync::Arc; -use log::{debug, error, info, trace}; +use log::{debug, error, info, trace, warn}; use rand::seq::SliceRandom; use tokio::net::UdpSocket; @@ -38,7 +38,27 @@ impl UDPMultiSender { } async fn splitted_udp_socket(bind_addr: &str) -> Result<(UdpSocket, UDPMultiSender)> { - let listener_std = std::net::UdpSocket::bind(bind_addr)?; + let listener_std = loop { + match std::net::UdpSocket::bind(bind_addr) { + Ok(listener) => break listener, + Err(e) => match e.kind() { + std::io::ErrorKind::AddrInUse => { + warn!("Address in use: {}", bind_addr); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + } + std::io::ErrorKind::AddrNotAvailable => { + warn!("Address not available: {}", bind_addr); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + } + _ => { + error!("Error binding to {}: {} ({})", bind_addr, e, e.kind()); + trace!("Error: {}", e); + return Err(Box::new(e)); + } + }, + } + }; + let responder_std = listener_std.try_clone()?; listener_std.set_nonblocking(true)?; responder_std.set_nonblocking(true)?;