use std::fmt;
use std::marker::PhantomData;
use crate::a_sync::syslog_trait::AsyncSyslogApi;
use crate::formatters::SyslogFormatter;
use crate::map_error_os;
use crate::portable;
use crate::common::*;
use crate::error::SyRes;
use crate::socket::TapType;
use crate::AsyncSyslogDestination;
use super::async_socket::*;
#[allow(async_fn_in_trait)]
pub trait AsyncSyslogInternalIO: fmt::Debug + Send + 'static
{
async fn send_to_syscons(logstat: LogStat, msg_payload: &str);
async fn send_to_stderr(logstat: LogStat, msg: &str);
async fn sleep_micro(us: u64);
}
#[allow(async_fn_in_trait)]
pub trait AsyncMutex<F: SyslogFormatter, D: AsyncSyslogDestination, DS: AsyncSyslogApi<F, D>>
{
type MutxGuard<'mux>: AsyncMutexGuard<'mux, F, D, DS> where Self: 'mux;
fn a_new(v: DS) -> Self;
async fn a_lock<'mux>(&'mux self) -> Self::MutxGuard<'mux>;
}
pub trait AsyncMutexGuard<'mux, F: SyslogFormatter, D: AsyncSyslogDestination, DS: AsyncSyslogApi<F, D>>
{
fn guard(&self) -> &DS;
fn guard_mut(&mut self) -> &mut DS;
}
#[derive(Debug)]
pub struct AsyncSyslogInternal<F: SyslogFormatter + Send, D: AsyncSyslogDestination, IO: AsyncSyslogInternalIO>
{
logtag: String,
logpid: String,
logstat: LogStat,
facility: LogFacility,
logmask: i32,
stream: D::SocketTap,
_p: PhantomData<F>,
_p2: PhantomData<IO>,
}
impl<F: SyslogFormatter + Send, D: AsyncSyslogDestination, IO: AsyncSyslogInternalIO> AsyncSyslogInternal<F, D, IO>
{
pub(crate)
fn new(
ident: Option<&str>,
logstat: LogStat,
facility: LogFacility,
req_tap: D
) -> SyRes<Self>
{
let log_facility =
if facility.is_empty() == false &&
(facility & !LogMask::LOG_FACMASK).is_empty() == true
{
facility
}
else
{
LogFacility::LOG_USER
};
let logtag =
match ident
{
Some(r) =>
truncate_n(r, RFC_MAX_APP_NAME).to_string(),
None =>
truncate_n(
portable::p_getprogname()
.unwrap_or("".to_string())
.as_str(),
RFC_MAX_APP_NAME
)
.to_string()
};
let sock = D::SocketTap::new(req_tap)?;
return Ok(
Self
{
logtag: logtag,
logpid: portable::get_pid().to_string(),
logstat: logstat,
facility: log_facility,
logmask: 0xff,
stream: sock,
_p: PhantomData,
_p2: PhantomData,
}
);
}
#[inline]
pub(crate)
fn is_logmasked(&self, pri: Priority) -> bool
{
return ((1 << (pri & LogMask::LOG_PRIMASK)) & self.logmask) == 0;
}
#[inline]
pub(crate)
fn get_taptype(&self) -> TapType
{
return self.stream.get_type();
}
#[inline]
pub(crate)
fn set_logtag<L: AsRef<str>>(&mut self, logtag: L, update_pid: bool)
{
self.logtag =
truncate_n(logtag.as_ref(), RFC_MAX_APP_NAME).to_string();
if update_pid == true
{
self.logpid = portable::get_pid().to_string();
}
return;
}
#[inline]
pub(crate) async
fn disconnectlog(&mut self) -> SyRes<()>
{
return
self
.stream
.disconnectlog()
.await
.map_err(|e| map_error_os!(e, "can not disconnect log properly"));
}
}
impl<F: SyslogFormatter + Send, D: AsyncSyslogDestination, IO: AsyncSyslogInternalIO> AsyncSyslogApi<F, D>
for AsyncSyslogInternal<F, D, IO>
{
async
fn update_tap_data(&mut self, tap_data: D) -> SyRes<()>
{
let is_con = self.stream.is_connected();
if is_con == true
{
self
.stream
.disconnectlog()
.await
.map_err(|e|
map_error_os!(e, "update_tap_data() can not disconnect log properly")
)?;
}
self.stream.update_tap_data(tap_data);
if is_con == true
{
self.stream.connectlog().await?;
}
return Ok(());
}
#[inline]
fn change_identity(&mut self, ident: &str)
{
self.set_logtag(ident, true);
}
async
fn reconnect(&mut self) -> SyRes<()>
{
self.disconnectlog().await?;
self.connectlog().await?;
return Ok(());
}
async
fn closelog(&mut self) -> SyRes<()>
{
return self.disconnectlog().await;
}
fn set_logmask(&mut self, logmask: i32) -> i32
{
let oldmask = self.logmask;
if logmask != 0
{
self.logmask = logmask;
}
return oldmask;
}
#[inline]
async
fn connectlog(&mut self) -> SyRes<()>
{
return self.stream.connectlog().await;
}
async
fn vsyslog1(&mut self, pri: Priority, fmt: F)
{
if self.is_logmasked(pri) == true
{
return;
}
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 mut msg_formatted =
fmt.vsyslog1_format(D::SocketTap::get_max_msg_size(), pri_fac, &self.logtag, msg_pid);
IO::send_to_stderr(self.logstat, msg_formatted.get_stderr_output()).await;
if self.stream.is_connected() == false
{
match self.connectlog().await
{
Ok(_) => {},
Err(e) =>
{
IO::send_to_stderr(self.logstat, &e.into_inner() ).await;
return;
}
}
}
let fullmsg = msg_formatted.get_full_msg();
loop
{
match self.stream.send(fullmsg.as_bytes()).await
{
Ok(_) =>
return,
Err(err) =>
{
if self.get_taptype().is_network() == false
{
#[cfg(target_family = "unix")]
{
if let Some(nix::libc::ENOBUFS) = err.raw_os_error()
{
if self.get_taptype().is_priv() == true
{
break;
}
IO::sleep_micro(1).await;
}
else
{
let _ = self.disconnectlog().await;
match self.connectlog().await
{
Ok(_) => {},
Err(_e) => break,
}
}
}
#[cfg(target_family = "windows")]
{
let Ok(werr) = err.downcast::<windows::core::Error>()
else
{
IO::send_to_stderr(self.logstat, "error downcast failed").await;
break;
};
IO::send_to_stderr(self.logstat, &werr.message()).await;
break;
}
}
else
{
let _ = self.disconnectlog().await;
match self.connectlog().await
{
Ok(_) => {},
Err(_e) => break,
}
}
}
}
}
IO::send_to_syscons(self.logstat, msg_formatted.get_stderr_output()).await;
}
}