extern crate syslog_rs;
use std::fmt;
use std::io::ErrorKind;
use std::net::Shutdown;
use std::time::Duration;
use std::{borrow::Cow, io::IoSlice};
use syslog_rs::a_sync::AsyncMutexGuard;
use syslog_rs::formatters::DefaultSyslogFormatter;
use syslog_rs::nix::errno::Errno;
use syslog_rs::nix::libc;
use syslog_rs::error::SyslogError;
use syslog_rs::{common, throw_error_errno, throw_error_os, AsyncSyslog, AsyncSyslogTap, AsyncTap, LogFacility, LogStat, Priority, SyslogDestMsg, SyslogLocal, TapType, PATH_CONSOLE, PATH_LOG, PATH_LOG_PRIV, PATH_OLDLOG, PATH_OSX};
use syslog_rs::{AsyncSyslogInternal, AsyncSyslogInternalIO, AsyncMutex, error::SyRes, formatters::SyslogFormatter, map_error_os, AsyncSyslogDestination};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use tokio::net::UnixDatagram;
use tokio::sync::MutexGuard;
use tokio::time::sleep;
use tokio::{io::AsyncWrite, sync::Mutex};
#[derive(Debug)]
pub struct OurAsyncSyslogIO;
impl OurAsyncSyslogIO
{
pub(crate) async
fn async_send_to_fd<W>(mut file_fd: W, msg: &str, newline: &str) -> SyRes<usize>
where W: AsyncWrite + Unpin
{
return
file_fd
.write_vectored(&[IoSlice::new(msg.as_bytes()), IoSlice::new(newline.as_bytes())])
.await
.map_err(|e|
map_error_os!(e, "async_send_to_fd() writev() failed")
);
}
}
impl AsyncSyslogInternalIO for OurAsyncSyslogIO
{
#[inline]
async
fn send_to_stderr(logstat: LogStat, msg: &str)
{
if logstat.intersects(LogStat::LOG_PERROR) == true
{
let stderr_lock = tokio::io::stderr();
let newline = "\n";
let _ = Self::async_send_to_fd(stderr_lock, msg, newline).await;
}
}
#[inline]
async
fn send_to_syscons(logstat: LogStat, msg_payload: &str)
{
if logstat.intersects(LogStat::LOG_CONS)
{
let syscons =
File
::options()
.create(false)
.read(false)
.write(true)
.custom_flags(libc::O_NONBLOCK | libc::O_CLOEXEC)
.open(*PATH_CONSOLE)
.await;
if let Ok(file) = syscons
{
let newline = "\n";
let _ = Self::async_send_to_fd(file, msg_payload, newline);
}
}
}
async
fn sleep_micro(us: u64)
{
sleep(Duration::from_micros(us)).await;
}
}
#[derive(Debug)]
struct MutexOveride<T: Sized>(Mutex<T>);
#[derive(Debug)]
struct MutexGuardNative<'a, T>(MutexGuard<'a, T>);
impl<'a, T> MutexGuardNative<'a, T>
{
fn new(g: MutexGuard<'a, T>) -> Self
{
return Self(g);
}
}
impl<F: SyslogFormatter + Send, D: AsyncSyslogDestination> AsyncMutex<F, D, AsyncSyslogInternal<F, D, OurAsyncSyslogIO>>
for MutexOveride<AsyncSyslogInternal<F, D, OurAsyncSyslogIO>>
{
type MutxGuard<'mux> = MutexGuardNative<'mux, AsyncSyslogInternal<F, D, OurAsyncSyslogIO>>;
fn a_new(v: AsyncSyslogInternal<F, D, OurAsyncSyslogIO>) -> Self
{
return Self(Mutex::new(v));
}
async
fn a_lock<'mux>(&'mux self) -> Self::MutxGuard<'mux>
{
return MutexGuardNative::new(self.0.lock().await);
}
}
impl<'mux, F: SyslogFormatter + Send, D: AsyncSyslogDestination> AsyncMutexGuard<'mux, F, D, AsyncSyslogInternal<F, D, OurAsyncSyslogIO>>
for MutexGuardNative<'mux, AsyncSyslogInternal<F, D, OurAsyncSyslogIO>>
{
fn guard(&self) -> &AsyncSyslogInternal<F, D, OurAsyncSyslogIO>
{
return &self.0;
}
fn guard_mut(&mut self) -> &mut AsyncSyslogInternal<F, D, OurAsyncSyslogIO>
{
return &mut self.0;
}
}
#[derive(Debug, Clone)]
struct NativeSyslogLocal(SyslogLocal);
impl NativeSyslogLocal
{
fn new(s: SyslogLocal) -> Self
{
return Self(s);
}
}
impl fmt::Display for NativeSyslogLocal
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.0)
}
}
impl AsyncSyslogDestination for NativeSyslogLocal
{
type SocketTap = AsyncTap::<tokio::net::UnixDatagram, Self>;
}
impl SyslogDestMsg for NativeSyslogLocal
{
fn get_max_msg_len() -> usize
{
if *common::RFC5424_MAX_DGRAM >= common::MAXLINE
{
return common::MAXLINE;
}
else
{
return *common::RFC5424_MAX_DGRAM;
};
}
}
impl AsyncSyslogTap<NativeSyslogLocal> for AsyncTap<UnixDatagram, NativeSyslogLocal>
{
fn new(req_tap: NativeSyslogLocal) -> SyRes<Self>
{
return Self::new(req_tap);
}
async
fn connectlog(&mut self) -> SyRes<()>
{
let sock =
UnixDatagram
::unbound()
.map_err(|e|
map_error_os!(e, "unbounded unix datagram initialization failure: {}", e)
)?;
let tap_type =
if self.get_tap_data().0.get_use_alternative() == false && self.get_tap_data().0.get_custom_remote_path().is_some() == true
{
if let Err(e) = sock.connect(self.get_tap_data().0.get_custom_remote_path().as_ref().unwrap())
{
throw_error_os!(e, "failed to open connection to syslog server at '{}'",
self.get_tap_data().0.get_custom_remote_path().as_ref().unwrap().display());
}
else
{
TapType::CustomLog
}
}
else if self.get_tap_data().0.get_custom_remote_path().is_some() == true &&
sock.connect(self.get_tap_data().0.get_custom_remote_path().as_ref().unwrap()).is_ok() == true
{
TapType::CustomLog
}
else if let Ok(_) = sock.connect(PATH_LOG_PRIV)
{
TapType::Priv
}
else if let Ok(_) = sock.connect(PATH_LOG)
{
TapType::UnPriv
}
else if let Ok(_) = sock.connect(PATH_OLDLOG)
{
TapType::OldLog
}
else if let Ok(_) = sock.connect(PATH_OSX)
{
TapType::Priv
}
else
{
throw_error_errno!(Errno::last(), "failed to open connection to syslog server");
};
self.set_sock(sock);
self.set_cur_tap_type(tap_type);
return Ok(());
}
async
fn send(&mut self, msg: &[u8]) -> std::io::Result<usize>
{
let sock =
self
.get_sock_mut()
.ok_or_else(||
std::io::Error::new(ErrorKind::NotConnected, "no connection")
)?;
return sock.send(msg).await;
}
async
fn disconnectlog(&mut self) -> std::io::Result<()>
{
match self.take_sock()
{
Some(s) =>
{
self.set_cur_tap_type(TapType::None);
s.shutdown(Shutdown::Both)
},
None =>
{
self.set_cur_tap_type(TapType::None);
Ok(())
}
}
}
fn is_connected(&self) -> bool
{
return self.get_sock().is_some();
}
fn get_type(&self) -> TapType
{
return self.get_tap_type();
}
fn get_max_msg_size() -> usize
{
return NativeSyslogLocal::get_max_msg_len();
}
fn update_tap_data(&mut self, tap_data: NativeSyslogLocal)
{
self.update_tap_data(tap_data);
}
}
#[tokio::main]
async
fn main()
{
let syslog =
AsyncSyslog
::<NativeSyslogLocal, DefaultSyslogFormatter, OurAsyncSyslogIO, MutexOveride<_>>
::openlog_with(
Some("example_cust"),
LogStat::LOG_CONS | LogStat::LOG_NDELAY | LogStat::LOG_PID,
LogFacility::LOG_DAEMON,
NativeSyslogLocal::new(SyslogLocal::new())
)
.await
.unwrap();
syslog.vsyslog(Priority::LOG_DEBUG, "test custom async exec in example").await;
println!("Hello, world!");
}