syslog: tee the server-notice/log stream to syslog (native /dev/log unix datagram or udp host:port)
This commit is contained in:
parent
56d3774647
commit
5ea543188d
4 changed files with 115 additions and 0 deletions
|
|
@ -130,6 +130,12 @@ amu_target = both
|
||||||
# "nick!user@host realname" matches a regex (native engine; no config to enable).
|
# "nick!user@host realname" matches a regex (native engine; no config to enable).
|
||||||
# rline_matchonnickchange = yes # also re-check the R-lines when a user changes nick
|
# rline_matchonnickchange = yes # also re-check the R-lines when a user changes nick
|
||||||
|
|
||||||
|
# --- syslog: mirror the server-notice / log stream to the system logger ---
|
||||||
|
# syslog = yes
|
||||||
|
# syslog_target = /dev/log # a Unix socket path, or host:port for UDP
|
||||||
|
# syslog_facility = daemon # kern user mail daemon auth ... local0..local7
|
||||||
|
# syslog_tag = echoircd
|
||||||
|
|
||||||
# --- security groups: securitygroup = <name> [criteria...]
|
# --- security groups: securitygroup = <name> [criteria...]
|
||||||
# criteria: public tls insecure account unregistered oper exclude-oper
|
# criteria: public tls insecure account unregistered oper exclude-oper
|
||||||
# bot exclude-bot webirc exclude-webirc mask=<glob> exclude=<glob>
|
# bot exclude-bot webirc exclude-webirc mask=<glob> exclude=<glob>
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ pub mod serverban;
|
||||||
pub mod showfile;
|
pub mod showfile;
|
||||||
pub mod solvemsg;
|
pub mod solvemsg;
|
||||||
pub mod snoop;
|
pub mod snoop;
|
||||||
|
pub mod syslog;
|
||||||
pub mod tline;
|
pub mod tline;
|
||||||
pub mod whoisport;
|
pub mod whoisport;
|
||||||
|
|
||||||
|
|
|
||||||
107
src/modules/syslog.rs
Normal file
107
src/modules/syslog.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
//! syslog — mirror the server-notice / log stream to the system logger. Off unless
|
||||||
|
//! `syslog = yes`. Sends to a local Unix datagram socket (`syslog_target`, default
|
||||||
|
//! `/dev/log`) or, when the target looks like `host:port`, over UDP. Messages are
|
||||||
|
//! RFC 3164 `<PRI>TAG[pid]: msg`; the receiving daemon stamps the time.
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! syslog = yes
|
||||||
|
//! syslog_target = /dev/log # or e.g. 10.0.0.5:514 for a remote collector
|
||||||
|
//! syslog_facility = daemon # kern user mail daemon auth ... local0..local7
|
||||||
|
//! syslog_tag = echoircd
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The socket is cached per target on the core thread (snotice is single-threaded),
|
||||||
|
//! reopened only when the configured target changes.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::net::UdpSocket;
|
||||||
|
use std::os::unix::net::UnixDatagram;
|
||||||
|
|
||||||
|
use crate::server::Server;
|
||||||
|
|
||||||
|
/// An opened syslog transport.
|
||||||
|
enum Sink {
|
||||||
|
Unix(UnixDatagram),
|
||||||
|
Udp(UdpSocket, String), // socket + "host:port" destination
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
/// (target-string, sink) cached for the current config; reopened on change.
|
||||||
|
static SINK: RefCell<Option<(String, Sink)>> = const { RefCell::new(None) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Syslog facility name → numeric code (RFC 3164 §4.1.1).
|
||||||
|
fn facility(name: &str) -> u8 {
|
||||||
|
match name.to_ascii_lowercase().as_str() {
|
||||||
|
"kern" => 0,
|
||||||
|
"user" => 1,
|
||||||
|
"mail" => 2,
|
||||||
|
"auth" => 4,
|
||||||
|
"syslog" => 5,
|
||||||
|
"lpr" => 6,
|
||||||
|
"news" => 7,
|
||||||
|
"uucp" => 8,
|
||||||
|
"cron" => 9,
|
||||||
|
"authpriv" => 10,
|
||||||
|
"ftp" => 11,
|
||||||
|
"local0" => 16,
|
||||||
|
"local1" => 17,
|
||||||
|
"local2" => 18,
|
||||||
|
"local3" => 19,
|
||||||
|
"local4" => 20,
|
||||||
|
"local5" => 21,
|
||||||
|
"local6" => 22,
|
||||||
|
"local7" => 23,
|
||||||
|
_ => 3, // daemon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open the transport for `target` (`host:port` ⇒ UDP, else a Unix datagram path).
|
||||||
|
fn open(target: &str) -> Option<Sink> {
|
||||||
|
if target.contains(':') && !target.starts_with('/') {
|
||||||
|
let sock = UdpSocket::bind("0.0.0.0:0")
|
||||||
|
.or_else(|_| UdpSocket::bind("[::]:0"))
|
||||||
|
.ok()?;
|
||||||
|
Some(Sink::Udp(sock, target.to_string()))
|
||||||
|
} else {
|
||||||
|
let sock = UnixDatagram::unbound().ok()?;
|
||||||
|
sock.connect(target).ok()?;
|
||||||
|
Some(Sink::Unix(sock))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tee `msg` to syslog when enabled. Called at the tail of [`Server::snotice`].
|
||||||
|
pub fn tee(s: &Server, msg: &str) {
|
||||||
|
if !s.conf_bool("syslog", false) {
|
||||||
|
SINK.with(|c| *c.borrow_mut() = None); // dropped/disabled: forget any socket
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let target = s
|
||||||
|
.conf("syslog_target")
|
||||||
|
.map(str::to_string)
|
||||||
|
.unwrap_or_else(|| "/dev/log".to_string());
|
||||||
|
let tag = s.conf("syslog_tag").unwrap_or("echoircd");
|
||||||
|
// severity "notice" (5); PRI = facility*8 + severity
|
||||||
|
let pri = facility(s.conf("syslog_facility").unwrap_or("daemon")) as u16 * 8 + 5;
|
||||||
|
let line = format!("<{pri}>{tag}[{}]: {msg}", std::process::id());
|
||||||
|
SINK.with(|cell| {
|
||||||
|
let mut slot = cell.borrow_mut();
|
||||||
|
// (re)open if the target changed or nothing is open yet
|
||||||
|
let need_open = match slot.as_ref() {
|
||||||
|
Some((t, _)) => t != &target,
|
||||||
|
None => true,
|
||||||
|
};
|
||||||
|
if need_open {
|
||||||
|
*slot = open(&target).map(|sink| (target.clone(), sink));
|
||||||
|
}
|
||||||
|
if let Some((_, sink)) = slot.as_ref() {
|
||||||
|
let ok = match sink {
|
||||||
|
Sink::Unix(sock) => sock.send(line.as_bytes()).is_ok(),
|
||||||
|
Sink::Udp(sock, dst) => sock.send_to(line.as_bytes(), dst).is_ok(),
|
||||||
|
};
|
||||||
|
if !ok {
|
||||||
|
*slot = None; // transient failure: drop so we reopen next time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -757,6 +757,7 @@ impl Server {
|
||||||
self.deliver_server_notice(o, msg, &jval);
|
self.deliver_server_notice(o, msg, &jval);
|
||||||
}
|
}
|
||||||
crate::modules::chanlog::tee(self, msg);
|
crate::modules::chanlog::tee(self, msg);
|
||||||
|
crate::modules::syslog::tee(self, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Broadcast a `*** msg` server NOTICE to *every* registered local user — for
|
/// Broadcast a `*** msg` server NOTICE to *every* registered local user — for
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue