mirror of
https://github.com/timvisee/lazymc.git
synced 2025-05-19 12:50:23 -07:00
164 lines
5.4 KiB
Rust
164 lines
5.4 KiB
Rust
use std::path::Path;
|
|
use std::sync::mpsc::channel;
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
|
|
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
|
|
|
|
use crate::config::{Config, Server as ConfigServer};
|
|
use crate::mc::ban::{self, BannedIps};
|
|
use crate::mc::whitelist;
|
|
use crate::server::Server;
|
|
|
|
/// File watcher debounce time.
|
|
const WATCH_DEBOUNCE: Duration = Duration::from_secs(2);
|
|
|
|
/// Service to watch server file changes.
|
|
pub fn service(config: Arc<Config>, server: Arc<Server>) {
|
|
// Ensure server directory is set, it must exist
|
|
let dir = match ConfigServer::server_directory(&config) {
|
|
Some(dir) if dir.is_dir() => dir,
|
|
_ => {
|
|
warn!(target: "lazymc", "Server directory doesn't exist, can't watch file changes to reload whitelist and banned IPs");
|
|
return;
|
|
}
|
|
};
|
|
|
|
// Keep watching
|
|
#[allow(clippy::blocks_in_if_conditions)]
|
|
while {
|
|
// Update all files once
|
|
reload_bans(&config, &server, &dir.join(ban::FILE));
|
|
reload_whitelist(&config, &server, &dir);
|
|
|
|
// Watch for changes, update accordingly
|
|
watch_server(&config, &server, &dir)
|
|
} {}
|
|
}
|
|
|
|
/// Watch server directory.
|
|
///
|
|
/// Returns `true` if we should watch again.
|
|
#[must_use]
|
|
fn watch_server(config: &Config, server: &Server, dir: &Path) -> bool {
|
|
// Directory must exist
|
|
if !dir.is_dir() {
|
|
error!(target: "lazymc", "Server directory does not exist at {} anymore, not watching changes", dir.display());
|
|
return false;
|
|
}
|
|
|
|
// Create watcher for directory
|
|
let (tx, rx) = channel();
|
|
let mut watcher =
|
|
watcher(tx, WATCH_DEBOUNCE).expect("failed to create watcher for banned-ips.json");
|
|
if let Err(err) = watcher.watch(dir, RecursiveMode::NonRecursive) {
|
|
error!(target: "lazymc", "An error occured while creating watcher for server files: {}", err);
|
|
return true;
|
|
}
|
|
|
|
// Handle change events
|
|
loop {
|
|
match rx.recv().unwrap() {
|
|
// Handle file updates
|
|
DebouncedEvent::Create(ref path)
|
|
| DebouncedEvent::Write(ref path)
|
|
| DebouncedEvent::Remove(ref path) => {
|
|
update(config, server, dir, path);
|
|
}
|
|
|
|
// Handle file updates on both paths for rename
|
|
DebouncedEvent::Rename(ref before_path, ref after_path) => {
|
|
update(config, server, dir, before_path);
|
|
update(config, server, dir, after_path);
|
|
}
|
|
|
|
// Ignore write/remove notices, will receive write/remove event later
|
|
DebouncedEvent::NoticeWrite(_) | DebouncedEvent::NoticeRemove(_) => {}
|
|
|
|
// Ignore chmod changes
|
|
DebouncedEvent::Chmod(_) => {}
|
|
|
|
// Rewatch on rescan
|
|
DebouncedEvent::Rescan => {
|
|
debug!(target: "lazymc", "Rescanning server directory files due to file watching problem");
|
|
return true;
|
|
}
|
|
|
|
// Rewatch on error
|
|
DebouncedEvent::Error(err, _) => {
|
|
error!(target: "lazymc", "Error occurred while watching server directory for file changes: {}", err);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Process a file change on the given path.
|
|
///
|
|
/// Should be called both when created, changed or removed.
|
|
fn update(config: &Config, server: &Server, dir: &Path, path: &Path) {
|
|
// Update bans
|
|
if path.ends_with(ban::FILE) {
|
|
reload_bans(config, server, path);
|
|
}
|
|
|
|
// Update whitelist
|
|
if path.ends_with(whitelist::WHITELIST_FILE) || path.ends_with(whitelist::OPS_FILE) {
|
|
reload_whitelist(config, server, dir);
|
|
}
|
|
|
|
// TODO: update on server.properties change
|
|
}
|
|
|
|
/// Reload banned IPs.
|
|
fn reload_bans(config: &Config, server: &Server, path: &Path) {
|
|
// Bans must be enabled
|
|
if !config.server.block_banned_ips && !config.server.drop_banned_ips {
|
|
return;
|
|
}
|
|
|
|
trace!(target: "lazymc", "Reloading banned IPs...");
|
|
|
|
// File must exist, clear file otherwise
|
|
if !path.is_file() {
|
|
debug!(target: "lazymc", "No banned IPs, {} does not exist", ban::FILE);
|
|
// warn!(target: "lazymc", "Not blocking banned IPs, {} file does not exist", ban::FILE);
|
|
server.set_banned_ips_blocking(BannedIps::default());
|
|
return;
|
|
}
|
|
|
|
// Load and update banned IPs
|
|
match ban::load(path) {
|
|
Ok(ips) => server.set_banned_ips_blocking(ips),
|
|
Err(err) => {
|
|
debug!(target: "lazymc", "Failed load banned IPs from {}, ignoring: {}", ban::FILE, err);
|
|
}
|
|
}
|
|
|
|
// Show warning if 127.0.0.1 is banned
|
|
if server.is_banned_ip_blocking(&("127.0.0.1".parse().unwrap())) {
|
|
warn!(target: "lazymc", "Local address 127.0.0.1 IP banned, probably not what you want");
|
|
warn!(target: "lazymc", "Use '/pardon-ip 127.0.0.1' on the server to unban");
|
|
}
|
|
}
|
|
|
|
/// Reload whitelisted users.
|
|
fn reload_whitelist(config: &Config, server: &Server, dir: &Path) {
|
|
// Whitelist must be enabled
|
|
if !config.server.wake_whitelist {
|
|
return;
|
|
}
|
|
|
|
// TODO: whitelist must be enabled in server.properties
|
|
|
|
trace!(target: "lazymc", "Reloading whitelisted users...");
|
|
|
|
// Load and update whitelisted users
|
|
match whitelist::load_dir(dir) {
|
|
Ok(whitelist) => server.set_whitelist_blocking(Some(whitelist)),
|
|
Err(err) => {
|
|
debug!(target: "lazymc", "Failed load whitelist from {}, ignoring: {}", dir.display(), err);
|
|
}
|
|
}
|
|
}
|