/*
 * dipcker.c
 *
 * Allows the user to see and delete some information about IPC structures
 * in the local kernel.
 *
 * By Kamran Karimi
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#ifndef __GLIBC__
#include <linux/ipc.h>
#include <linux/shm.h>
#include <linux/sem.h>
#include <linux/msg.h>
#else
#define SHMMNI 128
#define MSGMNI 128
#define SEMMNI 128
#endif
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>

#include <arpa/inet.h>
#include <linux/dipc.h>

#ifdef __GLIBC__
#define IPC_DIPC   00010000   /* make it distributed */
#endif

#ifndef SHM_STAT
#define SHM_STAT 13
#define SEM_STAT 18
#define MSG_STAT 11
#endif

struct info_link
{
 int type;
 union
 {
  struct shmid_ds shm;
  struct semid_ds sem;
  struct msqid_ds msg;
 } info;
 struct info_link *next, *prev;
}; 

struct info_link first_link;
struct info_link *last_link;

void main(int argc, char **argv)
{
 struct passwd *pw;
 int key, id, shmid, semid, msgid;
 int foundshm, foundsem, foundmsg;
 struct shmid_ds shm;
 struct semid_ds sem;
 struct msqid_ds msg;
 struct in_addr addr;
 char *errch;
 struct info_link *il;

 if(argc < 2)
 {
  printf("USAGE:\n       %s info\n", argv[0]);
  printf("       %s rm <type> <key>\n", argv[0]);
  printf("\n<type> can be one of 'msg', 'sem' or 'shm'\n");
  printf("<key> should be a numerical value\n\n");
  exit(0);
 }
 first_link.next = first_link.prev = NULL;
 first_link.type = -1;
 last_link = &first_link;
 if(!strcasecmp(argv[1], "info"))
 {  
  for(id = 0; id < SHMMNI; id++)
  {
   if(shmctl(id, SHM_STAT, &shm) != -1)
   {
    il = malloc(sizeof(struct info_link));
    if(il == NULL)
    {
     printf("dipcker: Out of memory\n");
     break;
    }
    il->type = SHM;
    il->info.shm = shm;
    il->next = NULL;
    il->prev = last_link;
    last_link->next = il;
    last_link = il;
   }
  }
  for(id = 0; id < MSGMNI; id++)
  {
   if(msgctl(id, MSG_STAT, &msg) != -1) 
   {
    il = malloc(sizeof(struct info_link));
    if(il == NULL)
    {
     printf("dipcker: Out of memory\n");
     break;
    }
    il->type = MSG;
    il->info.msg = msg;
    il->next = NULL;
    il->prev = last_link;
    last_link->next = il;
    last_link = il;
   }
  }
  for(id = 0; id < SEMMNI; id++)
  {
   if(semctl(id, 0, SEM_STAT, (union semun) &sem) != -1)
   {
    il = malloc(sizeof(struct info_link));
    if(il == NULL)
    {
     printf("dipcker: Out of memory\n");
     break;
    }
    il->type = SEM;
    il->info.sem = sem;
    il->next = NULL;
    il->prev = last_link;
    last_link->next = il;
    last_link = il;
   }
  }
  foundshm = foundsem = foundmsg = 0;
  il = first_link.next;
  while(il)
  {
   if(il->type != il->prev->type)
   {
    if(il->type == SHM)
    {
     foundshm = 1;
     printf("\nShared Memories:\n");
     printf("Key       Owner     Perms  DIPC  Attachs   Size      DIPC owner at\n");
    }
    else if(il->type == MSG)
    {
     foundmsg = 1;
     printf("\nMessage Queues:\n");
     printf("Key       Owner     Perms  DIPC  Messages  Size      DIPC owner at\n");
    }    
    else if(il->type == SEM)
    {
     foundsem = 1;
     printf("\nSemaphore Sets:\n");
     printf("Key       Owner     Perms  DIPC  Semaphs   DIPC owner at\n");
    }    
   }
   if(il->type == SHM)
   {
    shm = il->info.shm;
    pw = getpwuid(shm.shm_perm.uid);
#ifdef __GLIBC__
    addr.s_addr = htonl((unsigned long) shm.__shm_pages); 
#else
    addr.s_addr = htonl((unsigned long) shm.shm_pages); 
#endif
    printf("%-8d  %-9.9s %-5o  %-4.4s  %-8d  %-8d  %-15.15s\n",

#ifdef __GLIBC__
          shm.shm_perm.__key, 
#else
          shm.shm_perm.key, 
#endif
    	  pw->pw_name, shm.shm_perm.mode,
          (shm.shm_perm.mode & IPC_DIPC) ? "Yes" : "No",
          shm.shm_nattch, shm.shm_segsz,  
          (addr.s_addr == -1) ? "N/A" : inet_ntoa(addr));
   }
   else if(il->type == MSG)
   {
    msg = il->info.msg;
    pw = getpwuid(msg.msg_perm.uid);
#ifdef __GLIBC__
    addr.s_addr = htonl((unsigned long) msg.__rwait);
#else
    addr.s_addr = htonl((unsigned long) msg.rwait);
#endif
    printf("%-8d  %-9.9s %-5o  %-4.4s  %-8d  %-8d  %-15.15s\n",

#ifdef __GLIBC__
            msg.msg_perm.__key, 
#else
            msg.msg_perm.key, 
#endif
            pw->pw_name, msg.msg_perm.mode,
            (msg.msg_perm.mode & IPC_DIPC) ? "Yes" : "No",
            msg.msg_qnum, msg.msg_cbytes, 
            (addr.s_addr == -1) ? "N/A" : inet_ntoa(addr));
   }   
   else if(il->type == SEM)
   {
    sem = il->info.sem;
    pw = getpwuid(sem.sem_perm.uid);
#ifdef __GLIBC__
    addr.s_addr = htonl((unsigned long) sem.__undo);
#else
    addr.s_addr = htonl((unsigned long) sem.undo);
#endif
    printf("%-8d  %-9.9s %-5o  %-4.4s  %-8d  %-15.15s\n", 

#ifdef __GLIBC__
            sem.sem_perm.__key, 
#else
            sem.sem_perm.key, 
#endif
      	    pw->pw_name, sem.sem_perm.mode,
            (sem.sem_perm.mode & IPC_DIPC) ? "Yes" : "No",
            sem.sem_nsems, 
            (addr.s_addr == -1) ? "N/A" : inet_ntoa(addr));
   }
   il = il->next;   
  }
  if(foundshm == 0)
   printf("\nNo shared memory was found in this computer\n");
  if(foundmsg == 0)
   printf("\nNo message queue was found in this computer\n");
  if(foundsem == 0)
   printf("\nNo semaphore set was found in this computer\n");

  printf("\n");  
  il = first_link.next;
  while(il)
  {
   last_link = il->next;
   free(il);
   il = last_link;
  }
 }
 
 /******  removal ******/
 else if(!strcasecmp(argv[1], "rm"))
 {
  if(argc != 4)
  {
   printf("Wrong number of argumants\n");
   exit(20);
  } 
  key = strtol(argv[3], &errch, 10);
                            if(errch == argv[3])
  {
   printf("dipcker: ERROR. Invalid Key\n");
   exit(20);
  }
  if(!strcasecmp(argv[2], "shm"))
  {
   shmid = shmget(key, 0, 0); /* first try non-DIPC */
   if(shmid < 0)
   {
    shmid = shmget(key, 0, IPC_DIPC);
    if(shmid < 0)
    {
     printf("dipcker: shmget() failed, BECAUSE: %s\n", strerror(errno));
     exit(20);
    }
   }
   if(shmctl(shmid, IPC_RMID, NULL) < 0)
   {
    printf("dipcker: shmctl() failed, BECAUSE: %s\n", strerror(errno)); 
    exit(20);
   }
   printf("shared memory was successfully removed\n");
  }
  else if(!strcasecmp(argv[2], "msg"))
  {
   msgid = msgget(key, 0); /* first try non-DIPC */
   if(msgid < 0)
   {
    msgid = msgget(key, IPC_DIPC);
    if(msgid < 0)
    {
     printf("dipcker: msgget() failed, BECAUSE: %s\n", strerror(errno));
     exit(20);
    }
   }
   if(msgctl(msgid, IPC_RMID, NULL) < 0)
   {
    printf("dipcker: msgctl() failed, BECAUSE: %s\n", strerror(errno));
    exit(20);
   }
   printf("message queue was successfully removed\n");
  }
  else if(!strcasecmp(argv[2], "sem"))
  {
   semid = semget(key, 0, 0);   /* first try non-DIPC */
   if(semid < 0)
   {
    semid = semget(key, 0, IPC_DIPC);
    if(semid < 0)
    {
     printf("dipcker: semget() failed, BECAUSE: %s\n", strerror(errno));
     exit(20);
    }
   }
   if(semctl(semid, 0, IPC_RMID, NULL) < 0)
   {
    printf("dipcker: semctl() failed, BECAUSE: %s\n", strerror(errno));
    exit(20);
   }
   printf("semaphore set was successfully removed\n");
  } 
  else
   printf("dipcker: Unknown IPC structure\n");  
 }
 else
  printf("dipcker: Unknown command\n");
}
