/*

 shmalloc.c

 Author: Liraz Siri <liraz@bigfoot.com>

 Copyright (c) 1998 Liraz Siri <liraz@bigfoot.com> Ariel, Israel,
 		    All rights reserved.

 Created: Tue Sep 29 05:24:07 GMT 1998

 Code for handling shared memory segments, allocation, free resources, etc.
*/

#include <stdio.h>
#include "shmalloc.h"
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>

struct shmseg {
 int shmid;
 struct shmseg *next;
};

int DESTROY_SHM = 1;
struct shmseg *shmseg_root = NULL;

/* link shm_destroy to the exit procedures */
void shmalloc_init(void)
{
 atexit(shm_destroy);
}

/* Find a free not previously allocated shared memory segment and attach
   to it. */

char *shmalloc(int len)
{
 int shmid, shmkey;

 shmkey = SHMALLOC_KEY;

repeat:
 if((shmid = shmget(shmkey, len, 0600 | IPC_CREAT | IPC_EXCL)) < 0)
  switch(errno) {
   case EINVAL :
   case ENOSPC :
   case ENOMEM : return NULL;

   case EEXIST : shmkey++;
    		 goto repeat;

  }

 shm_link_destroy(shmid);
 return shmat(shmid, NULL, 0);
}

/* update our dynamic list of shmid's */
void shm_link_destroy(int shmid)
{
 struct shmseg **shmseg_node;

 /*-- link through to the last NULL link --*/
 for(shmseg_node = &shmseg_root; (*shmseg_node) != NULL;
     shmseg_node=&((*shmseg_node) -> next));

 *shmseg_node = (struct shmseg *)malloc(sizeof(struct shmseg));
 (*shmseg_node)->shmid = shmid;
 (*shmseg_node)->next = NULL;
}

/* mark the segments as destroyed. they'll actually be freed when the
   last user detaches the segment */
void shm_destroy(void)
{
 struct shmseg *shmseg_node;

 if(DESTROY_SHM)
  for(shmseg_node = shmseg_root; shmseg_node != NULL;
      shmseg_node = shmseg_node->next) 
   shmctl(shmseg_node->shmid, IPC_RMID, NULL);
}
