syslog-rs 0.2.1

A native Rust implementation of the glibc/libc syslog.
Documentation
/*-
* syslog-rs - a syslog client translated from libc to rust
* Copyright (C) 2021  Aleksandr Morozov
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
* 
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/


use std::mem::MaybeUninit;
use std::{
    net::Shutdown,
    os::unix::{
        prelude::AsRawFd, 
        io::RawFd
    }
};

use tokio::net::UnixDatagram;

use nix::errno::Errno;

use crate::{map_error, throw_error};

use super::common::*;
use super::error::{SyRes, SyslogError, SyslogErrCode};

/// A private enum with type of the socket
pub(crate) enum SyslogSocket
{
    /// Not initialized
    None,

    /// Unprivileged socket used
    UnPriv(UnixDatagram),

    /// Privileged socket used
    Priv(UnixDatagram),

    /// A compatibility socket used
    OldLog(UnixDatagram),
}

unsafe impl Sync for SyslogSocket{}
unsafe impl Send for SyslogSocket{}

impl SyslogSocket
{
    /// Creates a default instance
    pub(crate) fn none() -> Self
    {
        return Self::None;
    }

    /// Returns the rawfd without passing the ownership ower the
    /// the unixsocket. Will panic if called on enum None.
    pub(crate) fn get_raw_fd(&self) -> RawFd
    {
        match *self
        {
            Self::None => panic!("Assertion: wrong use of get_raw_fd()"),
            Self::UnPriv(ref s) | 
            Self::Priv(ref s) | 
            Self::OldLog(ref s) =>
            {
                return s.as_raw_fd();
            }
        }
    }

    fn connect_internal() -> SyRes<Self>
    {
        let sock = 
            UnixDatagram::unbound().map_err(|e|
                map_error!("unbounded unix datagram initialization failure: {}", e)
            )?;

        if let Ok(_) = sock.connect(PATH_LOG_PRIV)
        {
            return Ok(Self::Priv(sock));
        }
        else if let Ok(_) = sock.connect(PATH_LOG)
        {
            return Ok(Self::UnPriv(sock));
        }
        else if let Ok(_) = sock.connect(PATH_OLDLOG)
        {
            return Ok(Self::OldLog(sock));
        }
        else if let Ok(_) = sock.connect(PATH_OSX)
        {
            return Ok(Self::Priv(sock));
        }
        else
        {
            // failed to open socket
            throw_error!("failed to open connection to syslog server: {} ", 
                        Errno::last());
        };
    }

    /// Performs the connection to the syslog's server socket.
    /// Picks the socket automatically.
    pub(crate) fn connect() -> SyRes<Self>
    {
        // open connection first
        let stream = Self::connect_internal()?;

        // setting sendbuf
        let mut len: MaybeUninit<libc::socklen_t> = std::mem::MaybeUninit::uninit();

        // set the sndbuf len
        let res = unsafe
            {
                libc::getsockopt(
                    stream.get_raw_fd(), 
                    libc::SOL_SOCKET, 
                    libc::SO_SNDBUF, 
                    len.as_mut_ptr() as *mut libc::c_void, 
                    &mut { 
                        std::mem::size_of::<libc::socklen_t>() as libc::socklen_t
                    } 
                )
            };

        if res == 0
        {
            let mut len = unsafe { len.assume_init() };

            if len < MAXLINE
            {
                len = MAXLINE;

                unsafe {
                    libc::setsockopt(
                        stream.get_raw_fd(), 
                        libc::SOL_SOCKET, 
                        libc::SO_SNDBUF, 
                        &len as *const _ as *const libc::c_void, 
                        std::mem::size_of::<libc::socklen_t>() as libc::socklen_t
                    )
                };
            }
        }

        return Ok(stream);
    }

    /// Writes to the socket. Will panic if instance is enum None.
    pub(crate) async fn send(&mut self, msg: &[u8]) -> std::io::Result<usize>
    {
        match *self
        {
            Self::None => panic!("Assertion: wrong use of get_raw_fd()"),
            Self::UnPriv(ref mut s) | 
            Self::Priv(ref mut s) | 
            Self::OldLog(ref mut s) =>
            {
                return s.send(msg).await;
            }
        } 
    }

    /// Shutdowns the socket. Will panic if instance is enum None.
    pub(crate) fn shutdown(&mut self)
    {
        match *self
        {
            Self::None => panic!("Assertion: wrong use of get_raw_fd()"),
            Self::UnPriv(ref mut s) | 
            Self::Priv(ref mut s) | 
            Self::OldLog(ref mut s) =>
            {
                let _ = s.shutdown(Shutdown::Both);
            }
        }
    }

    /// Returns true is current instance of enum is None.
    pub(crate) fn is_none(&self) -> bool
    {
        match self
        {
            Self::None => return true,
            Self::UnPriv(_) | 
            Self::Priv(_) | 
            Self::OldLog(_) => return false,
        }
    }

    /// Returns true if stream was connected to privileged socket.
    pub(crate) fn is_priv(&self) -> bool
    {
        match self
        {
            Self::None => return false,
            Self::UnPriv(_) => return false,
            Self::Priv(_) => return true,
            Self::OldLog(_) => return false,
        }
    }
}