move rehash to its own coremod; reload-safe rehash (keeps running config on read failure) + snotice + servermask
This commit is contained in:
parent
dbdeb97392
commit
fceafa8064
4 changed files with 101 additions and 28 deletions
|
|
@ -106,9 +106,27 @@ impl Config {
|
||||||
conf_path: path.to_string(),
|
conf_path: path.to_string(),
|
||||||
..Config::default()
|
..Config::default()
|
||||||
};
|
};
|
||||||
let Ok(text) = std::fs::read_to_string(path) else {
|
if let Ok(text) = std::fs::read_to_string(path) {
|
||||||
return c;
|
Self::parse_into(&mut c, &text);
|
||||||
|
}
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like [`load`](Config::load) but returns `None` if the file can't be read,
|
||||||
|
/// so REHASH can keep the running config instead of resetting to defaults —
|
||||||
|
/// the way InspIRCd keeps the old config when a reload fails.
|
||||||
|
pub fn try_load(path: &str) -> Option<Config> {
|
||||||
|
let text = std::fs::read_to_string(path).ok()?;
|
||||||
|
let mut c = Config {
|
||||||
|
conf_path: path.to_string(),
|
||||||
|
..Config::default()
|
||||||
};
|
};
|
||||||
|
Self::parse_into(&mut c, &text);
|
||||||
|
Some(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse `key = value` lines into `c`; unknown keys and comments are ignored.
|
||||||
|
fn parse_into(c: &mut Config, text: &str) {
|
||||||
for line in text.lines() {
|
for line in text.lines() {
|
||||||
let line = line.trim();
|
let line = line.trim();
|
||||||
if line.is_empty() || line.starts_with('#') || line.starts_with(';') {
|
if line.is_empty() || line.starts_with('#') || line.starts_with(';') {
|
||||||
|
|
@ -204,6 +222,5 @@ impl Config {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
use crate::channels::Topic;
|
use crate::channels::Topic;
|
||||||
use crate::command::{CmdResult, Command};
|
use crate::command::{CmdResult, Command};
|
||||||
use crate::config::Config;
|
|
||||||
use crate::coremods::core_mode::apply_mode;
|
use crate::coremods::core_mode::apply_mode;
|
||||||
use crate::module::Hook;
|
use crate::module::Hook;
|
||||||
use crate::numeric::*;
|
use crate::numeric::*;
|
||||||
|
|
@ -19,7 +18,6 @@ pub fn commands() -> Vec<Box<dyn Command>> {
|
||||||
Box::new(Wallops),
|
Box::new(Wallops),
|
||||||
Box::new(SvsLogin),
|
Box::new(SvsLogin),
|
||||||
Box::new(SvsLogout),
|
Box::new(SvsLogout),
|
||||||
Box::new(Rehash),
|
|
||||||
Box::new(GlobOps),
|
Box::new(GlobOps),
|
||||||
Box::new(SaJoin),
|
Box::new(SaJoin),
|
||||||
Box::new(SaPart),
|
Box::new(SaPart),
|
||||||
|
|
@ -213,29 +211,6 @@ impl Command for Wallops {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// REHASH — reload the config file (MOTD, oper blocks, cloak key).
|
|
||||||
struct Rehash;
|
|
||||||
impl Command for Rehash {
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
"REHASH"
|
|
||||||
}
|
|
||||||
fn handle(&self, s: &mut Server, uid: Uid, _params: &[String]) -> CmdResult {
|
|
||||||
if !require_oper(s, uid) {
|
|
||||||
return CmdResult::Fail;
|
|
||||||
}
|
|
||||||
let fresh = Config::load(&s.conf_path);
|
|
||||||
s.motd = fresh.motd;
|
|
||||||
s.opers = fresh.opers;
|
|
||||||
s.cloak_key = fresh.cloak_key;
|
|
||||||
s.censor = fresh.censor;
|
|
||||||
s.amu = fresh.amu;
|
|
||||||
s.resolve_hosts = fresh.resolve_hosts;
|
|
||||||
s.use_resolved_host = fresh.use_resolved_host;
|
|
||||||
s.numeric(uid, RPL_REHASHING, &format!("{} :Rehashing", s.conf_path));
|
|
||||||
CmdResult::Ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// GLOBOPS — a message to every IRC operator.
|
/// GLOBOPS — a message to every IRC operator.
|
||||||
struct GlobOps;
|
struct GlobOps;
|
||||||
impl Command for GlobOps {
|
impl Command for GlobOps {
|
||||||
|
|
|
||||||
79
src/coremods/core_rehash.rs
Normal file
79
src/coremods/core_rehash.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
//! core_rehash — the REHASH command. Re-reads the config file and applies every
|
||||||
|
//! setting that can change at runtime, the way InspIRCd's rehash does:
|
||||||
|
//!
|
||||||
|
//! * opers only; replies with RPL_REHASHING (382) and a server-notice to +s opers;
|
||||||
|
//! * takes an optional `<servermask>` (we only rehash if it matches this server —
|
||||||
|
//! there's no remote-rehash over S2S yet);
|
||||||
|
//! * **keeps the running config if the file can't be read** (via `Config::try_load`),
|
||||||
|
//! so a REHASH of a deleted/renamed config never resets opers/cloak-key to defaults.
|
||||||
|
//!
|
||||||
|
//! Reloadable live: MOTD, oper blocks, cloak key, +G censor words, antimixedutf8,
|
||||||
|
//! and the reverse-DNS options. Listener/bind/SID changes still need a restart.
|
||||||
|
|
||||||
|
use crate::channels::glob_match;
|
||||||
|
use crate::command::{CmdResult, Command};
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::numeric::*;
|
||||||
|
use crate::server::Server;
|
||||||
|
use crate::Uid;
|
||||||
|
|
||||||
|
pub fn commands() -> Vec<Box<dyn Command>> {
|
||||||
|
vec![Box::new(Rehash)]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Rehash;
|
||||||
|
impl Command for Rehash {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"REHASH"
|
||||||
|
}
|
||||||
|
fn handle(&self, s: &mut Server, uid: Uid, params: &[String]) -> CmdResult {
|
||||||
|
if !s.is_oper(uid) {
|
||||||
|
s.numeric(
|
||||||
|
uid,
|
||||||
|
ERR_NOPRIVILEGES,
|
||||||
|
":Permission Denied- You're not an IRC operator",
|
||||||
|
);
|
||||||
|
return CmdResult::Fail;
|
||||||
|
}
|
||||||
|
// `REHASH <mask>` only rehashes servers matching the mask; we're one server.
|
||||||
|
if let Some(mask) = params.first() {
|
||||||
|
if !glob_match(mask, &s.name) {
|
||||||
|
s.numeric(uid, RPL_REHASHING, &format!("{mask} :No matching servers"));
|
||||||
|
return CmdResult::Ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let who = s
|
||||||
|
.users
|
||||||
|
.get(&uid)
|
||||||
|
.map(|u| u.nick.clone())
|
||||||
|
.unwrap_or_default();
|
||||||
|
let path = s.conf_path.clone();
|
||||||
|
match Config::try_load(&path) {
|
||||||
|
Some(fresh) => {
|
||||||
|
s.motd = fresh.motd;
|
||||||
|
s.opers = fresh.opers;
|
||||||
|
s.cloak_key = fresh.cloak_key;
|
||||||
|
s.censor = fresh.censor;
|
||||||
|
s.amu = fresh.amu;
|
||||||
|
s.resolve_hosts = fresh.resolve_hosts;
|
||||||
|
s.use_resolved_host = fresh.use_resolved_host;
|
||||||
|
s.numeric(uid, RPL_REHASHING, &format!("{path} :Rehashing"));
|
||||||
|
s.snotice(&format!("{who} is rehashing config: {path}"));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Unreadable config — keep what's running (do NOT reset to defaults).
|
||||||
|
s.send(
|
||||||
|
uid,
|
||||||
|
format!(
|
||||||
|
":{} NOTICE {who} :*** Cannot read {path}; keeping the running config",
|
||||||
|
s.name
|
||||||
|
),
|
||||||
|
);
|
||||||
|
s.snotice(&format!(
|
||||||
|
"{who} tried to REHASH but {path} could not be read; config unchanged"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CmdResult::Ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ pub mod core_info;
|
||||||
pub mod core_message;
|
pub mod core_message;
|
||||||
pub mod core_mode;
|
pub mod core_mode;
|
||||||
pub mod core_oper;
|
pub mod core_oper;
|
||||||
|
pub mod core_rehash;
|
||||||
pub mod core_user;
|
pub mod core_user;
|
||||||
pub mod core_watch;
|
pub mod core_watch;
|
||||||
|
|
||||||
|
|
@ -24,6 +25,7 @@ pub fn command_table() -> HashMap<&'static str, Box<dyn Command>> {
|
||||||
.chain(core_message::commands())
|
.chain(core_message::commands())
|
||||||
.chain(core_mode::commands())
|
.chain(core_mode::commands())
|
||||||
.chain(core_oper::commands())
|
.chain(core_oper::commands())
|
||||||
|
.chain(core_rehash::commands())
|
||||||
.chain(core_info::commands())
|
.chain(core_info::commands())
|
||||||
.chain(core_extra::commands())
|
.chain(core_extra::commands())
|
||||||
.chain(core_watch::commands())
|
.chain(core_watch::commands())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue