use std::{borrow::Cow, fmt, ops::{BitAnd, Shl}, path::Path, sync::LazyLock};
use crate::{error::{SyRes, SyslogError}, map_error_code, portable, throw_error};
#[cfg(target_family = "windows")]
pub use self::common_eventlog_items::*;
#[cfg(target_family = "unix")]
pub use self::common_syslog_items::*;
#[cfg(target_family = "windows")]
pub mod common_eventlog_items
{
use std::fmt;
use windows::Win32::System::EventLog::{EVENTLOG_ERROR_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_SUCCESS, EVENTLOG_WARNING_TYPE, REPORT_EVENT_TYPE};
use crate::{error::SyRes, throw_error};
#[allow(nonstandard_style)]
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Priority
{
LOG_EMERG = 0,
LOG_ALERT = 1,
LOG_CRIT = 2,
LOG_ERR = 3,
LOG_WARNING = 4,
LOG_NOTICE = 5,
LOG_INFO = 6,
LOG_DEBUG = 7,
}
impl fmt::Display for Priority
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self
{
Self::LOG_EMERG =>
write!(f, "[EMERG]"),
Self::LOG_ALERT =>
write!(f, "[ALERT]"),
Self::LOG_CRIT =>
write!(f, "[CRIT]"),
Self::LOG_ERR =>
write!(f, "[ERR]"),
Self::LOG_WARNING =>
write!(f, "[WARNING]"),
Self::LOG_NOTICE =>
write!(f, "[NOTICE]"),
Self::LOG_INFO =>
write!(f, "[INFO]"),
Self::LOG_DEBUG =>
write!(f, "[DEBUG]"),
}
}
}
impl From<Priority> for REPORT_EVENT_TYPE
{
fn from(value: Priority) -> Self
{
match value
{
Priority::LOG_EMERG | Priority::LOG_ALERT | Priority::LOG_CRIT =>
return EVENTLOG_WARNING_TYPE,
Priority::LOG_ERR =>
return EVENTLOG_ERROR_TYPE,
Priority::LOG_WARNING | Priority::LOG_NOTICE =>
return EVENTLOG_INFORMATION_TYPE,
Priority::LOG_INFO | Priority::LOG_DEBUG =>
return EVENTLOG_SUCCESS
}
}
}
impl Priority
{
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LogStat: i32
{
const LOG_PID = 1;
const LOG_CONS = 2;
const LOG_ODELAY = 0;
const LOG_NDELAY = 0;
const LOG_NOWAIT = 0;
const LOG_PERROR = 0x20;
}
}
#[cfg(feature = "build_sync")]
impl LogStat
{
#[inline]
pub(crate)
fn send_to_stderr(&self, msg: &str)
{
if self.intersects(LogStat::LOG_PERROR) == true
{
eprintln!("{}", msg);
}
}
#[inline]
pub(crate)
fn send_to_syscons(&self, msg_payload: &str)
{
if self.intersects(LogStat::LOG_CONS)
{
eprintln!("{}", msg_payload);
}
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LogFacility: i32
{
const LOG_KERN = 0;
const LOG_USER = 8;
const LOG_MAIL = 16;
const LOG_DAEMON = 24;
const LOG_AUTH = 32;
const LOG_SYSLOG = 40;
const LOG_LPR = 48;
const LOG_NEWS = 56;
const LOG_UUCP = 64;
const LOG_LOCAL0 = 128;
const LOG_LOCAL1 = 136;
const LOG_LOCAL2 = 144;
const LOG_LOCAL3 = 152;
const LOG_LOCAL4 = 160;
const LOG_LOCAL5 = 168;
const LOG_LOCAL6 = 176;
const LOG_LOCAL7 = 184;
}
}
impl LogFacility
{
pub
fn into_win_facility(self) -> u32
{
return self.bits() as u32 >> 3;
}
}
#[cfg(test)]
mod tests
{
use windows::Win32::System::EventLog::{EVENTLOG_ERROR_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_SUCCESS, EVENTLOG_WARNING_TYPE, REPORT_EVENT_TYPE};
use crate::Priority;
#[test]
fn test_conversion_prio_to_ret()
{
assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_EMERG), EVENTLOG_WARNING_TYPE);
assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_ALERT), EVENTLOG_WARNING_TYPE);
assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_CRIT), EVENTLOG_WARNING_TYPE);
assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_ERR), EVENTLOG_ERROR_TYPE);
assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_WARNING), EVENTLOG_INFORMATION_TYPE);
assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_NOTICE), EVENTLOG_INFORMATION_TYPE);
assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_INFO), EVENTLOG_SUCCESS);
assert_eq!(REPORT_EVENT_TYPE::from(Priority::LOG_DEBUG), EVENTLOG_SUCCESS);
}
}
}
#[cfg(target_family = "unix")]
pub mod common_syslog_items
{
use nix::libc;
use std::fmt;
use crate::error::SyRes;
use super::throw_error;
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LogStat: libc::c_int
{
const LOG_PID = libc::LOG_PID;
const LOG_CONS = libc::LOG_CONS;
const LOG_ODELAY = libc::LOG_ODELAY;
const LOG_NDELAY = libc::LOG_NDELAY;
const LOG_NOWAIT = libc::LOG_NOWAIT;
const LOG_PERROR = 0x20;
}
}
#[cfg(feature = "build_sync")]
impl LogStat
{
#[inline]
pub(crate)
fn send_to_stderr(&self, msg: &str)
{
if self.intersects(LogStat::LOG_PERROR) == true
{
let stderr_lock = std::io::stderr().lock();
let newline = "\n";
let _ = send_to_fd(stderr_lock, msg, &newline);
}
}
#[inline]
pub(crate)
fn send_to_syscons(&self, msg_payload: &str)
{
use std::fs::File;
use std::os::unix::fs::OpenOptionsExt;
if self.intersects(LogStat::LOG_CONS)
{
use crate::PATH_CONSOLE;
let syscons =
File
::options()
.create(false)
.read(false)
.write(true)
.custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
.open(*PATH_CONSOLE);
if let Ok(file) = syscons
{
let newline = "\n";
let _ = send_to_fd(file, msg_payload, newline);
}
}
}
}
#[allow(nonstandard_style)]
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Priority
{
LOG_EMERG = libc::LOG_EMERG,
LOG_ALERT = libc::LOG_ALERT,
LOG_CRIT = libc::LOG_CRIT,
LOG_ERR = libc::LOG_ERR,
LOG_WARNING = libc::LOG_WARNING,
LOG_NOTICE = libc::LOG_NOTICE,
LOG_INFO = libc::LOG_INFO,
LOG_DEBUG = libc::LOG_DEBUG,
}
impl fmt::Display for Priority
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self
{
Self::LOG_EMERG =>
write!(f, "[EMERG]"),
Self::LOG_ALERT =>
write!(f, "[ALERT]"),
Self::LOG_CRIT =>
write!(f, "[CRIT]"),
Self::LOG_ERR =>
write!(f, "[ERR]"),
Self::LOG_WARNING =>
write!(f, "[WARNING]"),
Self::LOG_NOTICE =>
write!(f, "[NOTICE]"),
Self::LOG_INFO =>
write!(f, "[INFO]"),
Self::LOG_DEBUG =>
write!(f, "[DEBUG]"),
}
}
}
impl Priority
{
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LogFacility: libc::c_int
{
const LOG_KERN = libc::LOG_KERN;
const LOG_USER = libc::LOG_USER;
const LOG_MAIL = libc::LOG_MAIL;
const LOG_DAEMON = libc::LOG_DAEMON;
const LOG_AUTH = libc::LOG_AUTH;
const LOG_SYSLOG = libc::LOG_SYSLOG;
const LOG_LPR = libc::LOG_LPR;
const LOG_NEWS = libc::LOG_NEWS;
const LOG_UUCP = libc::LOG_UUCP;
const LOG_LOCAL0 = libc::LOG_LOCAL0;
const LOG_LOCAL1 = libc::LOG_LOCAL1;
const LOG_LOCAL2 = libc::LOG_LOCAL2;
const LOG_LOCAL3 = libc::LOG_LOCAL3;
const LOG_LOCAL4 = libc::LOG_LOCAL4;
const LOG_LOCAL5 = libc::LOG_LOCAL5;
const LOG_LOCAL6 = libc::LOG_LOCAL6;
const LOG_LOCAL7 = libc::LOG_LOCAL7;
}
}
pub const PATH_LOG: &'static str = "/var/run/log";
pub const PATH_LOG_PRIV: &'static str = "/var/run/logpriv";
pub const PATH_OLDLOG: &'static str = "/dev/log";
pub const PATH_OSX: &'static str = "/var/run/syslog";
#[cfg(feature = "build_sync")]
pub(crate) mod sync_portion
{
use std::io::Write;
use std::io::IoSlice;
use crate::error::SyRes;
use crate::map_error_os;
pub(crate)
fn send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
where W: Write
{
return
file_fd
.write_vectored(
&[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())]
)
.map_err(|e|
map_error_os!(e, "send_to_fd() writev() failed")
);
}
}
#[cfg(feature = "build_sync")]
pub(crate) use self::sync_portion::*;
#[cfg(test)]
mod tests
{
use super::*;
#[cfg(feature = "build_sync")]
#[test]
fn test_error_message()
{
let testmsg = "this is test message!";
let newline = "\n";
let stderr_lock = std::io::stderr().lock();
let res = send_to_fd(stderr_lock, testmsg, &newline);
println!("res: {:?}", res);
assert_eq!(res.is_ok(), true, "err: {}", res.err().unwrap());
return;
}
#[test]
fn test_priority_shl()
{
assert_eq!((1 << 5), (1 << Priority::LOG_NOTICE));
}
}
}
pub static PATH_CONSOLE: LazyLock<&Path> = LazyLock::new(||
{
Path::new("/dev/console")
}
);
pub static RFC5424_MAX_DGRAM: LazyLock<usize> = LazyLock::new(||
{
portable::get_local_dgram_maxdgram() as usize
}
);
pub const MAXHOSTNAMELEN: usize = 256;
pub const LOG_FACMASK: i32 = 0x03f8;
pub const MAXLINE: usize = 8192;
pub const RFC3164_MAX_PAYLOAD_LEN: usize = 1024;
pub const WINDOWS_EVENT_REPORT_MAX_PAYLOAD_LEN: usize = 31839;
#[cfg(all(feature = "udp_truncate_1024_bytes", feature = "udp_truncate_1440_bytes"))]
compile_error!("either 'udp_truncate_1024_bytes' or 'udp_truncate_1440_bytes' should be enabled");
#[cfg(feature = "udp_truncate_1024_bytes")]
pub const RFC5424_UDP_MAX_PKT_LEN: usize = 1024;
#[cfg(any(feature = "udp_truncate_1440_bytes", all(not(feature = "udp_truncate_1440_bytes"), not(feature = "udp_truncate_1024_bytes"))))]
pub const RFC5424_UDP_MAX_PKT_LEN: usize = 2048;
#[cfg(feature = "tcp_truncate_1024_bytes")]
pub const RFC5424_TCP_MAX_PKT_LEN: usize = 1024;
#[cfg(feature = "tcp_truncate_2048_bytes")]
pub const RFC5424_TCP_MAX_PKT_LEN: usize = 2048;
#[cfg(feature = "tcp_truncate_4096_bytes")]
pub const RFC5424_TCP_MAX_PKT_LEN: usize = 4096;
#[cfg(feature = "tcp_truncate_max_bytes")]
pub const RFC5424_TCP_MAX_PKT_LEN: usize = MAXLINE;
pub const RFC_MAX_APP_NAME: usize = 48;
pub const IANA_PRIV_ENT_NUM: u64 = 32473;
pub const NILVALUE: &'static str = "-";
pub const ESC_CHAR_REPL: &'static str = "#000";
pub const NILVALUE_B: &'static [u8] = b"-";
pub const WSPACE: &'static str = " ";
pub const OBRACE: &'static str = "[";
pub const CBRACE: &'static str = "]";
pub const CBRACE_SEM: &'static str = "]:";
pub const QCHAR: &'static str = "\"";
pub const ATSIGN: &'static str = "@";
pub const EQSIGN: &'static str = "=";
pub const NEXTLINE: &'static str = "\n";
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct LogMask: i32
{
const LOG_FACMASK = 0x3f8;
const LOG_PRIMASK = 7;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SyslogMsgPriFac(i32);
impl fmt::Display for SyslogMsgPriFac
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.0)
}
}
impl TryFrom<i32> for SyslogMsgPriFac
{
type Error = SyslogError;
fn try_from(value: i32) -> Result<Self, Self::Error>
{
if (value & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
{
throw_error!("unknwon facility/priority: {:x}", value);
}
return Ok(Self(value));
}
}
impl TryFrom<&[u8]> for SyslogMsgPriFac
{
type Error = SyslogError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error>
{
let val =
str::from_utf8(value)
.map_or(
None,
|pri_str|
{
i32::from_str_radix(pri_str, 10)
.map_or(
None,
|val|
Some(val & LogMask::LOG_PRIMASK | val & LogMask::LOG_FACMASK.bits())
)
}
)
.ok_or(
map_error_code!(InternalError, "cannot convert to prio|facility")
)?;
if (val & !(LogMask::LOG_PRIMASK | LogMask::LOG_FACMASK )) != 0
{
throw_error!("unknwon facility/priority: {:x}", val);
}
return Ok(Self(val));
}
}
impl SyslogMsgPriFac
{
pub(crate)
fn set_facility(p: Priority, f: LogFacility) -> Self
{
return Self( p as i32 | f.bits() );
}
pub
fn get_val(&self) -> i32
{
return self.0;
}
pub
fn get_priority(&self) -> Priority
{
return Priority::from(self.0 & LogMask::LOG_PRIMASK);
}
pub
fn get_log_facility(&self) -> LogFacility
{
return LogFacility::from_bits_retain(self.0 & LogMask::LOG_FACMASK);
}
}
impl From<i32> for Priority
{
fn from(value: i32) -> Self
{
if value == Priority::LOG_ALERT as i32
{
return Priority::LOG_ALERT;
}
else if value == Priority::LOG_CRIT as i32
{
return Priority::LOG_CRIT;
}
else if value == Priority::LOG_DEBUG as i32
{
return Priority::LOG_DEBUG;
}
else if value == Priority::LOG_EMERG as i32
{
return Priority::LOG_EMERG;
}
else if value == Priority::LOG_ERR as i32
{
return Priority::LOG_ERR;
}
else if value == Priority::LOG_INFO as i32
{
return Priority::LOG_INFO;
}
else if value == Priority::LOG_NOTICE as i32
{
return Priority::LOG_NOTICE;
}
else
{
return Priority::LOG_WARNING;
}
}
}
#[macro_export]
macro_rules! LOG_MASK
{
($arg:expr) => (
(1 << ($arg))
)
}
#[macro_export]
macro_rules! LOG_UPTO
{
($arg:expr) => (
((1 << (($arg) + 1)) - 1)
)
}
impl Shl<Priority> for i32
{
type Output = i32;
fn shl(self, rhs: Priority) -> i32
{
let lhs = self;
return lhs << rhs as i32;
}
}
impl BitAnd<Priority> for i32
{
type Output = i32;
#[inline]
fn bitand(self, rhs: Priority) -> i32
{
return self & rhs as i32;
}
}
impl BitAnd<LogMask> for Priority
{
type Output = i32;
#[inline]
fn bitand(self, rhs: LogMask) -> i32
{
return self as i32 & rhs.bits();
}
}
impl BitAnd<LogMask> for LogFacility
{
type Output = LogFacility;
#[inline]
fn bitand(self, rhs: LogMask) -> Self::Output
{
return Self::from_bits_retain(self.bits() & rhs.bits());
}
}
impl BitAnd<LogMask> for i32
{
type Output = i32;
#[inline]
fn bitand(self, rhs: LogMask) -> i32
{
return self & rhs.bits();
}
}
pub
fn truncate(lt: &str) -> &str
{
let ltt =
match lt.char_indices().nth(lt.len()-1)
{
None => lt,
Some((idx, _)) => <[..idx],
};
return ltt;
}
pub
fn truncate_n<'t>(lt: &'t str, n: usize) -> &'t str
{
if lt.as_bytes().len() <= n
{
return lt;
}
let mut nn: usize = 0;
let mut cc = lt.chars();
let mut ln: usize;
loop
{
match cc.next()
{
Some(r) =>
{
ln = r.len_utf8();
nn += ln;
if nn == n
{
return <[..nn];
}
else if nn > n
{
return <[..nn-ln];
}
},
None =>
return lt,
}
}
}
pub
fn check_printable(a: &str) -> SyRes<()>
{
if a.is_empty() == true
{
throw_error!("empty SD value");
}
for p in a.chars()
{
if p.is_ascii() == false || p.is_ascii_graphic() == false || p == '@' || p == '=' || p == ']' || p == '\"'
{
throw_error!("incorrect char: '{:X}' in SD value", p as u64);
}
}
return Ok(());
}
pub
fn escape_chars(st: Cow<'static, str>) -> Cow<'static, str>
{
let mut out = String::with_capacity(st.len());
for c in st.chars()
{
if c.is_control() == true
{
out.push_str(ESC_CHAR_REPL);
}
else if c == '\"' || c == '\\' || c == ']'
{
out.push('\\');
out.push(c);
}
else
{
out.push(c);
}
}
if st.len() == out.len()
{
return st;
}
else
{
return Cow::Owned(out);
}
}
#[cfg(test)]
mod tests
{
use super::*;
#[test]
fn test_truncate()
{
let test = "cat\n";
let trunc = truncate(test);
assert_eq!("cat", trunc);
}
#[test]
fn test_truncate_n()
{
assert_eq!(truncate_n("abcde", 3), "abc");
assert_eq!(truncate_n("ボルテ", 4), "ボ");
assert_eq!(truncate_n("ボルテ", 5), "ボ");
assert_eq!(truncate_n("ボルテ", 6), "ボル");
assert_eq!(truncate_n("abcde", 0), "");
assert_eq!(truncate_n("abcde", 5), "abcde");
assert_eq!(truncate_n("abcde", 6), "abcde");
assert_eq!(truncate_n("ДАТА", 3), "Д");
assert_eq!(truncate_n("ДАТА", 4), "ДА");
assert_eq!(truncate_n("ДАТА", 1), "");
}
}