/*
 * referee.c
 *
 * part of dipcd source code
 *
 * Copyright (C) Kamran Karimi
 */

#include "dipcd.h"
#include "funcs.h"

char back_ux_name[80];

/* for the status field. All received queries first have the REF_WAITING 
   state. After an answer is sent to them, they are in REF_ANSWERED state.
   When the successfully create the IPC structure, they will be promoted to
   REF_NORMAL state */
#define REF_WAITING  0
#define REF_ANSWERED 1
#define REF_NORMAL   2

extern struct sockaddr_in REFEREE_ADDRESS;

extern int verbose;
extern int network_byte_order;
extern int debug_protocol;
extern int debug_conversion;
extern int global_timeout;
extern int referee_timeout;
extern int use_udp;
extern char dipcd_home[80];

static char ipc_type[3][4] = 
{
 "SHM",
 "MSG",
 "SEM"
};

struct dipc_list 
{
 key_t key;
 int size;
 int distrib;
 int status;
 int transfer_mode;
 int pid;
 int mode;
 time_t answer_time;
 struct sockaddr_in address;
 struct dipc_info info;
 struct dipc_list *next,*prev;
};
/* There are six queue in the referee. They are divided into two main types. 
   The three queues in each type are one for shared memories, one for message
   queues, and one for semaphore sets. The 'wanted' linked list is for new 
   queries to search for a key. If the corresponding structures are 
   successfully created in the local, the corresponding element will be 
   brought to the 'gotten' linked list */
struct dipc_list gotten_first[3];
struct dipc_list *gotten_last[3];

struct dipc_list wanted_first[3];
struct dipc_list *wanted_last[3];


static void sighup_handler(int signo)
{
 if(verbose)
  log(LOC, "SIGHUP Received. Rereading the list of trusted computers");
 read_dipc_allow(); 
}

static void exit_handler(int Code, void *arg)
{
 unlink(back_ux_name);
 if(verbose)
  log(LOC,"Exited With Code %d",Code); 
}


static void add_to_list(struct dipc_list **last, struct dipc_list *dl)
{
 dl->next = NULL;
 dl->prev = *last;
 (*last)->next = dl;
 *last = dl;
}

static struct dipc_list *find_in_list(struct dipc_list *first, int key)
{
 struct dipc_list *dl2 = first->next;
 
 while(dl2)
 {
  if(dl2->info.key == key)
   break;
  dl2 = dl2->next;
 }
 return dl2;
}

static void remove_from_list(struct dipc_list **last, struct dipc_list *dl)
{
 if(dl == *last)
  (*last) = (*last)->prev; 
 (dl->prev)->next = dl->next;
 if(dl->next)
  (dl->next)->prev = dl->prev;
}

static struct dipc_list *get_next_key(struct dipc_list *dl)
{
 struct dipc_list *dl2 = dl->next;
 
 while(dl2)
 {
  if(dl2->info.key == dl->info.key) 
   break;
  dl2 = dl2->next;
 }
 return dl2;
} 

/* searches to see if it can find a registered key that is the same as the 
   one in the query. It sends this information to the computer that asked
   for it */ 
static BOOLEAN respond_to(struct dipc_list *query)
{
 struct message response;
 struct dipc_list *dl;
 int type;
 
 response.info = query->info;  
 response.address = REFEREE_ADDRESS;
 response.pid = query->pid;
 response.request = RES_REFEREE;

 type = query->info.type;
 dl = find_in_list(&gotten_first[type], query->key);
 if(dl)
 {
  if(verbose || debug_protocol)
   log(LOC,"Found Existing Key %d of type %s From %s",
           dl->key,ipc_type[type], inet_ntoa(dl->address.sin_addr));
  response.info.arg3 = dl->size;
  
  /* 'raw' net address as arg4 needs extra conversion! */
  response.info.arg4 = ntohl(dl->address.sin_addr.s_addr);
  if(dl->distrib)
  {
   if(verbose)
    log(LOC,"found DIPC");
   response.info.arg2 = FOUND_DIPC;
   if(dl->transfer_mode == SEGMENT)
    response.info.arg2 |= IS_SEGMENT;
  }
  else
  {
   if(verbose)
    log(LOC,"found IPC");
   response.info.arg2 = FOUND_IPC; 
  }  
  /* log(LOC,"owner address is %s",inet_ntoa(dl->address.sin_addr)); */
 }
 else
 { /* no gotten found */
  if(verbose)
   log(LOC,"key not found");
  response.info.arg3 = 0;
  
  /* 'raw' net address as arg4 needs extra conversion! */
  response.info.arg4 = ntohl(query->address.sin_addr.s_addr); /* its own address */
  response.info.arg2 = NOT_FOUND;
 } 

 if (debug_protocol) {
   log(LOC,"Referee: respond %s func %d key %d pid %d %d",
    		 inet_ntoa(query->address.sin_addr), response.info.func,
    		 response.info.key, response.info.pid, response.pid);
   log(LOC,"Referee: args %d %d %d %d owner %d", 
      		 response.info.arg1, response.info.arg2, response.info.arg3, 
     		 response.info.arg4, response.info.owner ); 
 }

 if(send_in_message(&response, query->address) == FAIL)
 {
  log(LOC,"ERROR: Can't Return Result Of Search");
  remove_from_list(&wanted_last[type], query);
  free(query);
  return FAIL;
 }
 query->status = REF_ANSWERED;
 query->answer_time = time(NULL); 
 return SUCCESS;
}


/* Places the query in the 'wanted' linked list and then conducts a search 
   in the referee linked lists. If the query can be answered, it will
   call respond_to, otherwise it has to wait for the results of earlier 
   queries */
static BOOLEAN referee_dipc_search(struct message *message)
{
 struct dipc_list *dl, *dl2;
 int type = message->info.type;
 
 if(verbose)
  log(LOC,"Handling Search For Key %d of type %s",
                      message->info.key, ipc_type[type]);
 /* we should put the request in a linked list, waiting for a commit answer. */  
 dl2 = (struct dipc_list *)malloc(sizeof(struct dipc_list));
 if(dl2 == NULL)
 {
  log(LOC,"ERROR: No Mem");
  return FAIL;
 }
 dl2->key = message->info.key;
 dl2->pid = message->pid;
 dl2->info = message->info;
 dl2->size = 0;
 dl2->transfer_mode = message->info.arg3;
 dl2->distrib = message->info.arg2;
 dl2->status = REF_WAITING;
 if(verbose)
 {
  if(dl2->distrib)
   log(LOC,"new request is DIPC");
  else
   log(LOC,"new request is IPC");
 }  
 dl2->address = message->address;
 
 /* see if another one is already waiting. do this before putting the new 
    query in the list. */
 dl = find_in_list(&wanted_first[type], message->info.key);
 /* now put the new query in the list */
 add_to_list(&wanted_last[type], dl2);
 if(dl)
 { /* found another query, the new one should wait */
  if(verbose)
   log(LOC,"Key %d Must Wait",message->info.key);    
 }
 else
 {
  /* this is the only request. answer it now */
  if(FAIL == respond_to(dl2))
   return FAIL;
 }
 return SUCCESS; 
}


/* handles the commit messages. If the structure is created in the local
   machine, the request will be put in the 'gotten' linked list. otherwise
   it is thrown away. In any case it is checked to see if other quesries
   have been waiting for this result, and they are informed accordingly. */
static BOOLEAN referee_dipc_commit(struct message *message, int restart)
{
 struct dipc_list *dl, *dl2, *dl3;
 struct message response;
 int type;
 
 if(verbose)
  log(LOC,"Handling Commit For Key %d of Type %s",
          message->info.key, ipc_type[message->info.type]);
 if (debug_protocol) {
   log(LOC,"Referee: attemp com. %s func %d key %d pid %d %d",
     		 inet_ntoa(message->address.sin_addr), message->info.func, 
     		 message->info.key, message->info.pid, message->pid);
   log(LOC,"Referee: args %d %d %d %d owner %d", 
     		 message->info.arg1, message->info.arg2, message->info.arg3, 
     		 message->info.arg4, message->info.owner ); 
 }

 type = message->info.type;             
 response = *message;
 response.address = REFEREE_ADDRESS;
 /* this may be used to inform other tasks of the search result */
  response.request = RES_REFEREE;
 /* find the corresponding in the 'wanted' linked list */
 dl = find_in_list(&wanted_first[type], message->info.key);
 while(dl)
 {
  if(SAME_MACHINE(&(dl->address),message->address))
   break;
  dl = get_next_key(dl);
 } 
 if(dl == NULL)
 {
  log(LOC,"ERROR: Commit For Non-Existing Key");
  return FAIL;
 }
 else
 {
  /* in any case remove the request from wanted list, because either it
     will be thrown away (failer) or put in another linked list (success) */ 
  remove_from_list(&wanted_last[type], dl);        
  if(message->info.arg2 > 0)
  { /* successful COMMIT. put it in existing dipc list */
   if(verbose)
    log(LOC,"Commit: Success");
   dl->status = REF_NORMAL;
   dl->mode = message->info.arg3;
   dl->size = message->info.arg2;
    
   add_to_list(&gotten_last[type], dl);         
   /* we should also return FOUND to all the other waiting tasks.
      search from the start and call respond_to */
   dl2 = find_in_list(&wanted_first[type], dl->key);
   while(dl2) 
   {
    dl3 = get_next_key(dl2);
    if(respond_to(dl2) == FAIL)
     log(LOC,"ERROR: Can't Respond To Request");
    dl2 = dl3;
   }
  }
  else
  {  /* failed commit. remove it and give a chance to another machine */
   if(verbose)
    log(LOC,"Commit: No success");
   dl2 = get_next_key(dl);
   while(dl2)
   {
    if(SUCCESS == respond_to(dl2))
     break;
    dl2 = get_next_key(dl2); 
   }
   /* destroy the failed request */
   free(dl);   
  }
 }
 
 if(restart)
 {
  /* the process sending the commit is waiting inside its local kernel.
    We have to answer it, so it can continue. */
  response = *message;
  response.address = REFEREE_ADDRESS;
  response.request = RES_REFEREE;
  if (debug_protocol) {
    log(LOC,"Referee: commit %s func %d key %d pid %d %d",
     		 inet_ntoa(message->address.sin_addr), response.info.func, 
     		 response.info.key, response.info.pid, response.pid);
    log(LOC,"Referee: args %d %d %d %d owner %d", 
     		 response.info.arg1, response.info.arg2, response.info.arg3, 
     		 response.info.arg4, response.info.owner ); 
  }
  if(send_in_message(&response, message->address) == FAIL)
  {
   /* don't worry. The worker that sent the commit message will timeout */
   log(LOC,"ERORR: Can't Restart The Commit: %s",
          inet_ntoa(dl2->address.sin_addr));
   return FAIL;
  }
 } 
 if(verbose)
  log(LOC,"key %d Commit Done",message->info.key);        
 return SUCCESS;
}

/* a request to delete a key in referee linked lists. It also checks to see
   if it should inform a shared memory manager process to detach */
static BOOLEAN referee_dipc_delkey(struct message *message)
{
 struct message response;
 struct dipc_list *dl, *dl2;
 int type;

 if(verbose)
  log(LOC,"Handling Delete Of Key %d of Type %s",
         message->info.key, ipc_type[message->info.type]);  
 type = message->info.type;                    
 dl = find_in_list(&gotten_first[type], message->info.key);
 while(dl)
 {
  if(SAME_MACHINE(&(dl->address), message->address))
   break;
  dl = get_next_key(dl);
 } 
 if(dl == NULL)
 {
  log(LOC,"ERROR: There Is No Key To Delete");
  /* return FAIL; don't return fail, so that the stoped task can resatart */
 } 
 else
 {
  if(verbose)
  {
   if(dl->mode & IPC_OWN)
    log(LOC,"This Is The Owner");
   else
    log(LOC,"This Isn't The Owner");
  }  
  remove_from_list(&gotten_last[type], dl); 
  /* now see if there is only one remaining machine with the key.
     if this is the case and the key type is SHM, send a message 
     to that machine, so that the key_manager can detach itself */
  dl2 = find_in_list(&gotten_first[type], message->info.key);   
  if(type == SHM && dl2 && get_next_key(dl2) == NULL) 
  {
   if(verbose)
    log(LOC,"Only Machine %s Has This Shared Memory Key",
           inet_ntoa(dl->address.sin_addr));  
   response = *message;
   response.address = REFEREE_ADDRESS;
   response.request = REQ_SHMMAN;
   response.info.func = DETACH;

   if (debug_protocol) {
     log(LOC,"Referee: delmgr %s func %d key %d pid %d %d",
     		   inet_ntoa(dl2->address.sin_addr), response.info.key, 
     		   response.info.key, response.info.pid, response.pid);
     log(LOC,"Referee: args %d %d %d %d owner %d", 
     		   response.info.arg1, response.info.arg2, response.info.arg3, 
     		   response.info.arg4, response.info.owner ); 
   }
   if(send_in_message(&response, dl2->address) == FAIL)
   { 
    log(LOC,"ERROR: Can't Inform Shared Memory Owner To Delete");
    /* return FAIL; don't return fail, so that the stoped task can resatart */  
   }  
  }
  free(dl); 
 }
 /* in any case, come here and send a reply to original task */
 response = *message;
 response.address = REFEREE_ADDRESS;
 response.request = RES_REFEREE;

 if (debug_protocol) {
   log(LOC,"Referee: delkey %s func %d key %d pid %d %d",
     		   inet_ntoa(message->address.sin_addr), response.info.func, 
     		   response.info.key, response.info.pid, response.pid);
   log(LOC,"Referee: args %d %d %d %d owner %d", 
     		   response.info.arg1, response.info.arg2, response.info.arg3, 
     		   response.info.arg4, response.info.owner );
 }
 if(send_in_message(&response, message->address) == FAIL)
 {
  log(LOC,"ERORR: Can't Restart The Delete at %s",
                          inet_ntoa(message->address.sin_addr));
  return FAIL;
 } 
 if(verbose)
  log(LOC,"key %d Delete Acknowledge Done",message->info.key);   
 return SUCCESS; 
}


/* a request to remove an IPC structure. This one is comming from the owner
   of the structure. Inform other computers with a structure of the same 
   key */
static BOOLEAN referee_dipc_rmid(struct message *message)
{
 struct message response;
 struct dipc_list *dl, *dl2;
 int type = message->info.type;
 int key = message->info.key;

 if(verbose)
  log(LOC,"Handling RMID Of Key %d of Type %s",key, ipc_type[type]); 
 dl = find_in_list(&gotten_first[type], key);
 while(dl)
 {
  if(SAME_MACHINE(&(dl->address), message->address))
   break;
  dl = get_next_key(dl);
 }
 if(dl == NULL)
 {
  log(LOC,"ERROR: There Is No Key To RMID");
  /* return FAIL; don't return fail, so that the stoped task can resatart */
 } 
 else
 {  
  response = *message;
  response.address = REFEREE_ADDRESS;
  response.request = REQ_DOWORK;
  /* send all the keys except the first one */ 
  dl2 = find_in_list(&gotten_first[type], key);
  while(dl2)
  {
   if(dl2 != dl) /* not the original one! */
   {
    if(verbose)
     log(LOC,"telling key %d of type %s at %s to RMID",dl2->key, 
              ipc_type[type], inet_ntoa(dl2->address.sin_addr));

    if (debug_protocol) {
     log(LOC,"Referee: rmid %s func %d key %d pid %d %d",
     		   inet_ntoa(dl2->address.sin_addr), response.info.func, 
     		   response.info.key, response.info.pid, response.pid);
     log(LOC,"Referee: args %d %d %d %d owner %d", 
     		   response.info.arg1, response.info.arg2, response.info.arg3, 
     		   response.info.arg4, response.info.owner );
    }
    if(send_in_message(&response, dl2->address) == FAIL)
    { 
     log(LOC,"Can't Tell key %d of type %s at %s to RMID",dl2->key, 
                 ipc_type[type], inet_ntoa(dl2->address.sin_addr));
     /* return FAIL; don't return fail, so that the stoped task can resatart */
    }
   }  
   dl2 = get_next_key(dl2);
  }
  /* now see if there is only one machine with the key. This happens 
    when all the tasks are on the same machine.
    If this is the case and the key type is SHM, send a message 
    to that machine, so that the key_manager can detach itself */
  dl2 = find_in_list(&gotten_first[type], key);  
  if(type == SHM && dl2 && get_next_key(dl2) == NULL)
  {
   if(verbose)
    log(LOC,"Only Machine %s Has This Shared Memory Key",
            inet_ntoa(dl->address.sin_addr));
   response = *message;
   response.address = REFEREE_ADDRESS;
   response.request = REQ_SHMMAN;
   response.info.func = DETACH;

   if (debug_protocol) {
     log(LOC,"Referee: delmgr %s func %d key %d pid %d %d", 
     		   inet_ntoa(dl2->address.sin_addr), response.info.func, 
     		   response.info.key, response.info.pid, response.pid);
     log(LOC,"Referee: args %d %d %d %d owner %d", 
     		   response.info.arg1, response.info.arg2, response.info.arg3, 
     		   response.info.arg4, response.info.owner );
   }

   if(send_in_message(&response, dl2->address) == FAIL)
   {
    log(LOC,"ERROR: Can't Inform Shared Memory Owner To Delete");
    /* return FAIL; don't return fail, so that the stoped task can resatart */  
   } 
  }
 } /* if dl */     
 /* log(LOC,"acknowledging the RMID"); */
 response = *message;
 response.address = REFEREE_ADDRESS;
 response.request = RES_REFEREE;

 if (debug_protocol) {
   log(LOC,"Referee: ack rmid %s func %d key %d pid %d %d",
     		   inet_ntoa(message->address.sin_addr), response.info.func, 
     		   response.info.key, response.info.pid, response.pid);
   log(LOC,"Referee: args %d %d %d %d owner %d", 
     		   response.info.arg1, response.info.arg2, response.info.arg3, 
     		   response.info.arg4, response.info.owner );
 }
 if(send_in_message(&response, message->address) == FAIL)
  return FAIL;
 if(verbose)
  log(LOC,"Key %d RMID Acknowledge Done",key);
 return SUCCESS; 
}

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


/* checks for stale requests and if timed out, fakes a failed commit.
   This is called at regular intervals */
void check_answers(void)
{
 int type;
 struct message message;
 time_t curr_time;
 struct dipc_list *dl;
 
 if(referee_timeout <= 0 || global_timeout == 0) return;
 
 message.request = RES_REFEREE;
 message.info.arg2 = 0; /* FAIL */
 curr_time = time(NULL);
 for(type = 0; type < 3; type++)
 {
  dl = wanted_first[type].next;
  while(dl)
  {
   if(dl->status == REF_ANSWERED)
   {
    if((curr_time - dl->answer_time) > referee_timeout)
    {
     if(verbose || debug_protocol)
      log(LOC,"Handling Stale request at %s",
              inet_ntoa(dl->address.sin_addr));
     /* in case the request is on the local lost, the pid is missing here ... */
     message.address = dl->address;
     message.info = dl->info;
     message.pid = dl->pid;
     message.info.func = COMMIT;
     referee_dipc_commit(&message, 0);
    }
   }
   dl = dl->next;
  }
 }
}

/* 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 referee,
   without the need to redesign it. The work here should be done quickly, 
   because while the referee 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 dipc_list *dl;
 struct back_door_message bd_response;
 int type, 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(type = 0; type < 3; type++)
   {
    dl = gotten_first[type].next;
    while(dl)
    {
     bd_response.arg1 = type;
     bd_response.arg2 = dl->key;
     bd_response.arg3 = dl->size;
     bd_response.arg4 = dl->distrib;
     bd_response.arg5 = dl->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;        
     }
     dl = dl->next;
    }
    if(dl != NULL) break;
   }
   if(type == 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_REMOVE:
   if(verbose)
    log(LOC,"back door: request for removal\n");
   dl = gotten_first[bd_message->arg1].next;
   while(dl)
   {
    if(dl->key == bd_message->arg2 && 
       dl->address.sin_addr.s_addr == bd_message->arg5)
     break;
    dl = dl->next;
   }
   if(dl)
   {
    remove_from_list(&gotten_last[bd_message->arg1], dl);
    if(verbose)
     log(LOC,"back door: removed the entry\n");
    /* tell dipcreg of the success */
    bd_message->arg1 = 0;
   }
   else
   {
    if(verbose)
     log(LOC,"back door: did not find the entry\n");
    bd_message->arg1 = -1;
   } 
   want_trans = sizeof(*bd_message);
   act_trans = writen(ux_sockfd, (char *)bd_message, 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;
   
  default:
   log(LOC,"ERROR: Unknown back door request: %d\n", bd_message->cmd);
 }
}
    
void referee(void)
{
 fd_set rSelFD; /* for select */
 struct timeval TimeVal;
 int count;
 int want_trans, act_trans;
 struct message message;
 int in_client_len, in_len;
 struct sockaddr_in in_client_addr, in_addr;
 int in_sockfd,in_newsockfd = -1;
 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 sigaction sact;
 struct back_door_message bd_message;
  
 log(LOC,"referee Started");

 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)); 

 /* this forces the referee to read the etc/dipc.allow file again */
 sact.sa_handler = sighup_handler;
 if(sigaction(SIGHUP, &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 install exit handler BECAUSE %s",strerror(errno)); 

 for(count = 0; count < 3; count++)
 {
  gotten_first[count].next = gotten_first[count].prev = NULL;
  gotten_last[count] = &gotten_first[count];
  
  wanted_first[count].next = wanted_first[count].prev = NULL;
  wanted_last[count] = &wanted_first[count];
 } 
 /* prepare the "back door" socket */
 sprintf(back_ux_name,"%s/%s", dipcd_home, REFEREE_BACKDOOR);
 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);
  exit(20);
 }
  
 bzero((char *)&in_addr,sizeof(in_addr));
 in_addr.sin_family = AF_INET;
 in_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 in_addr.sin_port = htons(DIPC_REFEREE_PORT);
 in_len = sizeof(in_addr);
  
 in_sockfd = plug(AF_INET, (struct sockaddr *) &in_addr, in_len);
 if(in_sockfd < 0)
 {
  log(LOC,"ERROR: Can't Create Socket");
  exit(20);
 }

 while(1) 
 {
  TimeVal.tv_sec = referee_timeout;
  TimeVal.tv_usec = 0;
  FD_ZERO(&rSelFD);
  FD_SET(in_sockfd,&rSelFD);
  FD_SET(back_ux_sockfd, &rSelFD);
  while(1)
  {
   if(select(FD_SETSIZE,&rSelFD,NULL,NULL,&TimeVal) < 0)
   {
    if(errno != EINTR)
    {
     log(LOC,"ERROR: select() failed  BECAUSE %s",strerror(errno));
     close(in_sockfd);
     exit(20);
    }
   }
   else break; 
  }  
  if(FD_ISSET(in_sockfd,&rSelFD))
  {
   if(use_udp)
    in_newsockfd = in_sockfd;
   else
   {
    in_client_len = sizeof(in_client_addr);
    while(1)
    {
     if((in_newsockfd = accept(in_sockfd, (struct sockaddr *)&in_client_addr,
                                                      &in_client_len)) < 0)
     {
      if(errno != EINTR)
      { 
       log(LOC,"ERROR: accept() Failed  BECAUSE %s",strerror(errno));
       close(in_sockfd);
       exit(20);
      } 
     }
     else break;
    }
   }
   want_trans = sizeof(message);
   act_trans = get_in_message(in_newsockfd, &message);
   if(act_trans < want_trans)
    log(LOC,"ERROR: Received %d From %d Bytes  BECAUSE %s",
            act_trans, want_trans,strerror(errno));
   else 
   {
    if(verbose)
     log(LOC,"Remote Call From %s",inet_ntoa(message.address.sin_addr)); 

    /* check to see if the computer is allowed to access the referee */
    if(check_allow(message.address.sin_addr.s_addr) == FAIL)
    {
     log(LOC,"Warning: Address %s is not Allowed",inet_ntoa(message.address.sin_addr)); 
     if(!use_udp)
      close(in_newsockfd);
     continue; /* just ignore it */
    } 
    if (debug_protocol) {
      log(LOC,"Referee: call from %s func %d key %d pid %d %d",
                  inet_ntoa(message.address.sin_addr),
                  message.info.func, message.info.key,
                  message.info.pid, message.pid);
      log(LOC,"Referee: args %d %d %d %d owner %d",
                  message.info.arg1, message.info.arg2, message.info.arg3,
                  message.info.arg4, message.info.owner );
    }
    switch(message.info.func)
    {
     case SEARCH:
      referee_dipc_search(&message);
      break;

     case COMMIT:
      referee_dipc_commit(&message, 1); 
      break;

     case DELKEY:
      referee_dipc_delkey(&message);
      break;

     case RMID:
      referee_dipc_rmid(&message);  
      break;

     default: 
      log(LOC,"ERROR: Unknown Function %s", work_string(message.info.func)); 
      break;
    }
   } /* message received */  
   if(!use_udp)
    close(in_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);
   while(1)
   {
    if((back_ux_newsockfd = accept(back_ux_sockfd, 
         (struct sockaddr *)&back_ux_client_addr, &back_ux_client_len)) < 0)
    {
     if(errno != EINTR)
     { 
      log(LOC,"ERROR: accept() Failed  BECAUSE %s",strerror(errno));
      close(back_ux_newsockfd);
     }
     else continue;
    } 
    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);
     break; /* from the while loop */
    }
   } /* while(1) */  
  } /* if back door */ 
  /* either a timeout or a request was serviced, check stale requests */
  check_answers();
 } /* while(1) */
}
