master: reload config with SIGUSR1

This commit is contained in:
Denis Drakhnia 2023-11-02 17:07:04 +02:00
parent e30d1c2a65
commit 4039b9fc43
6 changed files with 151 additions and 59 deletions

20
Cargo.lock generated
View File

@ -262,6 +262,25 @@ dependencies = [
"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]]
name = "syn"
version = "2.0.37"
@ -491,6 +510,7 @@ dependencies = [
"log",
"once_cell",
"serde",
"signal-hook",
"thiserror",
"toml",
"xash3d-protocol",

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,8 @@ use std::collections::{HashMap, HashSet};
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs, UdpSocket};
use std::ops::Deref;
use std::time::Instant;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
use blake2b_simd::Params;
use fastrand::Rng;
@ -97,7 +98,7 @@ impl Counter {
}
}
struct MasterServer {
pub struct MasterServer {
sock: UdpSocket,
challenges: HashMap<SocketAddrV4, Entry<u32>>,
challenges_counter: Counter,
@ -137,38 +138,36 @@ where
Ok(None)
}
fn resolve_update_addr(cfg: &Config, local_addr: SocketAddr) -> SocketAddrV4 {
if let Some(s) = cfg.client.update_addr.as_deref() {
let addr = if !s.contains(':') {
format!("{}:{}", s, local_addr.port())
} else {
s.to_owned()
};
match resolve_socket_addr(&addr) {
Ok(Some(x)) => return x,
Ok(None) => error!("Update address: failed to resolve IPv4 for \"{}\"", addr),
Err(e) => error!("Update address: {}", e),
}
}
match local_addr {
SocketAddr::V4(x) => x,
SocketAddr::V6(_) => todo!(),
}
}
impl MasterServer {
fn new(cfg: Config) -> Result<Self, Error> {
pub fn new(cfg: Config) -> Result<Self, Error> {
let addr = SocketAddr::new(cfg.server.ip, cfg.server.port);
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 = {
let mut addr = None;
if let Some(update_addr) = cfg.client.update_addr {
addr = match resolve_socket_addr(&*update_addr) {
Ok(None) => {
error!(
"update address: failed to resolve IPv4 for \"{}\"",
update_addr
);
None
}
Err(e) => {
error!("update address: {}", e);
None
}
Ok(addr) => addr,
}
};
// fallback to local address
addr.unwrap_or_else(|| match sock.local_addr().unwrap() {
SocketAddr::V4(a) => a,
_ => todo!(),
})
};
let update_addr = resolve_update_addr(&cfg, addr);
Ok(Self {
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];
loop {
let (n, from) = self.sock.recv_from(&mut buf)?;
while !sig_flag.load(Ordering::Relaxed) {
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 {
SocketAddr::V4(a) => a,
@ -210,6 +239,14 @@ impl MasterServer {
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> {
@ -505,7 +542,3 @@ impl MasterServer {
}
}
}
pub fn run(cfg: Config) -> Result<(), Error> {
MasterServer::new(cfg)?.run()
}