use std::io::{Error};
use crate::formatters::{SyslogFormatted, SyslogFormatter};
use crate::portable;
use crate::common::*;
use crate::error::{SyRes, SyslogError};
use crate::SyslogDestination;
use super::socket::*;
#[cfg(target_family = "unix")]
use nix::libc;
#[derive(Debug, Clone)]
pub(crate) struct LogItems
{
pub(crate) logtag: String,
pub(crate) logpid: String,
pub(crate) logmask: i32,
pub(crate) logstat: LogStat,
pub(crate) facility: LogFacility,
}
impl LogItems
{
#[inline]
pub
fn new(ident: Option<&str>, logmask: i32, logstat: LogStat, facility: LogFacility) -> Self
{
let mut log_inst =
Self
{
logtag: String::new(),
logpid: portable::get_pid().to_string(),
logmask: logmask,
logstat: logstat,
facility: LogFacility::empty(),
};
log_inst.set_log_facility(facility);
log_inst.set_identity(ident);
return log_inst;
}
pub(crate)
fn set_log_facility(&mut self, facility: LogFacility)
{
let log_facility =
if facility.is_empty() == false &&
(facility & !LogMask::LOG_FACMASK).is_empty() == true
{
facility
}
else
{
LogFacility::LOG_USER
};
self.facility = log_facility;
return;
}
pub(crate)
fn set_identity(&mut self, ident: Option<&str>)
{
let logtag =
match ident
{
Some(r) =>
truncate_n(r, RFC_MAX_APP_NAME).to_string(),
None =>
truncate_n(
portable::p_getprogname()
.unwrap_or("notavail".to_string())
.as_str(),
RFC_MAX_APP_NAME
)
.to_string()
};
self.logtag = logtag;
return;
}
#[inline]
pub
fn get_progname(&self) -> &str
{
return &self.logtag;
}
#[inline]
pub
fn get_pid(&self) -> &str
{
return &self.logtag;
}
#[inline]
fn is_logmasked(&self, pri: Priority) -> bool
{
return ((1 << (pri & LogMask::LOG_PRIMASK)) & self.logmask) == 0;
}
pub(crate)
fn set_logmask(&mut self, logmask: i32) -> i32
{
let oldmask = self.logmask;
if logmask != 0
{
self.logmask = logmask;
}
return oldmask;
}
pub(crate)
fn vsyslog1_msg<F, D>(&self, pri: Priority, fmt: &F) -> Option<(SyslogFormatted, LogStat)>
where F: SyslogFormatter, D: SyslogDestination
{
if self.is_logmasked(pri) == true
{
return None;
}
let pri_fac = SyslogMsgPriFac::set_facility(pri, self.facility);
let msg_pid =
if self.logstat.intersects(LogStat::LOG_PID) == true
{
Some(self.logpid.as_str())
}
else
{
None
};
let msg_formatted =
fmt.vsyslog1_format(D::SocketTap::get_max_msg_size(), pri_fac, &self.logtag, msg_pid);
self.logstat.send_to_stderr(msg_formatted.get_stderr_output());
return Some((msg_formatted, self.logstat));
}
}
#[derive(Debug)]
pub(crate) struct SyslogSocketLockless<D: SyslogDestination>
{
stream: D::SocketTap
}
impl<D: SyslogDestination> SyslogSocketLockless<D>
{
pub(crate)
fn new(logstat: LogStat, net_tap_prov: D) -> SyRes<Self>
{
let mut sock =
D::SocketTap::new(net_tap_prov)?;
if logstat.contains(LogStat::LOG_NDELAY) == true
{
sock.connectlog()?;
}
return Ok(
Self{ stream: sock }
);
}
#[inline]
pub(crate)
fn update_tap_data(&mut self, tap_data: D) -> SyRes<()>
{
self.stream.disconnectlog()?;
self.stream.update_tap_data(tap_data);
return self.stream.connectlog();
}
#[inline]
pub(crate)
fn reconnectlog(&mut self) -> SyRes<()>
{
self.stream.disconnectlog()?;
self.stream.connectlog()?;
return Ok(());
}
#[inline]
pub(crate)
fn connectlog(&mut self) -> SyRes<()>
{
return self.stream.connectlog();
}
#[inline]
pub(crate)
fn disconnectlog(&mut self) -> SyRes<()>
{
return self.stream.disconnectlog();
}
#[cfg(target_family = "unix")]
fn handle_error(&mut self, e: Error) -> Result<(), SyslogError>
{
use std::io::ErrorKind;
use std::thread::sleep;
use std::time::Duration;
if e.kind() == ErrorKind::NotConnected
{
self.stream.connectlog()?;
return Ok(());
}
else
{
if D::DEST_TYPE.is_network() == false
{
if let Some(libc::ENOBUFS) = e.raw_os_error()
{
if self.stream.get_type().is_priv() == true
{
use nix::errno::Errno;
use crate::throw_error_errno;
throw_error_errno!(Errno::ENOBUFS, "Not enough space in priv socket.");
}
sleep(Duration::from_micros(1));
return Ok(());
}
else
{
self.stream.disconnectlog()?;
self.stream.connectlog()?;
return Ok(());
}
}
else
{
self.stream.disconnectlog()?;
self.stream.connectlog()?;
return Ok(());
}
}
}
#[cfg(target_family = "windows")]
fn handle_error(&mut self, e: Error) -> Result<(), SyslogError>
{
use crate::DestinationType;
use crate::error::SyslogErrCode;
if D::DEST_TYPE == DestinationType::Local
{
let Ok(werr) = e.downcast::<windows::core::Error>()
else
{
return Err(
SyslogError::new(SyslogErrCode::InternalError,
"error downcast failed".into())
);
};
return Err(
SyslogError::new(
SyslogErrCode::InternalError,
werr.message()
)
);
}
else if D::DEST_TYPE == DestinationType::Network
{
self.stream.disconnectlog()?;
self.stream.connectlog()?;
return Ok(());
}
else
{
return Err(
SyslogError::new(
SyslogErrCode::InternalError,
format!("IO error: {}", e)
)
);
}
}
pub(crate)
fn vsyslog1(&mut self, logstat: LogStat, mut msg_formatted: SyslogFormatted) -> SyRes<()>
{
let fullmsg = msg_formatted.get_full_msg();
let error =
loop
{
match self.stream.send(fullmsg.as_bytes())
{
Ok(_) =>
return Ok(()),
Err(e) =>
{
match self.handle_error(e)
{
Ok(_) =>
continue,
Err(e) =>
{
logstat.send_to_stderr(&format!("connectlog() failed with {}", e));
break e;
}
}
}
}
};
let _ = logstat.send_to_syscons(msg_formatted.get_stderr_output());
return Err(error);
}
}
#[cfg(test)]
mod tests
{
use crate::LOG_MASK;
use super::*;
#[test]
fn test_log_cons()
{
let msg = "header msg message payload";
let lsts = LogStat::LOG_CONS;
lsts.send_to_syscons(msg);
return;
}
#[test]
fn test_bit_operations()
{
let correct =
LogItems::new(Some("test1"), 0xFF, LogStat::LOG_PID, LogFacility::LOG_DAEMON);
assert_eq!(correct.facility, LogFacility::LOG_DAEMON);
assert_eq!((correct.facility & !LogFacility::LOG_DAEMON), LogFacility::empty());
}
#[test]
fn test_set_priority()
{
let mut correct =
LogItems::new(Some("test1"), 0xFF, LogStat::LOG_PID, LogFacility::LOG_DAEMON);
let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
assert_eq!(ret, 0xff);
let ret = correct.set_logmask(LOG_MASK!(Priority::LOG_ERR));
assert_eq!(ret, LOG_MASK!(Priority::LOG_ERR));
let ret = correct.is_logmasked(Priority::LOG_WARNING);
assert_eq!(ret, true);
let ret = correct.is_logmasked(Priority::LOG_ERR);
assert_eq!(ret, false);
let ret = correct.is_logmasked(Priority::LOG_CRIT);
assert_eq!(ret, true);
}
}