/**
 * TrueReality - IPC/UNIX/comm.c
 * Copyright (C) 1998 Niki W. Waibel
 *
 * This program is free software; you can redistribute it and/
 * or modify it under the terms of the GNU General Public Li-
 * cence as published by the Free Software Foundation; either
 * version 2 of the Licence, or any later version.
 *
 * This program is distributed in the hope that it will be use-
 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public Licence for more details.
 *
 * You should have received a copy of the GNU General Public
 * Licence along with this program; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 * USA.
 *
 * Information about me (the author):
 *   Niki W. Waibel, Reichenau 20, 6890 Lustenau, Austria - EUROPE
 *   niki.waibel@gmx.net
**/





#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>
#include <unistd.h>

#include <netdb.h>

#include <arpa/inet.h>

#include <netinet/in.h>

#include <sys/socket.h>
#include <sys/types.h>

#include "../../config.h"
#include "comm.h"


#ifdef DEBUG
#  include "../../debug.h"
#endif




int ipc_server;
int ipc_client;

struct sockaddr_in local_server_addr;





int UNIX_ReadIPC(int fd, char *buf, int count)
{
   int  i;
   char c;
   int  retValue = -1;
   

   
   for(i = 0; i < count; i++)
   {
      read(fd, &c, (size_t) 1);
      
      buf[i] = c;

      if(c == '\n')
      {
         retValue = 0;
         break;
      }
   }
   
   if(retValue != 0)
      i--;
   buf[i] = '\0';
   
#ifdef DEBUG_IPC
   if(debug)
      printf("\n <ReadIPC: '%s' has been read>\n", buf); fflush(stdout);
#endif

   return(retValue);
}





int UNIX_WriteIPC(int fd, char *buf, int count)
{
   int  i;
   int  retValue = -1;
   

   
#ifdef DEBUG_IPC
   if(debug)
      printf("\n <WriteIPC: '");
#endif
   
   for(i = 0; i < count; i++)
   {
      if(buf[i] == '\n' || buf[i] == '\0')
      {
         retValue = 0;
         break;
      }
      
      write(fd, &buf[i], (size_t) 1);
#ifdef DEBUG_IPC
   if(debug)
         putchar(buf[i]);
#endif
   }

   
   write(fd, "\n", (size_t) 1);
            
#ifdef DEBUG_IPC
   if(debug)
      printf("\\n' has been written>\n"); fflush(stdout);
#endif
   
   return(retValue);
}





/*
**   It is possible that some of this is Linux specific.
**   Please email if you have suggestions to make this more global to the UNIX world.
*/


int UNIX_ListenIPC()
{
   struct sockaddr_in remote_addr;

   struct hostent     *remote_host;
   struct servent     *service;

   int                addrlen;
   int                fd;

   char               *hostname;
   struct linger      linger = {0, 0};
   int                reuseaddr = 1;


 
   local_server_addr.sin_family      = AF_INET;
   local_server_addr.sin_addr.s_addr = INADDR_ANY;
   
   service = getservbyname(SERVICE, "tcp");
   if(service == NULL)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("ListenIPC");
         printf("Cannot find service (in /etc/services): '%s'\n\n", SERVICE);
      }
#endif
      return(-1);
   }

   local_server_addr.sin_port = service->s_port;

   fd = socket(AF_INET, SOCK_STREAM, 0);
   if(fd == -1)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("ListenIPC");
         printf("Cannot create socket\n\n");
      }
#endif
      return(-2);
   }

   if(bind(fd, (struct sockaddr *)&local_server_addr, sizeof(struct sockaddr_in)) == -1)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("ListenIPC");
         printf("Unable to bind address\n\n");
      }
#endif
      return(-3);
   }

   if(listen(fd, 5) == -1)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("ListenIPC");
         printf("Unable to listen to socket\n\n");
      }
#endif
      return(-4);
   }

   setpgid(0, 0); 

   
   
   addrlen = sizeof(struct sockaddr_in);
   
   ipc_server = accept(fd, (struct sockaddr *)&remote_addr, &addrlen);
   
   if(ipc_server == -1)
      exit(-1);

   close(fd);
   
   
   
   remote_host = gethostbyaddr((char *)&remote_addr.sin_addr, sizeof(struct in_addr), remote_addr.sin_family);
   
   if(remote_host == NULL)
      hostname = inet_ntoa(remote_addr.sin_addr);
   else
      hostname = (char *)remote_host->h_name;
 
   if(setsockopt(ipc_server, SOL_SOCKET, SO_LINGER, (struct linger *)&linger, sizeof(struct linger)) == -1)
   {
      /* connection aborted on error */
#ifdef DEBUG_IPC
      if(debug)
         perror("ListenIPC");
#endif
   
      exit(1);
   }

/*
 *   This does not seem to work ?!?!?!
 *
 */
   if(setsockopt(ipc_server, SOL_SOCKET, SO_REUSEADDR, (int *)&reuseaddr, sizeof(int)) == -1)
   {
      /* connection aborted on error */
#ifdef DEBUG_IPC
      if(debug)
         perror("ListenIPC");
#endif
   
      exit(1);
   }
   
   
   /* ipc_server is ready for use */
   return(0);
} /* UNIX_ListenIPC() */





int UNIX_ConnectIPC()
{
   struct sockaddr_in local_addr;
   struct sockaddr_in remote_addr;

   struct hostent     *remote_host;
   struct servent     *service;

   int                addrlen;



   memset((char *)&local_addr, 0, sizeof(struct sockaddr_in));
   memset((char *)&remote_addr, 0, sizeof(struct sockaddr_in));

   remote_addr.sin_family = AF_INET;

   remote_host = gethostbyname(REMOTE_HOST);
   if(remote_host == NULL)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("ConnectIPC");
         printf("Cannot find host: '%s'\n\n", REMOTE_HOST);
      }
#endif
       return(-1);
   }

   remote_addr.sin_addr.s_addr = ((struct in_addr *)(remote_host->h_addr))->s_addr;

   service = getservbyname(SERVICE, "tcp");
   if(service == NULL)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("ConnectIPC");
         printf("Cannot find service (in /etc/services): '%s'\n\n", SERVICE);
      }
#endif
       return(-2);
   }

   ipc_client = socket(AF_INET, SOCK_STREAM, 0);
   if(ipc_client == -1)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("ConnectIPC");
         printf("Cannot create socket\n\n");
      }
#endif
       return(-3);
   }

   remote_addr.sin_port = service->s_port;

   if(connect(ipc_client, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr_in)) == -1)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("ConnectIPC");
         printf("Cannot make a connection to socket\n\n");
      }
#endif
       return(-4);
   }

   addrlen = sizeof(struct sockaddr_in);
   if(getsockname(ipc_client, (struct sockaddr *)&local_addr, &addrlen) == -1)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("ConnectIPC");
         printf("Cannot get the local address\n\n");
      }
#endif
       return(-5);
   }

   return(0);
} /* UNIX_ConnectIPC() */





int UNIX_RemoveListenIPC(int fd)
{

/*
 *   an unlink should be done here because otherwise
 *   the socket cannot be created again until the operating system has removed
 *   the socket completely
 *
 *   if you know how this can be done please write an email to:
 *   niki.waibel@gmx.net
 *
 */


/*   char *link_name;


   link_name = (char *)malloc((size_t) 1 + sizeof(struct sockaddr));
   if(link_name == NULL)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("RemoveListenIPC");
         printf("Not enough memory to create string of the link name of the socket\n\n");
      }
#endif
      return(-1);
   }
   link_name[0] = '\0';
   strncat(link_name, (char *)(struct sockaddr *)&local_server_addr, (size_t)sizeof(struct sockaddr));
   
   unlink(link_name);
   
#ifdef DEBUG_IPC
   if(debug)
      printf("'%s'", (char *)&local_server_addr);
#endif
   free(link_name);
*/   /* this didn't work on my system */



   return(UNIX_RemoveConnectIPC(fd));
} /* UNIX_RemoveListenIPC(int fd) */





int UNIX_RemoveConnectIPC(int fd)
{
   if(close(fd) == -1)
   {
#ifdef DEBUG_IPC
      if(debug)
      {
         perror("RemoveIPC");
         printf("The IPC was not correct initialised before\n\n");
      }
#endif
      return(-2);
   }

   return(0);
   
} /* UNIX_RemoveConnectIPC(int fd) */
