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

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

#define min(a,b) ((a) < (b) ? (a) : (b))

struct linger linger = {1, 5};

struct sockaddr recent_read;

/* ugly hack: field names in glibc different */
#ifdef __GLIBC__
#define ___key __key
#define ___seq __seq 
#else
#define ___key key
#define ___seq seq 
#endif



extern struct sockaddr_in MY_LBACK, REFEREE_LBACK;
extern struct sockaddr_in MY_ADDRESS, REFEREE_ADDRESS;

extern clock_t start_time;

extern int num_allow;
extern long *allow;

extern int verbose;
extern int network_byte_order;
extern int debug_protocol;
extern int debug_conversion;
extern int use_udp;

static char mstr[200];

void gettime(clock_t *hours, clock_t *minutes, clock_t *seconds, 
                                                         clock_t *milis)
{
 clock_t time, ticks_hour, ticks_minute, ticks_sec;
 
 ticks_sec = sysconf(_SC_CLK_TCK);
 ticks_minute = ticks_sec * 60;
 ticks_hour = ticks_sec * 60 * 60;

 time = times(NULL) - start_time;
 *hours = time / ticks_hour;
 time = time - *hours * ticks_hour;
 *minutes = time / ticks_minute;
 time = time - *minutes * ticks_minute;
 *seconds = time / ticks_sec;
 time = time  - *seconds * ticks_sec;
 *milis = time * 100 / ticks_sec;                                                         
}

/* prints a message to the log file (stderr) */
void log(char *filename, int line, char *format, ...)
{
 va_list vl;
 clock_t hours, minutes, seconds, milis;
 
 gettime(&hours, &minutes, &seconds, &milis);
 sprintf(mstr, "[%d@%s:%d]{%02ld:%02ld:%02ld:%02ld} ",getpid(), filename, 
    line, hours, minutes, seconds, milis);
 va_start(vl, format);
 vsprintf(&mstr[strlen(mstr)], format, vl);
 va_end(vl);
 fprintf(stderr, "%s\n", mstr);
} 

/* used in parsing dipcd configuration files */ 
void strip_space(char *buff)
{
 char buff2[160];
 int count, count2 = 0;
 
 for(count = 0; count < strlen(buff); count++)
 {
  if(isspace(buff[count]))
   continue;
  buff2[count2] = buff[count];
  count2++;
 }
 buff2[count2] = 0;
 strcpy(buff, buff2);
}


/* this routine checks to see if the DIPC admin used symbolic internet
   addresses instead of number-dot format. If the answer is yes, this routine
   tries to resolve the address */
int check_symbolic(char *buff)
{
 long count, is_symbolic = FALSE;
 struct hostent *hostent;

 for(count = 0; count < strlen(buff); count++)
 {
  if(isalpha(buff[count]) && (buff[count] != '.'))
  {
   is_symbolic = TRUE;
   break;
  }
 }
 if(is_symbolic == TRUE)
 {
  hostent = gethostbyname(buff);
  if(hostent == NULL)
  {
   log(LOC,"Warning: Can't resolve name '%s' BECAUSE %s",
                                       buff, strerror(errno));
   return FAIL;
  }
  if(hostent->h_addrtype != AF_INET)
  {
   log(LOC,"Warning: '%s' is not an IP address (ignored)",buff);
   return FAIL;
  }
  strcpy(buff, inet_ntoa(*(struct in_addr *)hostent->h_addr_list[0]));  
 }
 return SUCCESS;
} 


BOOLEAN check_allow(long address)
{
 int count, result = FAIL;
 sigset_t newmask, oldmask;

 /* no sighup interrupt while we are searching the list */
 sigemptyset(&newmask);
 sigaddset(&newmask, SIGHUP);
 sigprocmask(SIG_BLOCK, &newmask, &oldmask); 
 for(count = 0; count < num_allow; count++)
 {
  if(allow[count] == address)
  {
   result = SUCCESS;
   break;
  }
 }
 sigprocmask(SIG_SETMASK, &oldmask, NULL);  /* restore signal status */
 return result;
} 


BOOLEAN read_dipc_allow(void)
{
 char *pos, buff[100];
 FILE *file;

 file = fopen(ALLOW_FILE,"r");
 if(file == NULL) 
 {
  log(LOC,"ERROR: Can't Open Allow File %s  BECAUSE %s",
                                       ALLOW_FILE, strerror(errno));
  return FAIL;
 }
 num_allow = 0;
 if(allow)
 {
  free(allow);
  allow = NULL;
 }
 while(fgets(buff,80,file))
 {
  pos = strchr(buff, '#'); /* no comments */
  if(pos) *pos = 0;
  strip_space(buff);
  if(strlen(buff) == 0)
   continue;
  if(check_symbolic(buff) == FAIL)
  {
   log(LOC,"Warning: Ignoring address: '%s'", buff);
   continue;
  }
  if(inet_addr(buff) == -1)
  {
   log(LOC,"Warning: '%s' is not an IP address (ignored)",buff);
   continue;
  }
  allow = realloc(allow, (num_allow + 1) * sizeof(int));
  if(allow == NULL)
  {
   log(LOC,"ERROR: Not enough memory");
   return FAIL;
  } 
  allow[num_allow] = inet_addr(buff);
  num_allow++;
 }
 return SUCCESS;
}

/** The following is for the support of heterogeneous systems **/
 
/* converts a 16 bit integer from big/little endian to little/big endian */
static short endian16(short to_convert)
{
 char temp;
 union
 {
  short short_form;
  char chr_form[2];
 } conv;
 
 conv.short_form = to_convert;
 temp = conv.chr_form[0];
 conv.chr_form[0] = conv.chr_form[1];
 conv.chr_form[1] = temp;
 
 return conv.short_form;
}

/* converts a 32 bit integer from big/little endian to little/big endian */
static int endian32(int to_convert)
{
 char temp;
 union
 {
  int int_form;
  char chr_form[4];
 } conv;
 
 conv.int_form = to_convert;
 temp = conv.chr_form[0];
 conv.chr_form[0] = conv.chr_form[3];
 conv.chr_form[3] = temp;
 temp = conv.chr_form[1];
 conv.chr_form[1] = conv.chr_form[2];
 conv.chr_form[2] = temp;
 
 return conv.int_form;
}
 
/* MSch: 'canonical' conversion method is using network byte order for the 
   transfer over the network.
   Conversion policy: convert _everything_ unconditionally, except for the 
   message-address and message->address2 sin_port and sin_addr fields.
   Reason: keep any assumption about the semantics of data fields outside the 
   conversion business; the message->address fields are always used as a direct
   addressing method for replies, hence the exception.
*/

/* put here the code to convert the fields of an inbound message structure to
   the receiving (this) machine's suitable form. message.arch has the architecture
   of the sender machine. MY_DIPC_ARCH has the architecture of this machine.*/ 

static void convert_message(struct message *message)
{
 struct in_addr owner_addr;
 
 if(message->arch == MY_DIPC_ARCH)
  return;
 /* else convert (struct message*) message: look at message->arch for info */
 else if(message->arch == LINUX_NBOR)
 {
   /* assume network byte order: convert data from network byte order */
   /* network part */
   message->address.sin_family = (int) ntohs((ushort) message->address.sin_family);
#if 0
   message->address.sin_port = ntohs(message->address.sin_port);
   message->address.sin_addr.s_addr = ntohl(message->address.sin_addr.s_addr); /* ?? */
#endif
   message->address2.sin_family = (int) ntohs((ushort) message->address2.sin_family);
#if 0
   message->address2.sin_port = ntohs(message->address2.sin_port);
   message->address2.sin_addr.s_addr = ntohl(message->address2.sin_addr.s_addr); /* ?? */
#endif
   /* local part - no conversion required ?? */
   message->pid        = (int) ntohl( message->pid);
   message->request    = (int) ntohl( message->request);
   message->reserved   = (int) ntohl( message->reserved);
   /* incoming dipc request */
   message->info.key   = (int) ntohl( message->info.key);   
   message->info.type  = (int) ntohl( message->info.type);   
   message->info.pid   = (int) ntohl( message->info.pid);   
   message->info.uid   = (int) ntohl( message->info.uid);   
   message->info.func  = (int) ntohl( message->info.func);   
   message->info.arg1  = (int) ntohl( message->info.arg1);   
   message->info.arg2  = (int) ntohl( message->info.arg2);   
   message->info.arg3  = (int) ntohl( message->info.arg3);   
   message->info.arg4  = (int) ntohl( message->info.arg4);   
   message->info.dsize = (int) ntohl( message->info.dsize);   
#if 1
   message->info.owner =       ntohl( message->info.owner);   
#endif
   if (debug_conversion) {
     log(LOC,"convert_in: message->func %d, message->pid %d",
		  message->info.func, message->pid);
     log(LOC,"message->info.key %d, message->info.pid %d",
		  message->info.key, message->info.pid);
     log(LOC,"message->info.args: %d %d %d %d",
		  message->info.arg1, message->info.arg2,
		  message->info.arg3, message->info.arg4);
     owner_addr.s_addr   = message->info.owner; 
     log(LOC,"message->info.owner %s %d", inet_ntoa(owner_addr), 
		   message->info.owner);
   }
  }      
  /* The rest is to convert from/to little/big-endian */
  /* Note: little endian and big endian representation of integers are
    symmetrical, that is why we don't have to care about the sender and
    receiver architectures */
  else
  {
    message->address.sin_family = (int) ntohs((ushort) message->address.sin_family);
#if 0
    message->address.sin_port = ntohs(message->address.sin_port);
    message->address.sin_addr.s_addr = ntohl(message->address.sin_addr.s_addr); /* ?? */
#endif
    message->address2.sin_family = (int) ntohs((ushort) message->address2.sin_family);
#if 0
    message->address2.sin_port = ntohs(message->address2.sin_port);
    message->address2.sin_addr.s_addr = ntohl(message->address2.sin_addr.s_addr); /* ?? */
#endif
    /* local part - no conversion required ?? */
    message->pid        = endian32( message->pid);
    message->request    = endian32( message->request);
    message->reserved   = endian32( message->reserved);
    /* incoming dipc request */
    message->info.key   = endian32( message->info.key);   
    message->info.type  = endian32( message->info.type);   
    message->info.pid   = endian32( message->info.pid);   
    message->info.uid   = endian32( message->info.uid);   
    message->info.func  = endian32( message->info.func);   
    message->info.arg1  = endian32( message->info.arg1);   
    message->info.arg2  = endian32( message->info.arg2);   
    message->info.arg3  = endian32( message->info.arg3);   
    message->info.arg4  = endian32( message->info.arg4);   
    message->info.dsize = endian32( message->info.dsize);   
#if 1
    message->info.owner = endian32( message->info.owner);   
#endif
    if (debug_conversion) {
      log(LOC,"convert_in: message->func %d, message->pid %d",
		  message->info.func, message->pid);
      log(LOC,"message->info.key %d, message->info.pid %d",
		  message->info.key, message->info.pid);
      log(LOC,"message->info.args: %d %d %d %d",
		  message->info.arg1, message->info.arg2,
		  message->info.arg3, message->info.arg4);
      owner_addr.s_addr   = message->info.owner; 
      log(LOC,"message->info.owner %s %d", inet_ntoa(owner_addr), 
		   message->info.owner);
    }
   }
} 

/* put here the code to convert the fields of an outbound message structure to
   the receiving (peer) machine's suitable form. message.arch has the architecture
   of the sender machine. MY_DIPC_ARCH has the architecture of this machine.*/ 
static void convert_out_message(struct message *message)
{
 struct in_addr owner_addr;
 
 /* conversion done by receiver ? */
 if(message->arch == MY_DIPC_ARCH)
  return;
 /* else : message->arch is LINUX_NBOR, convert (struct message*) message 
    to network byte order*/

 /* network part */
 message->address.sin_family = (int) ntohs((unsigned short)message->address.sin_family);
#if 0
 message->address.sin_port   = ntohs(message->address.sin_port);
 message->address.sin_addr.s_addr = ntohl(message->address.sin_addr.s_addr); /* ?? */
#endif
 message->address2.sin_family = (int) ntohs((unsigned short)message->address2.sin_family);
#if 0
 message->address2.sin_port   = ntohs(message->address2.sin_port);
 message->address2.sin_addr.s_addr = ntohl(message->address2.sin_addr.s_addr); /* ?? */
#endif
 /* local part */
 if (debug_conversion) {
   log(LOC,"convert_out: message->func %d, message->pid %d",
		message->info.func, message->pid);
   log(LOC,"message->info.key %d, message->info.pid %d",
		message->info.key, message->info.pid);
   log(LOC,"message->info.args: %d %d %d %d",
		message->info.arg1, message->info.arg2,
		message->info.arg3, message->info.arg4);
   owner_addr.s_addr   = message->info.owner; 
   log(LOC,"message->info.owner %s %d", inet_ntoa(owner_addr), 
		 message->info.owner);
 }
 message->pid        = (int) htonl(message->pid);
 message->request    = (int) htonl(message->request);
 message->reserved   = (int) htonl(message->reserved);
 /* outgoing dipc request */
 message->info.key   = (int) htonl(message->info.key);   
 message->info.type  = (int) htonl(message->info.type);   
 message->info.pid   = (int) htonl(message->info.pid);   
 message->info.uid   = (int) htonl(message->info.uid);   
 message->info.func  = (int) htonl(message->info.func);   
 message->info.arg1  = (int) htonl(message->info.arg1);   
 message->info.arg2  = (int) htonl(message->info.arg2);   
 message->info.arg3  = (int) htonl(message->info.arg3);   
 message->info.arg4  = (int) htonl(message->info.arg4);   
 message->info.dsize = (int) htonl(message->info.dsize);   
#if 1
 message->info.owner =       htonl(message->info.owner);   
#endif
 return;
} 

/* this routine is the place where inbound data conversions for different computer
   architectures are done. This is done after we have converted the message 
   itself. see above. */
static void convert_data(char *data, struct message *message)
{ 
 int item;
 struct semid_ds *semds;
 ushort *semary;
 struct sembuf *sbuf;
 struct msqid_ds *msqds;
 struct shmid_ds *shmds;

 /* conversion required ?? */
 if(message->arch == MY_DIPC_ARCH)
  return;
 switch(message->info.func)
 {
  case SEMCTL:
  /* 'message->info.arg2' has the second (semnum) argument */
  /* => arg2 and arg3 must be converted before ! */
   switch(message->info.arg3)
   {
    case SETVAL:
     /* convert (int) message->info.arg4 : done in convert_message! */
     if(message->arch == LINUX_NBOR);
      /* all done before :-) */
      /* message is in network byte order, but convert_message converted it! */
      /* No data attached for this one */
     else;
     break;
   
    case IPC_SET:
    case IPC_STAT:
     semds = (struct semid_ds *) data;      
     /* convert (struct semid_ds *) data */
     if(message->arch == LINUX_NBOR)
     {
       /* convert from network byte order */
       /* the only fields converted from the sem_perm structure: key, mode and 
          seq; uids/gids don't matter on the remote host unless running ugidd */
       /* glibc hack: ___key is either key or __key :-( */
       semds->sem_perm.___key  = (key_t)  ntohl( semds->sem_perm.___key);
       semds->sem_perm.mode = (ushort) ntohs((ushort) semds->sem_perm.mode);
       semds->sem_perm.___seq  = (ushort) ntohs((ushort) semds->sem_perm.___seq);
       /* converting access times won't harm ... */
       semds->sem_otime     = (time_t) ntohl( (ulong) semds->sem_otime);
       semds->sem_ctime     = (time_t) ntohl( (ulong) semds->sem_ctime);
       /* these don't mean anything on the remote machine ... may pass the 
          network unchanged; will have to be retrieved with IPC_STAT before 
          setting using IPC_SET anyway!: 
          semds->sem_base, semds->sem_pending, semds->sem_pending_last,
          semds->undo */
       semds->sem_nsems     = (ushort) ntohs((ushort) semds->sem_nsems);
     }
     else /* different endian */
     {
       /* the only fields converted from the sem_perm structure: key, mode and 
           seq; uids/gids don't matter on the remote host unless running ugidd */
        semds->sem_perm.___key  = (key_t)  endian32( semds->sem_perm.___key);
        semds->sem_perm.mode = (ushort) endian16((ushort) semds->sem_perm.mode);
        semds->sem_perm.___seq  = (ushort) endian16((ushort) semds->sem_perm.___seq);
        /* converting access times won't harm ... */
        semds->sem_otime     = (time_t) endian32( (ulong) semds->sem_otime);
        semds->sem_ctime     = (time_t) endian32( (ulong) semds->sem_ctime);
        /* these don't mean anything on the remote machine ... may pass the 
           network unchanged; will have to be retrieved with IPC_STAT before 
           setting using IPC_SET anyway!: 
           semds->sem_base, semds->sem_pending, semds->sem_pending_last,
           semds->undo */
        semds->sem_nsems     = (ushort) endian16((ushort) semds->sem_nsems);
     }
    
     break;
   
    case SETALL:
    case GETALL:
     semary = (ushort *) data;
     /* convert (ushort *)data */
     if(message->arch == LINUX_NBOR)
     {
       /* convert from network byte order */  
       /* how many ushort[] items? message->info.dsize/sizeof(ushort) ... */
       for (item = 0; item < (message->info.dsize/sizeof(ushort)); item++) 
           semary[item] = (ushort) ntohs((ushort) semary[item]);
     }
     else
     {
        /* how many ushort[] items? message->info.dsize/sizeof(ushort) ... */
        for (item = 0; item < (message->info.dsize/sizeof(ushort)); item++) 
            semary[item] = (ushort) endian16((ushort) semary[item]);
     }
     break;
  }     

  case SEMOP:
     /* 'message->info.arg2' has the third (nops) argument */
     /* => arg2 must be converted before */
     /* convert (struct sembuf *) data */
     sbuf = (struct sembuf *) data;
     if(message->arch == LINUX_NBOR)
     {
        /* convert from network byte order */  
       for (item = 0; item < message->info.arg2; item++) {
           sbuf[item].sem_num = (ushort) ntohs((ushort) sbuf[item].sem_num);
           sbuf[item].sem_op  =  (short) ntohs((ushort) sbuf[item].sem_op);
           sbuf[item].sem_flg =  (short) ntohs((ushort) sbuf[item].sem_flg);
       }
     }
     else 
     {
        for (item = 0; item < message->info.arg2; item++) {
            sbuf[item].sem_num = (ushort) endian16((ushort) sbuf[item].sem_num);
            sbuf[item].sem_op  =  (short) endian16((ushort) sbuf[item].sem_op);
            sbuf[item].sem_flg =  (short) endian16((ushort) sbuf[item].sem_flg);
        }
     }

     break;

  case MSGCTL:
     /* convert (struct msqid_ds *) data */
     msqds = (struct msqid_ds *) data;
     if(message->arch == LINUX_NBOR)
     {
       /* assume network byte order */  
       /* these items might make sense on the remote system... */
       msqds->msg_perm.___key  = (key_t)  ntohl( msqds->msg_perm.___key);
       msqds->msg_perm.mode = (ushort) ntohs((ushort) msqds->msg_perm.mode);
       msqds->msg_perm.___seq  = (ushort) ntohs((ushort) msqds->msg_perm.___seq);
       /* message queue pointers *msg_first and *msg_last not converted */
       msqds->msg_stime     = (time_t) ntohl( (ulong) msqds->msg_rtime);
       msqds->msg_rtime     = (time_t) ntohl( (ulong) msqds->msg_rtime);
       msqds->msg_ctime     = (time_t) ntohl( (ulong) msqds->msg_ctime);
       /* wait queues *wwait and *rwait meaningless, not converted - CAUTION! */
       msqds->msg_cbytes    = (ushort) ntohs((ushort) msqds->msg_cbytes);
       msqds->msg_qnum      = (ushort) ntohs((ushort) msqds->msg_qnum);
       msqds->msg_qbytes    = (ushort) ntohs((ushort) msqds->msg_qbytes);
       /* last send / receive pids not converted */
     }
     else
     {
        /* these items might make sense on the remote system... */
        msqds->msg_perm.___key  = (key_t)  endian32( msqds->msg_perm.___key);
        msqds->msg_perm.mode = (ushort) endian16((ushort) msqds->msg_perm.mode);
        msqds->msg_perm.___seq  = (ushort) endian16((ushort) msqds->msg_perm.___seq);
        /* message queue pointers *msg_first and *msg_last not converted */
        msqds->msg_stime     = (time_t) endian32( (ulong) msqds->msg_rtime);
        msqds->msg_rtime     = (time_t) endian32( (ulong) msqds->msg_rtime);
        msqds->msg_ctime     = (time_t) endian32( (ulong) msqds->msg_ctime);
        /* wait queues *wwait and *rwait meaningless, not converted - CAUTION! */
        msqds->msg_cbytes    = (ushort) endian16((ushort) msqds->msg_cbytes);
        msqds->msg_qnum      = (ushort) endian16((ushort) msqds->msg_qnum);
        msqds->msg_qbytes    = (ushort) endian16((ushort) msqds->msg_qbytes);
        /* last send / receive pids not converted */
     }
     break;
   
  case SHMCTL:
     /* (struct shmid_ds *) data */
     shmds = (struct shmid_ds *) data;
     if(message->arch == LINUX_NBOR)
     {
       /* assume network byte order */    
       /* these items might make sense on the remote system... */
       shmds->shm_perm.___key  = (key_t)  ntohl( shmds->shm_perm.___key);
       shmds->shm_perm.mode = (ushort) ntohs((ushort) shmds->shm_perm.mode);
       shmds->shm_perm.___seq  = (ushort) ntohs((ushort) shmds->shm_perm.___seq);
       /* the segment size sure should be know remotely, too :-) */
       shmds->shm_segsz     =    (int) ntohl( shmds->shm_segsz);
       /* access times won't hurt, either */
       shmds->shm_atime     = (time_t) ntohl( (ulong) shmds->shm_atime);
       shmds->shm_dtime     = (time_t) ntohl( (ulong) shmds->shm_dtime);
       shmds->shm_ctime     = (time_t) ntohl( (ulong) shmds->shm_ctime);
       /* creator/operator pid's are meaningless over the network anyway ?? */
       shmds->shm_cpid      = (ushort) ntohs((ushort) shmds->shm_cpid);
       shmds->shm_lpid      = (ushort) ntohs((ushort) shmds->shm_lpid);
       shmds->shm_nattch    =  (short) ntohs((ushort) shmds->shm_nattch);
       /* the rest (npages,*pages,*attaches) is 'private', don't mess it up! */
     }
     else
     {  
        /* these items might make sense on the remote system... */
        shmds->shm_perm.___key  = (key_t)  endian32( shmds->shm_perm.___key);
        shmds->shm_perm.mode = (ushort) endian16((ushort) shmds->shm_perm.mode);
        shmds->shm_perm.___seq  = (ushort) endian16((ushort) shmds->shm_perm.___seq);
        /* the segment size sure should be know remotely, too :-) */
        shmds->shm_segsz     =    endian32( shmds->shm_segsz);
        /* access times won't hurt, either */
        shmds->shm_atime     = (time_t) endian32( (ulong) shmds->shm_atime);
        shmds->shm_dtime     = (time_t) endian32( (ulong) shmds->shm_dtime);
        shmds->shm_ctime     = (time_t) endian32( (ulong) shmds->shm_ctime);
        /* creator/operator pid's are meaningless over the network anyway ?? */
        shmds->shm_cpid      = (ushort) endian16((ushort) shmds->shm_cpid);
        shmds->shm_lpid      = (ushort) endian16((ushort) shmds->shm_lpid);
        shmds->shm_nattch    =  (short) endian16((ushort) shmds->shm_nattch);
        /* the rest (npages,*pages,*attaches) is 'private', don't mess it up! */
      }

    default:
#if 0
   /* This needs a bit more work; most of the functions need no conversion! */
   log(LOC,"ERROR: Unknown Function %d", message->info.func);
#endif
   break;
 }  
}   

/* this routine is the place where outbound data conversions for different computer
   architectures are done. This is done after we have converted the message 
   itself. see above. */
static void convert_out_data(char *data, struct message *message)
{ 
 int item;
 struct semid_ds *semds;
 ushort *semary;
 struct sembuf *sbuf;
 struct msqid_ds *msqds;
 struct shmid_ds *shmds;

 /* conversion done by receiver, or not required */
 if(message->arch == MY_DIPC_ARCH)
  return;
 /* else: message->arch is LINUX_NBOR; we convert all stuff to network byte 
    order */
 switch(message->info.func)
 {
  case SEMCTL:
  /* 'message->info.arg2' has the second (semnum) argument */
   switch(message->info.arg3)
   {
    case SETVAL:
     /* convert (int) message->info.arg4 : done in convert_message, no data! */
     break;
   
    case IPC_SET:
    case IPC_STAT:      
     /* convert (struct semid_ds *) data */
     semds = (struct semid_ds *) data;
     /* convert to network byte order */
     /* the only fields converted from the sem_perm structure: key, mode and 
        seq; uids/gids don't matter on the remote host unless running ugidd */
     semds->sem_perm.___key  = (key_t)  htonl( semds->sem_perm.___key);
     semds->sem_perm.mode = (ushort) htons((ushort) semds->sem_perm.mode);
     semds->sem_perm.___seq  = (ushort) htons((ushort) semds->sem_perm.___seq);
     /* converting access times won't harm ... */
     semds->sem_otime     = (time_t) htonl( (ulong) semds->sem_otime);
     semds->sem_ctime     = (time_t) htonl( (ulong) semds->sem_ctime);
     /* these don't mean anything on the remote machine ... may pass the 
        network unchanged; will have to be retrieved with IPC_STAT before 
        setting using IPC_SET anyway!: 
        semds->sem_base, semds->sem_pending, semds->sem_pending_last,
         semds->undo */
     semds->sem_nsems     = (ushort) htons((ushort) semds->sem_nsems);
     break;
   
    case SETALL:
    case GETALL:
     /* convert (ushort *)data */
     semary = (ushort *) data;
     /* convert to network byte order */
     /* how many ushort[] items? message->info.dsize/sizeof(ushort) ... */
     for (item = 0; item < (message->info.dsize/sizeof(ushort)); item++) 
         semary[item] = (ushort) htons((ushort) semary[item]);
     break;
   }
   break;
     
  case SEMOP:
   /* 'message->info.arg2' has the third (nops) argument */
   /* => arg2 must be converted before */
   /* convert (struct sembuf *) data */
   sbuf = (struct sembuf *) data;
   /* convert to network byte order */
   for (item = 0; item < message->info.arg2; item++) {
       sbuf[item].sem_num = (ushort) htons((ushort) sbuf[item].sem_num);
       sbuf[item].sem_op  =  (short) htons((ushort) sbuf[item].sem_op);
       sbuf[item].sem_flg =  (short) htons((ushort) sbuf[item].sem_flg);
   }
   break;

  case MSGCTL:
   /* convert (struct msqid_ds *) data */
   msqds = (struct msqid_ds *) data;
   /* convert to network byte order */
   /* these items might make sense on the remote system... */
   msqds->msg_perm.___key  = (key_t)  htonl( msqds->msg_perm.___key);
   msqds->msg_perm.mode = (ushort) htons((ushort) msqds->msg_perm.mode);
   msqds->msg_perm.___seq  = (ushort) htons((ushort) msqds->msg_perm.___seq);
   /* message queue pointers *msg_first and *msg_last not converted */
   msqds->msg_stime     = (time_t) htonl( (ulong) msqds->msg_rtime);
   msqds->msg_rtime     = (time_t) htonl( (ulong) msqds->msg_rtime);
   msqds->msg_ctime     = (time_t) htonl( (ulong) msqds->msg_ctime);
   /* wait queues *wwait and *rwait meaningless, not converted - CAUTION! */
   msqds->msg_cbytes    = (ushort) htons((ushort) msqds->msg_cbytes);
   msqds->msg_qnum      = (ushort) htons((ushort) msqds->msg_qnum);
   msqds->msg_qbytes    = (ushort) htons((ushort) msqds->msg_qbytes);
   /* last send / receive pids not converted */
   break;
   
  case SHMCTL:
   /* (struct shmid_ds *) data */
   shmds = (struct shmid_ds *) data;
   /* convert to network byte order */
   /* these items might make sense on the remote system... */
   shmds->shm_perm.___key  = (key_t)  htonl( shmds->shm_perm.___key);
   shmds->shm_perm.mode = (ushort) htons((ushort) shmds->shm_perm.mode);
   shmds->shm_perm.___seq  = (ushort) htons((ushort) shmds->shm_perm.___seq);
   /* the segment size sure should be know remotely, too :-) */
   shmds->shm_segsz     =    (int) htonl( shmds->shm_segsz);
   /* access times won't hurt, either */
   shmds->shm_atime     = (time_t) htonl( (ulong) shmds->shm_atime);
   shmds->shm_dtime     = (time_t) htonl( (ulong) shmds->shm_dtime);
   shmds->shm_ctime     = (time_t) htonl( (ulong) shmds->shm_ctime);
   /* creator/operator pid's are meaningless over the network; convert nattch */
   shmds->shm_cpid      = (ushort) htons((ushort) shmds->shm_cpid);
   shmds->shm_lpid      = (ushort) htons((ushort) shmds->shm_lpid);
   shmds->shm_nattch    =  (short) htons((ushort) shmds->shm_nattch);
   /* the rest (npages,*pages,*attaches) is 'private', don't mess it up! */
   break;
  
  default:
#if 0
   /* This needs a bit more work; most of the functions need no conversion! */
   log(LOC,"ERROR: Unknown Function %d", message->info.func);
#endif
   break;
 }  
}   
/** End of heterogeneous support routines **/


char *work_string(int func)
{
 static char strings[24][11] = 
 {
  "SEMOP     ",
  "SEMCTL    ",

  "MSGSND    ",
  "MSGRCV    ",
  "MSGCTL    ",

  "SHMCTL    ",

  "DISABLE_RD",
  "DISABLE_WR",

  "WANTRD    ",
  "WANTWR    ",
  "DETACH    ",
  "AK        ",
  
  "WRPROT_SND",
  "RDPROT    ",
  "RDPROT_SND",
  "NOP_SND   ",
  "RCV       ",
  "TSTGET    ",
  
  "RMID      ",
  "DELKEY    ",
  "SEARCH    ",
  "COMMIT    ",
  
  "SEM_EXIT  ",
  
  "Bad Work  "
 };
 
 switch(func)
 {
  case SEMOP: return strings[0];
  case SEMCTL: return strings[1];

  case MSGSND: return strings[2];
  case MSGRCV: return strings[3];
  case MSGCTL: return strings[4];

  case SHMCTL: return strings[5];

  case DISABLE_RD: return strings[6];
  case DISABLE_WR: return strings[7];
  case WANTRD: return strings[8];
  case WANTWR: return strings[9];
  case DETACH: return strings[10];
  case AK: return strings[11];
  
  case WRPROT_SND: return strings[12];
  case RDPROT: return strings[13];
  case RDPROT_SND: return strings[14];
  case NOP_SND: return strings[15];
  case RCV: return strings[16];
  case TSTGET: return strings[17];
  
  case RMID: return strings[18];
  case DELKEY: return strings[19];
  case SEARCH: return strings[20];
  case COMMIT: return strings[21];
 
  case SEM_EXIT: return strings[22];
  
  default:
   break;
 }
 log(LOC,"ERROR: Bad Work function: %d", func);
 return strings[22];
}


char *request_string(int req)
{
 static char strings[7][12] = 
 {
  "REQ_SHMMAN ",
  "RES_SHMMAN ",

  "REQ_REFEREE",
  "RES_REFEREE",
  
  "REQ_DOWORK ",
  "RES_DOWORK ",
  
  "Bad Request"
 };
 
 switch(req)
 {
  case REQ_SHMMAN: return strings[0];
  case RES_SHMMAN: return strings[1];

  case REQ_REFEREE: return strings[2];
  case RES_REFEREE: return strings[3];
  
  case REQ_DOWORK: return strings[4];
  case RES_DOWORK: return strings[5];
 
  default:
   break;
 }
 log(LOC,"ERROR: Bad Request: %d", req);
 return strings[6];
}
 
/* read nbytes from the network and put them in *ptr */
int readn(int fd, char *ptr, int nbytes)
{
 int nleft, nread; 
 int sock_len = sizeof(recent_read);
 
 nleft = nbytes;
 while(nleft > 0)
 {
  if(use_udp) /* should work for UNIX sockets */
   nread = recvfrom(fd, ptr, nleft, 0, (struct sockaddr *)&recent_read, &sock_len);
  else
   nread = read(fd,ptr,nleft);
  if(nread < 0)
  {
   if(errno != EINTR)
    break;
  }    
  if(nread == 0) break;
  if(nread > 0)
  {
   nleft -= nread;
   ptr += nread;
  }
  /* a rudimentary flow control */
  /*if(use_udp)
  { 
   char temp;
   int nwritten;
   
   do
   {
    nwritten = sendto(fd, &temp, sizeof(char), 0, &recent_read, sock_len);
    if((nwritten < 0 && errno != EINTR) || nwritten > 0)
     break;
   } while(1);
  } */
 } 
 return (nbytes - nleft);
}  

/* send nbytes from *ptr to the network */
int writen(int fd, char *ptr, int nbytes, struct sockaddr_in *addr)
{
 int nleft,nwritten;
 
 nleft = nbytes;
 while(nleft > 0)
 { 
  if(use_udp && addr != NULL) /* should work for UNIX sockets */
  /* flow control: nwritten = sendto(fd, ptr, min(nleft, UDP_SEND_SIZE), 0, 
                     (struct sockaddr *)addr, sizeof(struct sockaddr_in));*/
   nwritten = sendto(fd, ptr, nleft, 0, 
                     (struct sockaddr *)addr, sizeof(struct sockaddr_in));
  else
   nwritten = write(fd,ptr,nleft);
  if(nwritten < 0)
  {
   if(errno != EINTR)
    break;
  }
  if(nwritten == 0) break;
  if(nwritten > 0)
  {
   nleft -= nwritten;
   ptr += nwritten;
  }
  /* a rudimentary flow control */
  /*if(use_udp)
  {
   char temp;
   int nread;
   struct sockaddr flow_read;
   int flow_len = sizeof(flow_read);
   
   do
   {
     nread = recvfrom(fd, &temp, sizeof(char), 0, &flow_read, &flow_len);
     if((nread < 0 && errno != EINTR) || nread > 0)
      break;
   } while(1);
  } */
 }  
 return (nbytes - nleft);
}

/* prepare a socket to receive incoming conections */
FD plug(int family, struct sockaddr *my_addr, int addr_len)
{
 FD sockfd;
 int sock_type;
 
 if(use_udp && (family == AF_INET))
	  sock_type = SOCK_DGRAM;
 else
  sock_type = SOCK_STREAM;
  
 if((sockfd = socket(family,sock_type,0)) < 0)
 {
  log(LOC,"ERROR: socket() Failed  BECAUSE %s",strerror(errno));
  return BAD_FD;
 }
 if(setsockopt(sockfd, SOL_SOCKET, SO_LINGER , (char *)&linger, 
                                                  sizeof(linger)) < 0)
 {
  log(LOC,"ERROR: setsockopt() Failed  BECAUSE %s",strerror(errno));
  close(sockfd);
  return BAD_FD;
 }  
 if(bind(sockfd,my_addr,addr_len) < 0)
 {
  log(LOC,"ERROR: bind() Failed  BECAUSE %s",strerror(errno));
  close(sockfd);
  return BAD_FD;
 }
 if(use_udp && (family == AF_INET))
  return sockfd;
  
 if(listen(sockfd,5) < -1)
 {
  log(LOC,"ERROR: listen() Failed BECAUSE %s",strerror(errno));
  close(sockfd);
  return BAD_FD;
 }
 return sockfd;
}
 
/* get a socket connection to another waiting one */ 
FD ring(int family, struct sockaddr *addr, int addr_len, int silent)
{
 int sockfd, sock_type;
 sigset_t newmask, oldmask;
 struct sockaddr_in udp_addr;

 sigemptyset(&newmask);
 sigaddset(&newmask, SIGCHLD);
 sigprocmask(SIG_BLOCK, &newmask, &oldmask);
 
 if(use_udp && (family == AF_INET))
 {
  sock_type = SOCK_DGRAM;
  bzero((char *) &udp_addr, sizeof(struct sockaddr_in));
  udp_addr.sin_family = AF_INET;
  udp_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  udp_addr.sin_port = htons(0);
 }
 else
  sock_type = SOCK_STREAM;
 while(1)
 {
  if((sockfd = socket(family,sock_type,0)) < 0) 
  {
   if(errno != EINTR)
   {
    sigprocmask(SIG_SETMASK, &oldmask, NULL);
    return BAD_FD;
   }
  }
  else break;
 }
 if(setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *)&linger, 
                                                 sizeof(linger)) < 0)
 {
  log(LOC,"ERROR: setsockopt() Failed  BECAUSE %s",strerror(errno));
  close(sockfd);
  sigprocmask(SIG_SETMASK, &oldmask, NULL);
  return BAD_FD;
 }
 if(use_udp && family == AF_INET)
 {
  if(bind(sockfd,(struct sockaddr *)&udp_addr,sizeof(struct sockaddr_in)) < 0)
  {
   log(LOC,"ERROR: bind() Failed  BECAUSE %s",strerror(errno));
   close(sockfd);
   sigprocmask(SIG_SETMASK, &oldmask, NULL);
   return BAD_FD;
  } 
 }
 else
 {
  if(SAME_ADDRESS((struct sockaddr_in *)addr, MY_ADDRESS))
   (struct sockaddr_in *)addr = &MY_LBACK;
  else if(SAME_ADDRESS((struct sockaddr_in *)addr, REFEREE_ADDRESS) && 
          SAME_MACHINE((struct sockaddr_in *)addr,MY_ADDRESS))
   (struct sockaddr_in *)addr = &REFEREE_LBACK;

  /* connect() may not be able to restart in case of an interrupt */
  while(1)
  {
   if(connect(sockfd, addr, addr_len) < 0)
   {
    if(errno != EINTR)
    {
     if(family == AF_INET)
      log(LOC,"ERROR: connect() to %s failed  BECAUSE %s",
          inet_ntoa( ((struct sockaddr_in *) addr)->sin_addr),strerror(errno));
     else if(!silent)
       log(LOC,"ERROR: connect() to %s failed  BECAUSE %s",
     	   ((struct sockaddr_un *)addr)->sun_path, strerror(errno));
     close(sockfd);
     sigprocmask(SIG_SETMASK, &oldmask, NULL);
     return BAD_FD;
    }
   }
   else break;
  }
 }
 sigprocmask(SIG_SETMASK, &oldmask, NULL); 
 return sockfd;
}

 
/* send the contents of message associated data over an internet connection */ 
/* MS: extension of DIPC to implement network byte order transfers */
/* data sent by this function is part of a larger transfer, preceded by message */
int send_net_data(int in_sock, char *data, struct message *message,
                                                 struct sockaddr_in *addr)
{ 
 int want_trans, act_trans;
 
 /* transfer size is specified in message header - original message must be passed! */
 want_trans = message->info.dsize;
 convert_out_data(data, message);
 act_trans = writen(in_sock, (char *)data, want_trans, addr);
 if(act_trans < want_trans)
  log(LOC,"ERROR: Sent %d From %d Bytes BECAUSE %s",
          act_trans, want_trans,strerror(errno));
 return act_trans;
}
  
/* send the contents of a struct message over an opened internet connection */ 
/* MS: extension of DIPC to implement network byte order transfers */
/* message sent by this function is part of a larger transfer */
int send_net_message(int in_sock, struct message *message, 
                                                struct sockaddr_in *addr)
{ 
 int want_trans, act_trans;
 struct message net_message;

#if 0
 if(network_byte_order)
  message->arch = LINUX_NBOR;
 else
  message->arch = MY_DIPC_ARCH;
#endif

 message->reserved = -1;

 want_trans = sizeof(*message);
 /* MS: conversion _must_ work on copy of message in order to leave message in host 
        byte order for subsequent usage (i.e. in convert_out_data!) */
 memcpy(&net_message, message, want_trans);
 convert_out_message(&net_message);
 act_trans = writen(in_sock, (char *) &net_message, want_trans, addr);
 if(act_trans < want_trans)
  log(LOC,"ERROR: Sent %d From %d Bytes BECAUSE %s",
          act_trans, want_trans,strerror(errno));
 return act_trans;
}
  
/* send the contents of a struct message over an internet connection */ 
BOOLEAN send_in_message(struct message *message, struct sockaddr_in addr)
{ 
 int in_sock;
 int want_trans, act_trans;
 struct message net_message;
 
 if(network_byte_order)
  message->arch = LINUX_NBOR;
 else
  message->arch = MY_DIPC_ARCH;

 message->reserved = -1;
 in_sock = ring(AF_INET, (struct sockaddr *) &addr, sizeof(addr), 0);
 if(in_sock < 0)
 {
  log(LOC,"ERROR: Can't Connect To Internet address BECAUSE %s",
          strerror(errno));
  return FAIL;
 } 
 want_trans = sizeof(*message);

 if(network_byte_order)
 { 
  /* MS: conversion _must_ work on copy of message in order to leave message in host 
         byte order for subsequent usage (i.e. in convert_out_data!) */
  /*     Not sure about this one (obsolete if message isn't needed anymore */
  memcpy(&net_message, message, want_trans);
  convert_out_message(&net_message);
  act_trans = writen(in_sock, (char *) &net_message, want_trans, &addr);
 }
 else
 {
  convert_out_message(message);
  act_trans = writen(in_sock, (char *)message, want_trans, &addr);
 }
 
 if(act_trans < want_trans)
 {
  log(LOC,"ERROR: Sent %d From %d Bytes BECAUSE %s",
          act_trans, want_trans,strerror(errno));
  close(in_sock);
  return FAIL;
 }
 close(in_sock);
 return SUCCESS;
}
  
/* receive some data (for example message contents) over an internet 
   connection */  
int get_in_data(int in_sock, char *data, struct message *message)
{
 int act_trans;
 
 act_trans = readn(in_sock, data, message->info.dsize);
 if(act_trans == message->info.dsize)
  convert_data(data, message);
 return act_trans;
} 

/* receive an struct message from an internet connection */
int get_in_message(int in_sock, struct message *message)
{
 int act_trans;
 
 act_trans = readn(in_sock, (char *)message, sizeof(struct message));
 if(act_trans == sizeof(struct message))
  convert_message(message);
 return act_trans;
} 

/* called from front_end.c and back_end.c, when a child exits */
void child_handler(int signo)
{
 pid_t cpid;
 
 /* consider the possibility of multiple exits */
 while((cpid = waitpid(-1, NULL, WNOHANG)) > 0)
 {
  if(verbose)
   log(LOC,"Child %d Exited",cpid);
 }
}


BOOLEAN become(char *user)
{
 struct passwd *pw;
 
 pw = getpwnam(user);
 if(pw)
 {
  if((setegid(pw->pw_gid) < 0) || (seteuid(pw->pw_uid) < 0))
  {
   log(LOC,"ERROR: Can't Become: %s, uid: %d, gid: %d. Because: %s",
                   user, pw->pw_uid, pw->pw_gid, strerror(errno));
   return FAIL;
  }
  return SUCCESS;
 }
 log(LOC,"ERROR: User '%s' Not Found", user);
 return FAIL;
}
