/*
 * dipcref.c
 *
 * Allows the user to view and delete some information registered 
 * in referee linked lists.
 *
 * By Kamran Karimi
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pwd.h>

#include <linux/dipc.h>

extern int readn(int fd, char *ptr, int nbytes);
extern int writen(int fd, char *ptr, int nbytes);
extern int ring(int family, struct sockaddr *addr, int addr_len);

char TypeStr[3][4] = {"SHM", "MSG", "SEM" };

struct bd_link
{
 struct back_door_message bdm;
 struct bd_link *next, *prev;
};

struct bd_link first_link;
struct bd_link *last_link;
void main(int argc, char **argv)
{
 int want_trans, act_trans;
 char ux_name[80], dipcd_home[80];
 int ux_sockfd, ux_len;
 struct sockaddr_un ux_addr;
 struct back_door_message bd_message;
 struct bd_link *bdl;
 struct in_addr addr;
 char *errch;
 struct passwd *dipcdpw;
 
 if(argc < 2)
 {
  printf("USAGE:\n       %s info\n", argv[0]);
  printf("       %s rm <type>  <key>  <IP address>\n", argv[0]);
  printf("\n<type> can be one of 'msg', 'sem' or 'shm'\n");
  printf("<key> should be a numerical value\n");
  printf("<IP address> should be in the form of w.x.y.z\n\n");
  exit(0);
 }

 dipcdpw = getpwnam("dipcd");
 if(dipcdpw == NULL)
 {
  printf("dipcref: ERROR. User 'dipcd' was not found\n");
  exit(20);
 }
 strcpy(dipcd_home, dipcdpw->pw_dir);
 if(dipcd_home[strlen(dipcd_home) - 1] ==  '/')
  dipcd_home[strlen(dipcd_home) - 1] = 0;
 sprintf(ux_name,"%s/%s", dipcd_home, REFEREE_BACKDOOR);
 bzero((char *) &ux_addr,sizeof(ux_addr));
 ux_addr.sun_family = AF_UNIX;
 strcpy(ux_addr.sun_path,ux_name);
 ux_len = strlen(ux_addr.sun_path) + sizeof(ux_addr.sun_family);

 /****** displaying information *******/
 if(!strcasecmp(argv[1], "info"))
 {
  first_link.next = first_link.prev = NULL;
  last_link = &first_link;
                                 
  ux_sockfd = ring(AF_UNIX, (struct sockaddr *) &ux_addr, ux_len);
  if(ux_sockfd < 0)
  {
   printf("dipcref: ERROR. Can't connect to the referee\n");
   exit(20);
  }  
  bd_message.cmd = BACK_DOOR_INFO;
  want_trans = sizeof(bd_message);
  act_trans = writen(ux_sockfd,(char *)&bd_message, want_trans);
  if(act_trans < want_trans)
  {
   printf("dipcref: ERROR. Sent %d from %d bytes  BECAUSE %s",
           act_trans,want_trans,strerror(errno));        
   exit(20);
  }
  do
  {
   act_trans = readn(ux_sockfd,(char *)&bd_message, want_trans);
   if(act_trans < want_trans)
   {
    printf("dipcref: ERROR. Received %d From %d Bytes  BECAUSE %s",
           act_trans,want_trans,strerror(errno));        
    break;
   }
   if(bd_message.arg1 < 0) break;
   bdl = malloc(sizeof(struct bd_link));
   if(bdl == NULL)
   {
    printf("dipcref: ERROR. Out of memory\n");
    break;
   } 
   bdl->bdm = bd_message;
   bdl->next = NULL;
   bdl->prev = last_link;
   last_link->next = bdl;
   last_link = bdl;
  } while(1);
  /* show the information */
  if(first_link.next == NULL)
   printf("dipcref: No information in the referee.\n");
  else
  {
   printf("Information registered in the referee:\n\n");
   printf("Type  Key       Size     DIPC   Address\n");
   bdl = first_link.next;
   while(bdl)
   {
    addr.s_addr = bdl->bdm.arg5;
    if(bdl->bdm.arg1 == 1) /* message */
      bdl->bdm.arg3 = -1;
    printf("%-4.4s  %-8d  %-8d %-4.4s   %-15.15s\n",
           TypeStr[bdl->bdm.arg1],
           bdl->bdm.arg2, bdl->bdm.arg3, 
           (bdl->bdm.arg4) ? "Yes" : "No",
          inet_ntoa(addr));
    bdl = bdl->next;
   }
   /* free the linked list */
   bdl = first_link.next;
   while(bdl)
   {
    last_link = bdl->next;
    free(bdl);
    bdl = last_link;
   }
  } 
  printf("\n");
 }

 /******* removing a key ********/
 else if(!strcasecmp(argv[1], "rm"))
 {
  if(argc != 5)
  {
   printf("USAGE: %s rm  <type>  <key>  <IP address>\n", argv[0]);
   exit(20);
  }
  if(!strcasecmp(argv[2],"shm"))
   bd_message.arg1 = 0;
  else if(!strcasecmp(argv[2],"msg"))
   bd_message.arg1 = 1;
  else if(!strcasecmp(argv[2],"sem"))
   bd_message.arg1 = 2;
  else
  {
   printf("dipcref: ERROR. Unknown type %s\n", argv[2]);
   exit(20);
  }
  bd_message.arg2 = strtol(argv[3], &errch, 10);
  if(errch == argv[2])
  {
   printf("dipcref: ERROR. Invalid key\n");
   exit(20);
  }
  bd_message.arg5 = inet_addr(argv[4]);
  if(bd_message.arg5 == -1)
  {
   printf("dipcref: ERROR. Invalid IP address\n");
   exit(20);
  } 
  bd_message.cmd = BACK_DOOR_REMOVE;
  ux_sockfd = ring(AF_UNIX, (struct sockaddr *) &ux_addr, ux_len);
  if(ux_sockfd < 0)
  {
   printf("dipcref: ERROR. Can't connect to the referee\n");
   exit(20);
  }
  want_trans = sizeof(bd_message);
  act_trans = writen(ux_sockfd,(char *)&bd_message, want_trans);
  if(act_trans < want_trans)
  {
   printf("dipcref: ERROR. Sent %d from %d bytes  BECAUSE %s\n",
           act_trans,want_trans,strerror(errno));        
   exit(20);
  }
  bd_message.arg1 = -1; /* default */
  act_trans = readn(ux_sockfd,(char *)&bd_message, want_trans);
  if(act_trans < want_trans)
  {
   printf("dipcref: ERROR. Received %d from %d bytes  BECAUSE %s\n",
           act_trans,want_trans,strerror(errno));
  }
  if(bd_message.arg1 < 0)
   printf("dipcref: Failed to delete the entry\n");
  else
   printf("Entry deleted\n");
 }
 else 
  printf("dipcref: ERROR. Unknown command\n");
}
