/*
 * dipcker.c
 *
 * Allows the user to see and delete some information about IPC structures
 * in the local kernel. Part of dipcx program.
 *
 * 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 <linux/dipc.h>

#include "dipcx.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


static char types[3][15] = 
                      {"Shared Memory", "Message Queue", "Semaphore Set " };

static struct info_link
{
 int type;
 int key;
 int num;
 union
 {
  struct shmid_ds shm;
  struct semid_ds sem;
  struct msqid_ds msg;
 } info;
 struct info_link *next, *prev;
} first_link;
static struct info_link *last_link;

static int first_time = 1;

int get_kernel_list(void)
{
 int id, shmid, semid, msgid;
 int panel_num;
 struct shmid_ds shm;
 struct semid_ds sem;
 struct msqid_ds msg;
 struct info_link *il;

 if(!first_time)
 {
  il = first_link.next;
  while(il)
  {
   last_link = il->next;
   free(il);
   il = last_link;
  } 
 }
 first_link.next = first_link.prev = NULL;
 first_link.type = -1;
 first_link.key = -1;
 last_link = &first_link;
 
 first_time = 0;
   
 for(id = 0; id < SHMMNI; id++)
 {
  shmid = shmctl(id, SHM_STAT, &shm);
  if(shmid != -1)
  {
   il = malloc(sizeof(struct info_link));
   if(il == NULL)
   {
    show_ok_notice("Out of memory");
    break;
   }
   il->type = SHM;
   il->key = 
#ifdef __GLIBC__
    shm.shm_perm.__key;
#else
   shm.shm_perm.key; 
#endif
   il->info.shm = shm;
   il->next = NULL;
   il->prev = last_link;
   last_link->next = il;
   last_link = il;
  }
 } 
 for(id = 0; id < MSGMNI; id++)
 {
  msgid = msgctl(id, MSG_STAT, &msg);
  if(msgid != -1) 
  {
   il = malloc(sizeof(struct info_link));
   if(il == NULL)
   {
    show_ok_notice("Out of memory");
    break;
   }
   il->type = MSG;
   il->key =
#ifdef __GLIBC__
    msg.msg_perm.__key;
#else
   msg.msg_perm.key; 
#endif   
   il->info.msg = msg;
   il->next = NULL;
   il->prev = last_link;
   last_link->next = il;
   last_link = il;
  }
 }
 for(id = 0; id < SEMMNI; id++)
 {
  semid = semctl(id, 0, SEM_STAT, (union semun)&sem);
  if(semid != -1)
  {
   il = malloc(sizeof(struct info_link));
   if(il == NULL)
   {
    show_ok_notice("Out of memory");
    break;
   }
   il->type = SEM;
   il->key =
#ifdef __GLIBC__
    sem.sem_perm.__key;
#else
   sem.sem_perm.key; 
#endif      
   il->info.sem = sem;
   il->next = NULL;
   il->prev = last_link;
   last_link->next = il;
   last_link = il;
  }
 }
 
 panel_num = -1;
 il = first_link.next;
 while(il)
 {
  char buff[80];
 
  panel_num++;
  il->num = panel_num;   
  /* printf(" Type   Key   DIPC\n"); */
  sprintf(buff, "%s   %d", types[il->type], il->key);  
  
  xv_set(panel_list, 
   PANEL_LIST_INSERT, panel_num,
   PANEL_LIST_STRING, panel_num, buff,
   PANEL_LIST_CLIENT_DATA, panel_num, panel_num,
   NULL);
      
  il = il->next;
 }
 return panel_num;
}


int get_kernel_info(int num)
{
 char buff[80], buff2[80], buff3[80], buff4[80];
 int distrib;
 struct shmid_ds shm;
 struct semid_ds sem;
 struct msqid_ds msg;
 struct in_addr addr;
 struct info_link *il;
 struct passwd *pw;
 
 il = first_link.next;
 while(il)
 {
  if(il->num == num) break;
  il = il->next;
 }
 if(il == NULL)
 {
  show_ok_notice("The requested entry was not found");
  return FAIL;
 } 
 
 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
  sprintf(buff4, "Size: %d    Attaches: %d", 
                                  shm.shm_segsz, shm.shm_nattch);
  distrib = shm.shm_perm.mode;
 } 
 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
  sprintf(buff4, "Message Bytes: %d    Messages: %d", 
                    msg.msg_cbytes, msg.msg_qnum);
  distrib = msg.msg_perm.mode;
 } 
 else
 {
  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
  sprintf(buff4,"Number of Semaphores: %d", sem.sem_nsems);
  distrib = sem.sem_perm.mode;
 }
 sprintf(buff," Type: %s   Key: %d", types[il->type], il->key);
 sprintf(buff2,"Owner: %s    Permissions: %o", pw->pw_name, distrib);
 sprintf(buff3, "Distributed: %s     DIPC Owner at: %s",
                          (distrib & IPC_DIPC) ? "Yes" : "No",
                          (addr.s_addr == -1) ? "N/A" : inet_ntoa(addr));
 
 show_ok_4_notice(buff, buff2, buff3, buff4);
 return SUCCESS;
}
 
int rem_kernel_info(int num)
{ 
 int key, shmid, semid, msgid;
 struct info_link *il;

 il = first_link.next;
 while(il)
 {
  if(il->num == num) break;
  il = il->next;
 }
 if(il == NULL)
 {
  show_ok_notice("The requested entry was not found");
  return FAIL;
 } 
  
 key = il->key;
 
 if(il->type == SHM)
 {
  shmid = shmget(key, 0, 0);
  if(shmid < 0)
  {
   shmid = shmget(key, 0, IPC_DIPC);
   if(shmid < 0)
   {
    show_ok_2_notice("shmget() failed", strerror(errno));
    return FAIL;
   }
  }
  if(shmctl(shmid, IPC_RMID, NULL) < 0)
  {
   show_ok_2_notice("shmctl() failed", strerror(errno)); 
   return FAIL;
  }
  /* printf("shared memory was successfully removed\n"); */
 } 
 else if(il->type == MSG)
 {
  msgid = msgget(key, 0);
  if(msgid < 0)
  {
   msgid = msgget(key, IPC_DIPC);
   if(msgid < 0)
   {
    show_ok_2_notice("msgget() failed", strerror(errno));
    return FAIL;
   }
  } 
  if(msgctl(msgid, IPC_RMID, NULL) < 0)
  {
   show_ok_2_notice("msgctl() failed", strerror(errno));
   return FAIL;
  }
   /* printf("message queue was successfully removed\n"); */
 }
 else if(il->type == SEM)
 {
  semid = semget(key, 0, 0);
  if(semid < 0)
  {
   semid = semget(key, 0, IPC_DIPC);
   if(semid < 0)
   {
    show_ok_2_notice("semget() failed", strerror(errno));
    return FAIL;
   }
  }
  if(semctl(semid, 0, IPC_RMID, NULL) < 0)
  {
   show_ok_2_notice("semctl() failed", strerror(errno));
   return FAIL;
  }
   /* printf("semaphore set was successfully removed\n"); */
 } 
 else
 {
  show_ok_notice("Unknown IPC structure");
  return FAIL;
 }
 
 xv_set(panel_list, 
   PANEL_LIST_DELETE, num,
   NULL);
   
 return SUCCESS;
}


int is_shm(int num)
{
 struct info_link *il;

 il = first_link.next;
 while(il)
 {
  if(il->num == num) break;
  il = il->next;
 }
 if(il == NULL || il->type != SHM)
  return 0;
 return 1;
} 

int shm_key(int num)
{
 struct info_link *il;

 il = first_link.next;
 while(il)
 {
  if(il->num == num) break;
  il = il->next;
 }
 if(il == NULL || il->type != SHM)
  return -1;
 return il->key;
} 
 
