Browse Source

master: reload config with SIGUSR1

ipv6
Denis Drakhnia 1 year ago
parent
commit
4039b9fc43
  1. 20
      Cargo.lock
  2. 1
      master/Cargo.toml
  3. 4
      master/config/main.toml
  4. 5
      master/src/logger.rs
  5. 69
      master/src/main.rs
  6. 105
      master/src/master_server.rs

20
Cargo.lock generated

@ -262,6 +262,25 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.37" version = "2.0.37"
@ -491,6 +510,7 @@ dependencies = [
"log", "log",
"once_cell", "once_cell",
"serde", "serde",
"signal-hook",
"thiserror", "thiserror",
"toml", "toml",
"xash3d-protocol", "xash3d-protocol",

1
master/Cargo.toml

@ -19,6 +19,7 @@ fastrand = "2.0.1"
serde = { version = "1.0.188", features = ["derive"] } serde = { version = "1.0.188", features = ["derive"] }
toml = "0.5.11" toml = "0.5.11"
blake2b_simd = "<0.6" blake2b_simd = "<0.6"
signal-hook = { version = "0.3.17", default-features = false }
xash3d-protocol = { path = "../protocol", version = "0.1.0" } xash3d-protocol = { path = "../protocol", version = "0.1.0" }
[dependencies.chrono] [dependencies.chrono]

4
master/config/main.toml

@ -2,7 +2,7 @@
[log] [log]
# Possible values: 0-5, off, error, warn, info, debug, trace # Possible values: 0-5, off, error, warn, info, debug, trace
level = "warn" level = "info"
[server] [server]
ip = "0.0.0.0" ip = "0.0.0.0"
@ -21,7 +21,7 @@ admin = 10
version = "0.19" version = "0.19"
update_title = "https://github.com/FWGS/xash3d-fwgs" update_title = "https://github.com/FWGS/xash3d-fwgs"
update_map = "Update please" update_map = "Update please"
update_addr = "mentality.rip:27010" update_addr = "mentality.rip"
[hash] [hash]
len = 64 len = 64

5
master/src/logger.rs

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com> // SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
use log::{LevelFilter, Metadata, Record}; use log::{Metadata, Record};
struct Logger; struct Logger;
@ -30,9 +30,8 @@ impl log::Log for Logger {
static LOGGER: Logger = Logger; static LOGGER: Logger = Logger;
pub fn init(level_filter: LevelFilter) { pub fn init() {
if let Err(e) = log::set_logger(&LOGGER) { if let Err(e) = log::set_logger(&LOGGER) {
eprintln!("Failed to initialize logger: {}", e); eprintln!("Failed to initialize logger: {}", e);
} }
log::set_max_level(level_filter);
} }

69
master/src/main.rs

@ -6,35 +6,74 @@ mod config;
mod logger; mod logger;
mod master_server; mod master_server;
use log::error; use std::process;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
fn main() { use log::{error, info};
let cli = cli::parse().unwrap_or_else(|e| { use signal_hook::consts::signal::*;
eprintln!("{}", e); use signal_hook::flag as signal_flag;
std::process::exit(1);
});
let mut cfg = config::load(cli.config_path.as_ref()).unwrap_or_else(|e| { use crate::cli::Cli;
eprintln!("Failed to load config \"{}\": {}", cli.config_path, e); use crate::config::Config;
std::process::exit(1); use crate::master_server::{Error, MasterServer};
});
fn load_config(cli: &Cli) -> Result<Config, config::Error> {
let mut cfg = config::load(cli.config_path.as_ref())?;
if let Some(level) = cli.log_level { if let Some(level) = cli.log_level {
cfg.log.level = level; cfg.log.level = level;
} }
if let Some(ip) = cli.listen_ip { if let Some(ip) = cli.listen_ip {
cfg.server.ip = ip; cfg.server.ip = ip;
} }
if let Some(port) = cli.listen_port { if let Some(port) = cli.listen_port {
cfg.server.port = port; cfg.server.port = port;
} }
logger::init(cfg.log.level); log::set_max_level(cfg.log.level);
if let Err(e) = master_server::run(cfg) { Ok(cfg)
error!("{}", e); }
fn run() -> Result<(), Error> {
let cli = cli::parse().unwrap_or_else(|e| {
eprintln!("{}", e);
std::process::exit(1); std::process::exit(1);
});
logger::init();
let cfg = load_config(&cli).unwrap_or_else(|e| {
eprintln!("Failed to load config \"{}\": {}", cli.config_path, e);
process::exit(1);
});
let mut master = MasterServer::new(cfg)?;
let sig_flag = Arc::new(AtomicBool::new(false));
signal_flag::register(SIGUSR1, sig_flag.clone())?;
loop {
master.run(&sig_flag)?;
if sig_flag.swap(false, Ordering::Relaxed) {
info!("Reloading config from {}", cli.config_path);
match load_config(&cli) {
Ok(cfg) => {
if let Err(e) = master.update_config(cfg) {
error!("{}", e);
}
}
Err(e) => error!("failed to load config: {}", e),
}
}
}
}
fn main() {
if let Err(e) = run() {
error!("{}", e);
process::exit(1);
} }
} }

105
master/src/master_server.rs

@ -5,7 +5,8 @@ use std::collections::{HashMap, HashSet};
use std::io; use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs, UdpSocket}; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs, UdpSocket};
use std::ops::Deref; use std::ops::Deref;
use std::time::Instant; use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
use blake2b_simd::Params; use blake2b_simd::Params;
use fastrand::Rng; use fastrand::Rng;
@ -97,7 +98,7 @@ impl Counter {
} }
} }
struct MasterServer { pub struct MasterServer {
sock: UdpSocket, sock: UdpSocket,
challenges: HashMap<SocketAddrV4, Entry<u32>>, challenges: HashMap<SocketAddrV4, Entry<u32>>,
challenges_counter: Counter, challenges_counter: Counter,
@ -137,38 +138,36 @@ where
Ok(None) Ok(None)
} }
impl MasterServer { fn resolve_update_addr(cfg: &Config, local_addr: SocketAddr) -> SocketAddrV4 {
fn new(cfg: Config) -> Result<Self, Error> { if let Some(s) = cfg.client.update_addr.as_deref() {
let addr = SocketAddr::new(cfg.server.ip, cfg.server.port); let addr = if !s.contains(':') {
info!("Listen address: {}", addr); format!("{}:{}", s, local_addr.port())
let sock = UdpSocket::bind(addr).map_err(Error::BindSocket)?; } else {
s.to_owned()
let update_addr = { };
let mut addr = None;
if let Some(update_addr) = cfg.client.update_addr { match resolve_socket_addr(&addr) {
addr = match resolve_socket_addr(&*update_addr) { Ok(Some(x)) => return x,
Ok(None) => { Ok(None) => error!("Update address: failed to resolve IPv4 for \"{}\"", addr),
error!( Err(e) => error!("Update address: {}", e),
"update address: failed to resolve IPv4 for \"{}\"",
update_addr
);
None
} }
Err(e) => {
error!("update address: {}", e);
None
} }
Ok(addr) => addr,
match local_addr {
SocketAddr::V4(x) => x,
SocketAddr::V6(_) => todo!(),
}
} }
};
// fallback to local address impl MasterServer {
addr.unwrap_or_else(|| match sock.local_addr().unwrap() { pub fn new(cfg: Config) -> Result<Self, Error> {
SocketAddr::V4(a) => a, let addr = SocketAddr::new(cfg.server.ip, cfg.server.port);
_ => todo!(), info!("Listen address: {}", addr);
}) let sock = UdpSocket::bind(addr).map_err(Error::BindSocket)?;
}; // make socket interruptable by singals
sock.set_read_timeout(Some(Duration::from_secs(u32::MAX as u64)))?;
let update_addr = resolve_update_addr(&cfg, addr);
Ok(Self { Ok(Self {
sock, sock,
@ -193,10 +192,40 @@ impl MasterServer {
}) })
} }
fn run(&mut self) -> Result<(), Error> { pub fn update_config(&mut self, cfg: Config) -> Result<(), Error> {
let local_addr = self.sock.local_addr()?;
let addr = SocketAddr::new(cfg.server.ip, cfg.server.port);
if local_addr != addr {
info!("Listen address: {}", addr);
self.sock = UdpSocket::bind(addr).map_err(Error::BindSocket)?;
// make socket interruptable by singals
self.sock
.set_read_timeout(Some(Duration::from_secs(u32::MAX as u64)))?;
self.clear();
}
self.update_addr = resolve_update_addr(&cfg, addr);
self.timeout = cfg.server.timeout;
self.clver = cfg.client.version;
self.update_title = cfg.client.update_title;
self.update_map = cfg.client.update_map;
self.admin_list = cfg.admin_list;
self.hash = cfg.hash;
Ok(())
}
pub fn run(&mut self, sig_flag: &AtomicBool) -> Result<(), Error> {
let mut buf = [0; MAX_PACKET_SIZE]; let mut buf = [0; MAX_PACKET_SIZE];
loop { while !sig_flag.load(Ordering::Relaxed) {
let (n, from) = self.sock.recv_from(&mut buf)?; let (n, from) = match self.sock.recv_from(&mut buf) {
Ok(x) => x,
Err(e) => match e.kind() {
io::ErrorKind::Interrupted => break,
io::ErrorKind::TimedOut | io::ErrorKind::WouldBlock => continue,
_ => Err(e)?,
},
};
let from = match from { let from = match from {
SocketAddr::V4(a) => a, SocketAddr::V4(a) => a,
@ -210,6 +239,14 @@ impl MasterServer {
error!("{}: {}", from, e); error!("{}: {}", from, e);
} }
} }
Ok(())
}
fn clear(&mut self) {
info!("Clear all servers and challenges");
self.challenges.clear();
self.servers.clear();
self.admin_challenges.clear();
} }
fn handle_packet(&mut self, from: SocketAddrV4, src: &[u8]) -> Result<(), Error> { fn handle_packet(&mut self, from: SocketAddrV4, src: &[u8]) -> Result<(), Error> {
@ -505,7 +542,3 @@ impl MasterServer {
} }
} }
} }
pub fn run(cfg: Config) -> Result<(), Error> {
MasterServer::new(cfg)?.run()
}

Loading…
Cancel
Save