/* ---------------------------------------------------------------------
   (c) ED 2005
   Project      : clib/psock (portable sockets)
   Function     : UNIX sockets wrapper
   Module       : SUN
   File         : sun.c
   Created      : 09-11-2005
   Modified     : 15-11-2005
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   Log

   1.1 15-11-2005 sun_last_remote() added
   1.0 10-11-2005 Initial version
   0.0 09-11-2005 Created

   --------------------------------------------------------------------- */
#ifdef __cplusplus
#error This is not C++. Please use a C compiler.
#endif

#include "psock/inc/sun.h"
#include "ed/inc/sysalloc.h"
#include "ed/inc/sys.h"

/* UNIX socket address */
#include <sys/un.h>

/* private macro definitions =========================================== */

#define ID "SUN"
#define VER "1.1"

#define MODULE ID "."

#define DBG 0

/* private constants =================================================== */

/* private types ======================================================= */

/* private structures ================================================== */

struct sun
{
   SOCKET sock;
   struct sockaddr_un loc_addr;
   struct sockaddr_un rem_addr;
};

/* private functions =================================================== */

/* public internal functions =========================================== */

/* entry points ======================================================== */

/* ---------------------------------------------------------------------
   sun_sid ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *sun_sid (void)
{
   return ID;
}

/* ---------------------------------------------------------------------
   sun_sver ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *sun_sver (void)
{
   return VER;
}

/* ---------------------------------------------------------------------
   sun_serr ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *sun_serr (sun_err_e err)
{
   char const *s = "SUN_ERR_???";

   if (err < SUN_ERR_NB)
   {
      static char const *as[] =
      {
         "SUN_OK",
#define ITEM(n_, s_)\
         #s_,

#include "psock/inc/sun_err.itm"

#undef ITEM

      };
      s = as[err];
   }
   return s;
}

/* ---------------------------------------------------------------------
   sun_create ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
sun_s *sun_create (char const *s_loc)
{
   sun_s *this = malloc (sizeof *this);

   if (this != NULL)
   {
      CLR (this, sun_s);

      this->loc_addr.sun_family = AF_UNIX;
      strcpy (this->loc_addr.sun_path, s_loc);

      remove (this->loc_addr.sun_path);

      {
         SOCKET sock = socket (PF_UNIX, SOCK_DGRAM, 0);

         if (sock != INVALID_SOCKET)
         {
            this->sock = sock;

            int rc = bind (sock, (struct sockaddr *) &this->loc_addr, sizeof this->loc_addr);

            if (rc < 0)
            {
               perror (MODULE "bind");
               sun_delete (this), this = NULL;
            }
         }
         else
         {
            sun_delete (this), this = NULL;
         }
      }
   }
   return this;
}

/* ---------------------------------------------------------------------
   sun_delete ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
void sun_delete (sun_s * this)
{
   if (this != NULL)
   {
      remove (this->loc_addr.sun_path);
      closesocket (this->sock);
      free (this);
   }
}

/* ---------------------------------------------------------------------
   sun_send ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
sun_err_e sun_send (sun_s * this
                    ,char const *s_dis
                    ,void const *p_data
                    ,size_t size
                    ,size_t * p_sentlen)
{
   sun_err_e err = SUN_OK;

   if (this != NULL)
   {
      int sentlen;
      if (s_dis == NULL)
      {
         /* response */
         sentlen = sendto (this->sock, p_data, size, 0, (struct sockaddr *) &this->rem_addr, sizeof this->rem_addr);
         if (sentlen >= 0)
         {
#if DBG
            printf (MODULE "send > %lu bytes sent to %s (response)\n"
                    ,(unsigned long) sentlen
                    ,this->rem_addr.sun_path
               );
            SYS_dump (p_data, sentlen);
#endif
         }
         else
         {
            perror ("sendto()");
         }
      }
      else
      {
         /* command, trap */
         struct sockaddr_un rem_addr =
         {
            .sun_family = AF_UNIX,
         };
         size_t len = strlen (s_dis);

         if (len + 1 < sizeof rem_addr.sun_path)
         {
            memcpy (rem_addr.sun_path, s_dis, len + 1);
            sentlen = sendto (this->sock, p_data, size, 0, (struct sockaddr *) &rem_addr, sizeof rem_addr);

            if (sentlen >= 0)
            {
#if DBG
               printf (MODULE "send > %lu bytes sent to %s (command)\n"
                       ,(unsigned long) sentlen
                       ,rem_addr.sun_path
                  );
               SYS_dump (p_data, sentlen);
#endif
            }
            else
            {
               perror ("sendto()");
            }
         }
         else
         {
            err = SUN_ERR_PATH_LENGTH;
         }
      }

      if (err == SUN_OK)
      {
         if (p_sentlen != NULL)
         {
            *p_sentlen = sentlen;
         }
      }
   }
   else
   {
      err = SUN_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   sun_receive ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
sun_err_e sun_receive (sun_s * this
                       ,void *p_data
                       ,size_t size
                       ,size_t * p_reclen)
{
   sun_err_e err = SUN_OK;

   if (this != NULL)
   {
      socklen_t addr_size = sizeof this->rem_addr;
#if 0
      printf (MODULE "socket receiving a datagram\n");
#endif
      int rc = recvfrom (this->sock, p_data, size, 0, (struct sockaddr *) &this->rem_addr, &addr_size);
      if (rc < 0)
      {
         perror (MODULE "recvfrom()");
         err = SUN_ERR_RECEIVE;
      }
      else
      {
#if DBG
         printf ("\n");
         printf (MODULE "recv < %lu bytes from %s\n"
                 ,(unsigned long) rc
                 ,this->rem_addr.sun_path
            );
         SYS_dump (p_data, rc);
#endif

         if (p_reclen != NULL)
         {
            *p_reclen = rc;
         }
      }
   }
   else
   {
      err = SUN_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   sun_sock ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
SOCKET sun_sock (sun_s * const this)
{
   SOCKET sock = INVALID_SOCKET;

   if (this != NULL)
   {
      sock = this->sock;
   }

   return sock;
}

/* ---------------------------------------------------------------------
   sun_last_remote ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *sun_last_remote (sun_s * const this)
{
   char const *s = NULL;

   if (this != NULL)
   {
      s = this->rem_addr.sun_path;
   }

   return s;
}

/* public data ========================================================= */

/* ---------------------------------------------------------------------
   Generated by NEW (c) ED 2.8
   Powered by C-code generator library  1.2
   --------------------------------------------------------------------- */
