/*
 * shm_man.c
 *
 * part of dipcd source code
 *
 * Copyright (C) Kamran Karimi
 */
	
#include "dipcd.h"
#include "funcs.h"

char back_ux_name[80];

/* shm_man is called by the employer that serviced the successful shmget() 
   system call. When an employer find out that it has helped make a machine 
   the owner of a shared memory, it will call the shm_man, to manage this 
   memory segment. */

/* for reader and writer queues */
#define SHM_READ  0
#define SHM_WRITE 1

/* There are two separete queues for readers and writers. When a machine 
   sends a request to the shared memory manager, it is put in the 
   appropriate WANT_ACCESS queue. After some time (or may be immediately) 
   the manager will try to satisfy the request. It may revoke other 
   machines' access to the shared memory and send the shared memory 
   contents to the requester. The manager then places the machine in the 
   PENDING queue and waits for an aknowledgment from it, to make sure it 
   has started using the shared memory. If the request does not arrive 
   within the shm_ak_timeout, it is assumed that the machine has some 
   problems, and its request is discarded. Otherwise it is turned into an
   ACCESSOR */

#define WANT_ACCESS 0
#define PENDING     1
#define ACCESSOR    2

extern struct sockaddr_in MY_ADDRESS, REFEREE_ADDRESS;
extern int verbose;
extern int transfer_mode;
extern int dipc_page_size;
extern int debug_protocol;
extern int debug_conversion;
extern int global_timeout;
extern int shm_ak_timeout;
extern double shm_hold_timeout;

extern char dipcd_home[80];

static clock_t last_change; /* tick value at last shm change */
static double ticks_sec; /* ticks per second */

static int ux_sockfd;
static char ux_name[80]; 

struct accessor {
 struct sockaddr_in address;
 int pid;
 int offset, mode;
 time_t time;
 struct accessor *next,*prev;
};

/* six queues: 3 for WANT_ACCESS, PENDING and ACCESSOR readers,
  and 3 similar queues for writers */

struct shm_page
{
 struct accessor accessors_first[3][2];
 struct accessor *accessors_last[3][2];
} *shm_pages;

/* information about the managed shared memory  */ 
struct {
 key_t key;
 int id;
 char *attach;
 int size;
 int num_page; /* is '1' in the segment mode */
} my_shm;

static void sig_handler(int signo)
{
 if(verbose)
  log(LOC,"Quiting By Signal No. %d",signo);
 exit(5);
}

static void exit_handler(int Code, void *arg)
{ 
 unlink(ux_name);
 unlink(back_ux_name);
 if(verbose)
  log(LOC,"Exited With Code %d",Code); 
}
 
   
/* This functions removes a link, pointed to by accessor, from the designated 
   linked list, but does not free its memory.
   'role' can be one of WANT_ACCESS, PENDING and ACCESSOR.
   'type' can be one of SHM_READ and SHM_WRITE */
static void rem_accessor(int role, int type, struct accessor *accessor)
{
 int pagenum = accessor->offset / dipc_page_size;
 
 if(transfer_mode == SEGMENT)
  pagenum = 0;
 (accessor->prev)->next = accessor->next;
 if(accessor->next)
  (accessor->next)->prev = accessor->prev;
 if(shm_pages[pagenum].accessors_last[role][type] == accessor)
  shm_pages[pagenum].accessors_last[role][type] = 
                          shm_pages[pagenum].accessors_last[role][type]->prev;
}


/* add the link to the specified queue */
static void add_accessor(int role, int type, struct accessor *accessor)
{
 int pagenum = accessor->offset / dipc_page_size;

 if(transfer_mode == SEGMENT)
  pagenum = 0;
 accessor->next = NULL;
 accessor->prev = shm_pages[pagenum].accessors_last[role][type];
 shm_pages[pagenum].accessors_last[role][type]->next = accessor;
 shm_pages[pagenum].accessors_last[role][type] = accessor;
}


/* this function removes a link, pointed to by accessor, from the designated
   linked list and then frees its memory. */
static void del_accessor(int role, int type, struct accessor *accessor)
{
 rem_accessor(role, type, accessor);
 if(role == WANT_ACCESS && type == SHM_READ && accessor->next &&
    SAME_ADDRESS(&(accessor->address), MY_ADDRESS))
 {
  /* if the above conditions hold, then we tried to make the owner machine 
     a reader to satisfy another machine's request. If we are deleting this 
     request, then better delete the original one as well */
  rem_accessor(role, type, accessor->next);
  free(accessor->next);
 }
 free(accessor);
}


/* check to see if there are any machines that have not sent acknowledgement
   for shm_ak_timeout seconds */
static void check_timeout(void)
{
 int count, count2;
 struct accessor *pending, *pending2;
 static time_t last_check = 0;
 time_t curr_time = time(NULL);
 
 /* this routine may be called at intervals less than shm_ak_timeout */
 if(shm_ak_timeout <= 0 || global_timeout == 0) return;
 if(curr_time - last_check < shm_ak_timeout) return;
 for(count = 0; count < my_shm.num_page; count++)
 {
  for(count2 = 0; count2 < 2; count2++)
  {
   pending = shm_pages[count].accessors_first[PENDING][count2].next;
   while(pending)
   {
    if(curr_time - pending->time > shm_ak_timeout)
    {
     log(LOC,"ERROR: Address: %s failed to Ak %s in time",
           inet_ntoa(pending->address.sin_addr), 
           (count2 == 0) ? "Reading" : "Writing");
     pending2 = pending->prev;
     /* this is how we deal with processes who 
        don't keep their promises :-) */
     del_accessor(PENDING, count2, pending);
     pending = pending2;
    }  
    pending = pending->next;
   }
  }
 } 
 last_check = curr_time;
}


/* uses an address to find an accessor.
   shm_man deals with machines in the cluster, not individual processes */ 
static struct accessor *find_accessor(int role, int type, 
                                struct sockaddr_in address, int offset)
{
 int pagenum = offset / dipc_page_size;
 struct accessor *accessor;
 
 if(transfer_mode == SEGMENT)
  pagenum = 0; 
 accessor = shm_pages[pagenum].accessors_first[role][type].next;
 while(accessor)
 {
  if(SAME_MACHINE(&(accessor->address), address))
   break;
  accessor = accessor->next;
 }
 return accessor;
} 


/* clear it all. Used for example when a new writer forces all previous 
   readers to resign. */
static void del_all_accessors(int role, int type, int offset)
{
 int pagenum = offset / dipc_page_size;
 struct accessor *accessor, *accessor2;
 
 if(transfer_mode == SEGMENT)
  pagenum = 0; 
 accessor = shm_pages[pagenum].accessors_first[role][type].next;
 while(accessor)
 {
  accessor2 = accessor->next;
  free(accessor);
  accessor = accessor2;
 }
 shm_pages[pagenum].accessors_first[role][type].next = NULL;
 shm_pages[pagenum].accessors_last[role][type] = 
                             &shm_pages[pagenum].accessors_first[role][type];
}  


/* takes a request and changes it to a pending or accessor */
static void change_role(int new_role, int type, struct accessor *accessor)
{
 int pagenum;
 
 pagenum = accessor->offset / dipc_page_size;
 if(transfer_mode == SEGMENT)
  pagenum = 0;
 /* remove from the queue of the old role */
 if(new_role > WANT_ACCESS)
  rem_accessor(new_role - 1, type, accessor);

 /* add it to new role */
 if(SAME_ADDRESS(&(accessor->address), MY_ADDRESS))
 {
  accessor->next = shm_pages[pagenum].accessors_first[new_role][type].next;;
  accessor->prev = &shm_pages[pagenum].accessors_first[new_role][type];
  if(shm_pages[pagenum].accessors_first[new_role][type].next)
   (shm_pages[pagenum].accessors_first[new_role][type].next)->prev = accessor;
  shm_pages[pagenum].accessors_first[new_role][type].next = accessor;
  if(shm_pages[pagenum].accessors_last[new_role][type] == 
           &shm_pages[pagenum].accessors_first[new_role][type])
   shm_pages[pagenum].accessors_last[new_role][type] = accessor;
 }
 else
 { 
  accessor->next = NULL;
  accessor->prev = shm_pages[pagenum].accessors_last[new_role][type];
  shm_pages[pagenum].accessors_last[new_role][type]->next = accessor;
  shm_pages[pagenum].accessors_last[new_role][type] = accessor; 
 }
 accessor->time = time(NULL);
}


/* A brand new request for read or wirte has arrived. Put it in the 
   corresponding WANT_ACCESS queue. Won't add duplicate request from the same 
   computer. */
static BOOLEAN make_want_access(struct sockaddr_in address, int Pid, 
                                          int type, int offset, int mode)
{
 struct accessor *want_access;
 int pagenum;
 
 pagenum = offset / dipc_page_size;

 if(transfer_mode == SEGMENT)
  pagenum = 0;
   
 if(verbose)
  log(LOC,"New access request from %s to %s, pagenum: %d, mode: %s",
          inet_ntoa(address.sin_addr), (type == 0) ? "Read" : "Write",
           offset / dipc_page_size, (mode == 0) ? "Segment" : "Page"); 
 want_access = shm_pages[pagenum].accessors_first[WANT_ACCESS][type].next;
 while(want_access)
 {
  if(SAME_ADDRESS(&(want_access->address), address))
   break;
  want_access = want_access->next;
 }
 if(want_access)
 {
  log(LOC,"ERROR: Duplicate access request");
  return FAIL;
 }
 want_access = (struct accessor *) malloc(sizeof(struct accessor));
 if(!want_access)
 {
  log(LOC,"ERROR: No Mem");
  return FAIL;
 }
 want_access->address = address;
 want_access->pid = Pid;
 if(mode == SEGMENT && transfer_mode != SEGMENT)
  want_access->offset = pagenum * dipc_page_size;
 else 
  want_access->offset = offset;
 want_access->mode = mode;
 /* add it */
 change_role(WANT_ACCESS, type, want_access);  
 return SUCCESS;
}


/* Allows a requesting machine to read or write. This is used after an 
   aknowledgement has arrived. */
static BOOLEAN allow(struct sockaddr_in address, int Pid, int op, int offset)
{
 struct message message;
 
 message.address = MY_ADDRESS;
 message.request = RES_SHMMAN;
 message.pid = Pid;
 message.info.key = my_shm.key;
 message.info.arg1 = my_shm.key;
 message.info.arg2 = offset;
 message.info.arg3 = transfer_mode;
 message.info.func = op;
 message.info.type = SHM;
 message.info.dsize = 0;
 
 if (debug_protocol)
   log(LOC,"Allowing address %s to %s, pagenum %d",
                inet_ntoa(address.sin_addr), (op == WANTRD) ? "Read" : "Write",
                offset / dipc_page_size); 
 if(send_in_message(&message, address) == FAIL)
 {
  log(LOC,"ERROR: Can't Allow address %s to %s, pagenum %d",
          inet_ntoa(address.sin_addr), (op == WANTRD) ? "Read" : "Write",
           offset / dipc_page_size); 
  return FAIL;
 }
 if(verbose)
  log(LOC,"Allow address %s, pid %d to %s, pagenum %d",
          inet_ntoa(address.sin_addr), Pid, (op == WANTRD) ? "Read" : "Write",
          offset / dipc_page_size); 
 last_change = times(NULL);
 return SUCCESS;
}


/* when an aknowledge from a machine has arrived, it shows that, that 
   machine it ready to restart the stopped processes. allow it */ 
static BOOLEAN acknowledge(struct message *message)
{
 int offset = message->info.arg2;
 int res, pagenum = offset / dipc_page_size; 
 struct accessor *pending; 
 
 if(transfer_mode == SEGMENT)
  pagenum = 0; 
 pending = shm_pages[pagenum].accessors_first[PENDING][SHM_WRITE].next;
 if(pending)
 {
  if(!SAME_ADDRESS(&(pending->address), message->address))
  {
   log(LOC,"ERROR: Ak expected from %s, but came from %s, pagenum %d",
     inet_ntoa(pending->address.sin_addr),inet_ntoa(message->address.sin_addr),
     offset / dipc_page_size);
   return FAIL;
  }
  if(verbose)
   log(LOC,"Ak from a new writer. pagenum = %d, pending pagenum %d", 
    offset / dipc_page_size, pending->offset / dipc_page_size);

  res = allow(message->address, pending->pid, WANTWR, pending->offset);
  if(res == SUCCESS)
  {
   del_all_accessors(ACCESSOR, SHM_READ, offset);
   del_all_accessors(ACCESSOR, SHM_WRITE, offset);
   change_role(ACCESSOR, SHM_WRITE, pending); 
  } 
  return res; 
 } 
 else 
 {
  /* the ak may be for a reader */
  pending = shm_pages[pagenum].accessors_first[PENDING][SHM_READ].next; 
  while(pending)
  {
   if(verbose)
    log(LOC,"Ak from a new reader. pagenum %d", offset / dipc_page_size);

   if(SAME_ADDRESS(&(message->address),pending->address))
    break; pending = pending->next;
  }
  if(pending == NULL)
  {
   log(LOC,"ERROR: Request For Unknown Reader. pagenum %d", 
                                                    offset / dipc_page_size);
   return FAIL;
  } 
  res = allow(message->address, pending->pid, WANTRD, pending->offset);
  if(res == SUCCESS)
  {
   del_all_accessors(ACCESSOR, SHM_WRITE, offset);
   change_role(ACCESSOR, SHM_READ, pending);
  } 
  return res; 
 } /* Ak for a reader */
 return SUCCESS;
}


/* tries to make a new machine the writer of the shared memory.
   It can't do this if:
   * There are no processes wanting to write.
   * There are some pending requests for read or write */
static BOOLEAN make_writer(void)
{
 int pagenum, offset;
 BOOLEAN res;
 struct message message;
 struct accessor *reader, *reader2, *prev_reader, *current_writer;
 struct accessor *want_writer;
 
 for(pagenum = 0; pagenum < my_shm.num_page; pagenum++)
 {
  offset = pagenum * dipc_page_size;
  want_writer = shm_pages[pagenum].accessors_first[WANT_ACCESS][SHM_WRITE].next;
  if(!want_writer) continue; /* return FAIL */
 
  /* no schedule if there are pending writers or readers */
  if(shm_pages[pagenum].accessors_first[PENDING][SHM_WRITE].next != NULL) 
   continue; /* return FAIL; */
  if(shm_pages[pagenum].accessors_first[PENDING][SHM_READ].next != NULL) 
   continue; /* return FAIL; */
  
  message.address = MY_ADDRESS;
  message.address2 = want_writer->address;
  message.info.arg1 = my_shm.size;
  message.info.arg2 = want_writer->offset;
  message.info.arg3 = transfer_mode;
  message.info.type = SHM;
  message.info.key = my_shm.key;
  message.request = REQ_DOWORK;
  
  current_writer = shm_pages[pagenum].accessors_first[ACCESSOR][SHM_WRITE].next;
  if(current_writer != NULL)
  {
   if(verbose)
    log(LOC,"one writer, and another wants to write");
   if(find_accessor(ACCESSOR, SHM_WRITE, want_writer->address, offset))
   {
    log(LOC,"WARNING: Writer %s Wants to Write Key %d Again", 
            inet_ntoa(current_writer->address.sin_addr), my_shm.key);
    res = allow(want_writer->address, want_writer->pid, WANTWR, 
                                    want_writer->offset);
    del_accessor(WANT_ACCESS, SHM_WRITE, want_writer); /* discard it */
    continue; /* return res; */ 
   }
   else
   {
    message.info.func = RDPROT_SND;
    if (debug_protocol) {
      log(LOC,"Provide write access:");
      log(LOC,"%s sending to %s: func %d args %d %d %d %d owner %d", 
		   inet_ntoa(message.address.sin_addr), 
		   inet_ntoa(message.address2.sin_addr), 
		   message.info.func, 
		   message.info.arg1, message.info.arg2, message.info.arg3, 
		   message.info.arg4, message.info.owner ); 
    }
    if(send_in_message(&message, current_writer->address) == FAIL)
    {
     log(LOC,"ERROR: Can't Provide Contents");
     del_accessor(WANT_ACCESS, SHM_WRITE, want_writer);
      continue; /* return FAIL; */
    }
    change_role(PENDING, SHM_WRITE, want_writer);
   }
  }  
  else
  {  /* there is no writer */ 
   /* there are no writers, handle all possible readers */
   if(verbose)
    log(LOC,"no writer, and one wants to write");
   prev_reader = find_accessor(ACCESSOR, SHM_READ, want_writer->address, offset);
   if(prev_reader && verbose)
    log(LOC,"new writer at %s is a previous reader",
             inet_ntoa(prev_reader->address.sin_addr));
   reader = shm_pages[pagenum].accessors_first[ACCESSOR][SHM_READ].next; 
   while(reader)
   {
    if(!prev_reader && SAME_ADDRESS(&(reader->address), MY_ADDRESS))
     message.info.func = RDPROT_SND;  
    else 
     message.info.func = RDPROT;
    if(prev_reader != reader) /* don't bother the future writer! */
    {
     if(verbose)
      log(LOC,"removing reader at %s", inet_ntoa(reader->address.sin_addr));
     if (debug_protocol) {
       log(LOC,"Read Protecting:");
       log(LOC,"%s sending to %s: func %d args %d %d %d %d owner %d", 
		   inet_ntoa(message.address.sin_addr), 
		   inet_ntoa(message.address2.sin_addr), 
		   message.info.func, 
		   message.info.arg1, message.info.arg2, message.info.arg3, 
		   message.info.arg4, message.info.owner ); 
     }
     if(send_in_message(&message, reader->address) == FAIL)
     {
      log(LOC,"ERROR: Can't read-protect");
      reader2 = reader->prev;
      del_accessor(ACCESSOR, SHM_READ, reader);
      reader = reader2; 
      /* return FAIL; continue... */
     }         
    } 
    reader = reader->next; 
   }
   change_role(PENDING, SHM_WRITE, want_writer);
   if(prev_reader)
   {
    /* this machine has the contents, just allow it to continue */
    if(verbose)
     log(LOC,"A Previous Reader, Allowing It To Write");
    res = allow(want_writer->address, want_writer->pid, WANTWR, 
                                      want_writer->offset);
    if(res == SUCCESS)
    {
     del_all_accessors(ACCESSOR, SHM_READ, offset);
     del_all_accessors(ACCESSOR, SHM_WRITE, offset);
     change_role(ACCESSOR, SHM_WRITE, want_writer);
    }
    else
     del_accessor(PENDING, SHM_WRITE, want_writer);
    continue; /* return res; */ 
   }
  }
 }   
 return SUCCESS;
}


/* tries to make a new machine the reader of the shared memory.
   It can't do this if:
   * There are no machines wanting to read.
   * There are other machines who want to write.
   * There are some pending requests for write */
static BOOLEAN make_reader(void)
{
 int pagenum, offset;
 BOOLEAN res;
 struct accessor *want_reader;
 struct message message;
 struct accessor *owner_reader;
 struct accessor *current_writer;

 for(pagenum = 0; pagenum < my_shm.num_page; pagenum++)
 { 
  offset = pagenum * dipc_page_size;
  
  want_reader = shm_pages[pagenum].accessors_first[WANT_ACCESS][SHM_READ].next;
  if(!want_reader) 
   continue; /* return FAIL; */
 
  if(shm_pages[pagenum].accessors_first[WANT_ACCESS][SHM_WRITE].next) 
   continue; /* return FAIL; */
  if(shm_pages[pagenum].accessors_first[PENDING][SHM_WRITE].next != NULL) 
   continue; /* return FAIL; */
 
  if( !SAME_MACHINE(&want_reader->address, MY_ADDRESS) &&
      !find_accessor(ACCESSOR, SHM_WRITE, MY_ADDRESS, offset) &&
      !find_accessor(ACCESSOR, SHM_READ, MY_ADDRESS, offset))
  {    
   /* someone wants to read, but I don't have the contents, so make 
      me a reader too! I'll come back to the original request the next time 
      this function is called. But what happens if by that time a new machine 
      wants to write this shared memory? */
   if(make_want_access(MY_ADDRESS, 0, SHM_READ, offset, 
                                               want_reader->mode) == FAIL)
   {
    log(LOC,"ERROR: Can not make the owner a candidate to read");
    continue; /* return FAIL; */
   }
   want_reader = shm_pages[pagenum].accessors_first[WANT_ACCESS][SHM_READ].next;      
  } 
  message.request = REQ_DOWORK;
  message.address = MY_ADDRESS;
  message.address2 = want_reader->address;
  message.info.key = my_shm.key;
  message.info.arg1 = my_shm.size;
  message.info.arg2 = want_reader->offset;
  message.info.arg3 = transfer_mode;
  message.info.type = SHM;
  
  current_writer = shm_pages[pagenum].accessors_first[ACCESSOR][SHM_WRITE].next;
  if(current_writer != NULL)
  {
   if(verbose)
    log(LOC,"one writer, and another wants to read");

   if(SAME_MACHINE(&(want_reader->address), current_writer->address))
   {
    log(LOC,"WARNING: Writer %s Wants To Read Key %d",
            inet_ntoa(current_writer->address.sin_addr), my_shm.key);
    res = allow(want_reader->address, want_reader->pid, WANTRD,
                                       want_reader->offset);
    del_accessor(WANT_ACCESS, SHM_READ, want_reader);
    continue; /* return res; */
   }
   else
   {
    message.info.func = WRPROT_SND; /* writer will send the contents */
    if (debug_protocol) {
      log(LOC,"Write Protecting ...");
      log(LOC,"%s sending to %s: func %d args %d %d %d %d owner %d", 
		   inet_ntoa(message.address.sin_addr), 
		   inet_ntoa(message.address2.sin_addr), 
		   message.info.func, 
		   message.info.arg1, message.info.arg2, message.info.arg3, 
		   message.info.arg4, message.info.owner ); 
    }
    if(send_in_message(&message, current_writer->address) == FAIL)
    {
     log(LOC,"ERROR: Can't Force To Write Protect");
     del_accessor(WANT_ACCESS, SHM_READ, want_reader);
     continue; /* return FAIL; */
    }
    /* the previous writer is a reader now */
    rem_accessor(ACCESSOR, SHM_WRITE, current_writer);
    add_accessor(ACCESSOR, SHM_READ, current_writer);
    if(verbose)
     log(LOC,"write protect request sent to writer");
    change_role(PENDING, SHM_READ, want_reader); 
   }
  } /* if there is a writer */ 
  else
  { /* no writer, just add it to readers and send it the contents */
   if(verbose)
    log(LOC,"no writer, and one wants to read");
   if(find_accessor(ACCESSOR, SHM_READ, want_reader->address, offset))
   {
    log(LOC,"WARNING: Reader %s Wants To Read Key %d again",
            inet_ntoa(want_reader->address.sin_addr), my_shm.key);
    res = allow(want_reader->address, want_reader->pid, WANTRD, 
                                      want_reader->offset);
    del_accessor(WANT_ACCESS, SHM_READ, want_reader);
    continue; /* return res; */
   }
   else
   {
    message.info.func = NOP_SND;
    owner_reader = find_accessor(ACCESSOR, SHM_READ, MY_ADDRESS, offset);
    if(owner_reader == NULL)
    {
     log(LOC,"ERROR: owner Machine Is Not A Reader");
     del_accessor(WANT_ACCESS, SHM_READ, want_reader);
     continue; /* return FAIL; */
    }
    if (debug_protocol) {
      log(LOC,"Provide read access:");
      log(LOC,"%s sending to %s: func %d args %d %d %d %d owner %d", 
		  inet_ntoa(message.address.sin_addr), 
		  inet_ntoa(message.address2.sin_addr), 
		  message.info.func, 
		  message.info.arg1, message.info.arg2, message.info.arg3, 
		  message.info.arg4, message.info.owner ); 
    }
    if(send_in_message(&message, owner_reader->address) == FAIL)
    {
     log(LOC,"ERROR: Can't Provide Contents");
     del_accessor(WANT_ACCESS, SHM_READ, want_reader);
     continue; /* return FAIL; */
    } 
    change_role(PENDING, SHM_READ, want_reader);
   }
  }
 } 
 return SUCCESS;
}


/* see what the incoming message wants to say */
static void work_on(int ux_sock, struct message *message)
{
 if(message->info.key == my_shm.key)
 {
  switch(message->info.func)
  {
   case WANTWR:
    if(verbose)
     log(LOC,"WANTWR from %s", inet_ntoa(message->address.sin_addr));
    make_want_access(message->address, message->pid, SHM_WRITE, 
                                 message->info.arg2, message->info.arg3);
    break;
  
   case WANTRD: 
    if(verbose)
     log(LOC,"WANTRD from %s", inet_ntoa(message->address.sin_addr));
    make_want_access(message->address, message->pid, SHM_READ,
                                     message->info.arg2, message->info.arg3);
    break;
  
   case AK:
    if(verbose)
     log(LOC,"AK from %s", inet_ntoa(message->address.sin_addr));
    acknowledge(message);
    break;
  
   case DETACH:
    if(verbose)
     log(LOC,"DETACH from %s", inet_ntoa(message->address.sin_addr));
    if(shmdt(my_shm.attach) < 0)
     log(LOC,"ERROR: shmdt() failed  BECAUSE %s",strerror(errno));

    exit(0);
    break;  
   default:
    log(LOC,"ERROR: Unknown Request from %s",
            inet_ntoa(message->address.sin_addr));
    break;
  }
 }
 else
  log(LOC,"ERROR: From %s: Key %d Is Not Mine (%d)",
       inet_ntoa(message->address.sin_addr), message->info.key, my_shm.key);

}


static void return_result(struct dipc_info *di, char *data)
{
 void *pt[2];
 
 pt[0] = di;
 pt[1] = data;
 if(dipc(di->pid, DIPC_RETWORK, (int *)pt) < DIPC_SUCCESS)
  log(LOC,"ERROR: DIPC_RETWORK failed");
}


/* the back_door control center. It receives request from the UNIX domain 
   socket and executes them. This is for 'out of band' control of the 
   shm_man,  without the need to redesign it. The work here should be done 
   quickly, because while the shm_man is handling back_door requests, it 
   won't be able to handle normal, more important requests. */
static void bd_work_on(int ux_sockfd, struct back_door_message *bd_message)
{
 struct accessor *ac;
 struct back_door_message bd_response;
 int count, type1, type2, want_trans, act_trans;
 
 switch(bd_message->cmd)
 {
  case BACK_DOOR_INFO:
   if(verbose)
    log(LOC,"back door: request for information\n");
   /* display the information for shared memories, message queues, and
      semaphore sets */
   for(count = 0; count < my_shm.num_page; count++)
   {   
    for(type1 = 0; type1 < 3; type1++)
    {
     for(type2 = 0; type2 < 2; type2++)
     {
      ac = shm_pages[count].accessors_first[type1][type2].next;
      while(ac)
      {
       bd_response.arg1 = type1; /* WANT_ACCESS, PENDING, ACCESOR */
       bd_response.arg2 = type2; /* SHM_READ, SHM_WRITE */
       bd_response.arg3 = count; /* segment no. */
       bd_response.arg4 = 0;
       bd_response.arg5 = ac->address.sin_addr.s_addr;
       want_trans = sizeof(bd_response);
       act_trans = writen(ux_sockfd, (char *)&bd_response, want_trans, NULL);
       if(act_trans < want_trans)
       {
        log(LOC,"ERROR: sent %d From %d Bytes  BECAUSE %s",
             act_trans, want_trans,strerror(errno));
        break;        
       }
       ac = ac->next;
      } 
      if(ac != NULL) break;
     }
     if(ac != NULL) break;
    }
   } 
   if(type1 == 3)
   {
    /* this signals the end of information */ 
    bd_response.arg1 = -1;
    want_trans = sizeof(bd_response);
    act_trans = writen(ux_sockfd, (char *)&bd_response, want_trans, NULL);
    if(act_trans < want_trans)
     log(LOC,"ERROR: sent %d From %d Bytes  BECAUSE %s",
           act_trans, want_trans,strerror(errno));
   }
   break;
 
  case BACK_DOOR_EXIT:
   exit(0); 
      
  default:
   log(LOC,"ERROR: Unknown back door request %d\n",bd_message->cmd);
 }
}


void shm_man(struct dipc_info *di, char *data)
{
 int timeout;
 int count, count2, count3;
 struct timeval TimeVal;
 fd_set rSelFD;
 int ux_newsockfd;
 int want_trans, act_trans;
 int ux_client_len, ux_len;
 struct sockaddr_un ux_client_addr, ux_addr;
 struct message message;
 struct sigaction sact;
 struct sockaddr_un back_ux_addr, back_ux_client_addr;
 int back_ux_len, back_ux_client_len;
 int back_ux_sockfd, back_ux_newsockfd;
 struct back_door_message bd_message;

 int key = di->key;
 struct accessor *first_writer;
 
 if(verbose)
  log(LOC,"shm_man Started: key %d",key);

 sact.sa_handler = sig_handler;
 sigemptyset(&sact.sa_mask);
 sact.sa_flags = 0;
 
 if(sigaction(SIGINT, &sact, NULL) < 0)
  log(LOC,"ERROR: Can't Catch Signal  BECAUSE %s",strerror(errno));
 if(sigaction(SIGTERM, &sact, NULL) < 0)
  log(LOC,"ERROR: Can't Catch Signal  BECAUSE %s",strerror(errno));
 if(sigaction(SIGSEGV, &sact, NULL) < 0)
  log(LOC,"ERROR: Can't Catch Signal BECAUSE %s",strerror(errno));
 if(on_exit(exit_handler, NULL) < 0)
  log(LOC,"ERROR: Can't Catch Exit  BECAUSE %s",strerror(errno));

 /* prepare the "back door" socket */
 sprintf(back_ux_name,"%s/%s.%d", dipcd_home, SHM_BACKDOOR, key);
 bzero((char *)&back_ux_addr,sizeof(back_ux_addr));
 back_ux_addr.sun_family = AF_UNIX;
 strcpy(back_ux_addr.sun_path, back_ux_name);
 back_ux_len = strlen(back_ux_addr.sun_path) + sizeof(back_ux_addr.sun_family); 

 unlink(back_ux_name); /* just in case... */
 back_ux_sockfd = plug(AF_UNIX, (struct sockaddr *)&back_ux_addr, back_ux_len);
 if(back_ux_sockfd < 0)
 { 
  log(LOC,"ERROR: Can't Create Socket %s",back_ux_name);
  di->arg1 = -errno;
  return_result(di, data);
  exit(20);
 }
 sprintf(ux_name,"%s/DIPC_SHM.%d", dipcd_home, di->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);

 unlink(ux_name); /* just in case */
 ux_sockfd = plug(AF_UNIX, (struct sockaddr *) &ux_addr, ux_len);
 if(ux_sockfd < 0)
 {
  log(LOC,"ERROR: Can't Create Socket %s",ux_name);
  di->arg1 = -errno;
  return_result(di, data);
  exit(20);
 }
 ticks_sec = (double) sysconf(_SC_CLK_TCK);
 my_shm.key = key;
 my_shm.id = shmget(key, 0, IPC_DIPC);
 if(my_shm.id < 0)
 {
  log(LOC,"ERROR: shmget() failed  BECAUSE %s",strerror(errno));
  di->arg1 = -errno;
  return_result(di, data);
  exit(20);
 }
 my_shm.attach = shmat(my_shm.id, NULL, 0);
 if(my_shm.attach == (char *) -1)
 {
  log(LOC,"ERROR: shmat() failed  BECAUSE %s",strerror(errno));
  di->arg1 = -errno;
  return_result(di, data);
  exit(20);
 } 
 my_shm.size = di->arg2;
 if(transfer_mode != SEGMENT)
  my_shm.num_page = my_shm.size / dipc_page_size + 1;
 else
  my_shm.num_page = 1; 
 
 shm_pages = (struct shm_page *) 
                  malloc(sizeof(struct shm_page) * my_shm.num_page);
 
 for(count = 0; count < my_shm.num_page; count++)
 {
  for(count2 = 0; count2 < 3; count2++) 
  {
   for(count3 = 0; count3 < 2; count3++)
   {
    shm_pages[count].accessors_first[count2][count3].next = NULL;
    shm_pages[count].accessors_first[count2][count3].prev = NULL;
    shm_pages[count].accessors_last[count2][count3] = 
                                  &shm_pages[count].accessors_first[count2][count3];
   }
  }
 }  
 /* this machine is the first writer. */
 for(count = 0; count < my_shm.num_page; count++)
 {
  int offset = count * dipc_page_size;
  
  make_want_access(MY_ADDRESS, 0, SHM_WRITE, offset, transfer_mode); 
  first_writer = shm_pages[count].accessors_first[WANT_ACCESS][SHM_WRITE].next;
  change_role(PENDING, SHM_WRITE, first_writer);
  change_role(ACCESSOR, SHM_WRITE, first_writer);
 }
 /* continue where the employer left.
    this should have been done by the employer that started the shm_man. 
    This is done here, so as to make sure the shm_man is all setup and 
    ready to function. In this way when the original process creating 
    the shared memory is restarted, there is a manager ready to handle 
    the requests */
 return_result(di, data);
 
 while(1)
 {
  timeout = 0;
  TimeVal.tv_sec = 0;
  TimeVal.tv_usec = shm_hold_timeout * 1000000; 
  FD_ZERO(&rSelFD);
  FD_SET(ux_sockfd,&rSelFD);
  FD_SET(back_ux_sockfd, &rSelFD);
  
  if(select(FD_SETSIZE, &rSelFD, NULL, NULL, &TimeVal) < 0)
  {
   log(LOC,"ERROR: select() failed  BECAUSE %s",strerror(errno));
   exit(20);
  }
  if(FD_ISSET(ux_sockfd,&rSelFD))
  {
   if(verbose)
    log(LOC,"Call from UNIX socket");
   ux_client_len = sizeof(ux_client_addr);
   if((ux_newsockfd = accept(ux_sockfd, (struct sockaddr *)&ux_client_addr,
                                                       &ux_client_len)) < 0)
   {
    log(LOC,"ERROR: accept() Failed  BECAUSE %s",strerror(errno));
    close(ux_sockfd);
    exit(20);
   } 
   want_trans = sizeof(message);
   act_trans = readn(ux_newsockfd,(char *)&message, want_trans);
   if(act_trans < want_trans)
    log(LOC,"ERROR: Received %d From %d Bytes  BECAUSE %s",
            act_trans, want_trans,strerror(errno));
   else
    work_on(ux_newsockfd, &message);
   close(ux_newsockfd);
  }
  else if(FD_ISSET(back_ux_sockfd,&rSelFD))
  {
   if(verbose)
    log(LOC,"back door call");
   back_ux_client_len = sizeof(back_ux_client_addr);
   if((back_ux_newsockfd = accept(back_ux_sockfd, 
         (struct sockaddr *)&back_ux_client_addr, &back_ux_client_len)) < 0)
   {
    log(LOC,"ERROR: accept() Failed  BECAUSE %s",strerror(errno));
    close(back_ux_newsockfd);
   }
   else
   {
    want_trans = sizeof(bd_message);
    act_trans = readn(back_ux_newsockfd,(char *)&bd_message, want_trans);
    if(act_trans < want_trans)
     log(LOC,"ERROR: Received %d From %d Bytes  BECAUSE %s",
            act_trans, want_trans,strerror(errno));
    else
     bd_work_on(back_ux_newsockfd, &bd_message);
    close(back_ux_newsockfd);
   } 
  } /* if back door */
  else
   timeout = 1;
  /* timeout variable is used to prevent the unlikely event of a
     wrap-around in times().
     Either a time out, or a new request arrived.
     see if can schedule a new reader or witer */
  if(timeout || ((times(NULL) - last_change) / ticks_sec) > shm_hold_timeout)
  {
   check_timeout();
   /* first the writers, then the readers */
   make_writer();
   make_reader();
  }
 } /* while */  
}
