/*
 * dipcshm.c
 *
 * Allows the user to view some information in a shared memory manager's 
 * 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] = {"WANT_ACCESS", "PENDING AK", "ACCESSING" };

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];
 char *errch;
 int key, ux_sockfd, ux_len;
 struct sockaddr_un ux_addr;
 struct back_door_message bd_message;
 struct bd_link *bdl;
 struct in_addr addr;
 struct passwd *dipcdpw; 

 if(argc != 3)
 {
  printf("USAGE: %s  info <key>\n", argv[0]);
  printf("       %s  rm  <key>\n", argv[0]);
  exit(0);
 }
 key = strtol(argv[2], &errch, 10);
 if(errch == argv[2])
 {
  printf("dipcker: ERROR. Invalid Key\n");
  exit(20);
 }

 dipcdpw = getpwnam("dipcd");
 if(dipcdpw == NULL)
 {
  printf("dipcreg: 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;

 if(!strcasecmp(argv[1], "info"))
 {
  sprintf(ux_name,"%s/%s.%d", dipcd_home, SHM_BACKDOOR, key);
  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);

  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("dipcshm: Can't connect to the shared memory manager\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("dipcshm: ERROR. Sent %d from %d fytes  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("dipcshm: 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("dipcshm: 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("dipcshm: No information in shared memory manager\n");
  else
  {
   printf("Page      State        Type    Address\n");
   bdl = first_link.next;
   while(bdl)
   {
    addr.s_addr = bdl->bdm.arg5;
    printf("%-8d  %-11.11s  %-6.6s  %-15.15s\n",
           bdl->bdm.arg3,
           TypeStr[bdl->bdm.arg1],
           (bdl->bdm.arg2) ? "WRITER" : "READER",
          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");
  }
 } 
 else if(!strcasecmp(argv[1], "rm"))
 {
  sprintf(ux_name,"%s/%s.%d", dipcd_home, SHM_BACKDOOR, key);
  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);
                                  
  ux_sockfd = ring(AF_UNIX, (struct sockaddr *) &ux_addr, ux_len);
  if(ux_sockfd < 0)
  {
   printf("dipcshm: Can't connect to the shared memory manager\n");
   exit(20);
  }  
  bd_message.cmd = BACK_DOOR_EXIT;
  want_trans = sizeof(bd_message);
  act_trans = writen(ux_sockfd,(char *)&bd_message, want_trans);
  if(act_trans < want_trans)
  {
   printf("dipcshm: ERROR. Sent %d from %d fytes  BECAUSE %s",
            act_trans,want_trans,strerror(errno));        
   exit(20);
  }
  printf("Remove Request Sent\n"); 
 }  
 else printf("dipcshm: ERROR. Unknown Command\n");
}
