/*
 * config.c
 *
 * Allows the user to view and change the DIPC configuration file.
 * Part of dipcx program.
 *
 * By Kamran Karimi
 */

#include <strings.h>
#include <ctype.h>
#include <stdlib.h>
#include <netdb.h>

#include "dipcx.h"

#define CONFIG_FILE "/etc/dipc.conf"

static char this_addr[30];
static char ref_addr[30];
static int trans_mode;
static int page_size;
static int inhibit_signal;
static int emp_ref, emp_shm, emp_work, shm_ak, ref_time;
static char hold_time[20];

static Panel_item this, ref, trans, psize, inhibit, empref, empshm, 
           empwork, shmak, reftime, holdtime;

/* used in parsing dipcd configuration file */ 
static 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 */
static 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)
  {
   /*sprintf(mstr,"Warning: Can't resolve name '%s' BECAUSE %s",
                                       buff, strerror(errno));
   mess(mstr, INFO);*/
   return FAIL;
  }
  if(hostent->h_addrtype != AF_INET)
  {
   /*sprintf(mstr,"Warning: '%s' is not an IP address (ignored)",buff);
   mess(mstr, INFO);*/
   return FAIL;
  }
  strcpy(buff, inet_ntoa(*(struct in_addr *)hostent->h_addr_list[0]));  
 }
 return SUCCESS;
} 

/* read my address and the referee address, plus time-out values and put them 
   in globaly known variables */
static int read_dipc_config(void)
{
 char *pos, buff[160];
 FILE *file;

 strcpy(this_addr, "dipc_auto_find");
 strcpy(ref_addr, "0.0.0.0");
 trans_mode = 0;
 page_size = PAGE_SIZE;
 inhibit_signal = 0;
 emp_ref = 60;
 emp_shm = 120;
 emp_work = 300;
 shm_ak = 60;
 ref_time = 60;
 strcpy(hold_time, "0.2");
 
 file = fopen(CONFIG_FILE,"r");
 if(file == NULL)
 { 
  char buff[80];
  
  sprintf(buff,"Can't open %s for reading. Using defaults", CONFIG_FILE);
  show_ok_notice(buff); 
  return FAIL;
 }
  
 while(fgets(buff,80,file))
 {
  pos = strchr(buff, '#'); /* no comments */
  if(pos) *pos = 0;
  strip_space(buff);
  pos = strchr(buff, '=');
  if(pos == NULL) continue;
  *pos = 0;
  if(!strcasecmp(buff,"this_machine"))
  {
   pos++;
   if(strcmp(pos,"0.0.0.0") && strcmp(pos,"127.0.0.1"))
    strcpy(this_addr,pos);
  }
  else if(!strcasecmp(buff,"referee"))
  {
   pos++;
   if(strcmp(pos, "0.0.0.0") && strcmp(pos, "127.0.0.1"))
    strcpy(ref_addr, pos);
  }
  else if(!strcasecmp(buff,"transfer_mode"))
  { 
   pos++;
   if(!strcasecmp(pos, "page"))
    trans_mode = 1;    
  }
  else if(!strcasecmp(buff,"dipc_page_size"))
  { 
   pos++;
   page_size = atoi(pos);
   if(page_size <= 0 || ((page_size / PAGE_SIZE) * PAGE_SIZE) != page_size)
    page_size = PAGE_SIZE;
  }
  else if(!strcasecmp(buff,"inhibit_shm_signal"))
  { 
   pos++;
   if(!strcasecmp(pos, "yes"))
    inhibit_signal = 1;
  }
  else if(!strcasecmp(buff,"employer_referee_timeout"))
  { 
   pos++;
   emp_ref = atoi(pos);  
  }
  else if(!strcasecmp(buff,"employer_shm_timeout"))
  { 
   pos++;
   emp_shm = atoi(pos);  
  }
  else if(!strcasecmp(buff,"employer_worker_timeout"))
  { 
   pos++;
   emp_work = atoi(pos); 
  }
  else if(!strcasecmp(buff,"shm_ak_timeout"))
  { 
   pos++;
   shm_ak = atoi(pos);
  } 
  else if(!strcasecmp(buff,"referee_timeout"))
  { 
   pos++;
   ref_time = atoi(pos);  
  }
  else if(!strcasecmp(buff,"shm_hold_timeout"))
  { 
   pos++;
   strcpy(hold_time, pos);
  }
 } /* while */
 fclose(file);
 return SUCCESS;
}


static int save_dipc_config(void)
{
 char buff[80];
 FILE *file;
 
 strcpy(this_addr, (char *) xv_get(this, PANEL_VALUE));
 strcpy(ref_addr, (char *) xv_get(ref, PANEL_VALUE));
  
 trans_mode = (int) xv_get(trans, PANEL_VALUE);
 page_size = (int) xv_get(psize, PANEL_VALUE);
 inhibit_signal = (int) xv_get(inhibit, PANEL_VALUE); 
  
 emp_ref = (int) xv_get(empref, PANEL_VALUE);
 emp_shm = (int) xv_get(empshm, PANEL_VALUE);
 emp_work = (int) xv_get(empwork, PANEL_VALUE); 
 shm_ak = (int) xv_get(shmak, PANEL_VALUE);
 ref_time = (int) xv_get(reftime, PANEL_VALUE); 
 strcpy(hold_time, (char *) xv_get(holdtime, PANEL_VALUE)); 

 strip_space(this_addr);
 if(strcasecmp(this_addr, "dipc_auto_find"))
 {
  strcpy(buff, this_addr);
  if((check_symbolic(buff) == FAIL) || (inet_addr(buff) == -1) || 
      !strcmp(buff,"0.0.0.0") || !strcmp(buff,"127.0.0.1"))
  {
   show_ok_notice("This computer's IP address is invalid. Can't save");
   return FAIL; 
  }
 }   
 
 strip_space(ref_addr);
 strcpy(buff, ref_addr); 
 if((check_symbolic(buff) == FAIL) || (inet_addr(buff) == -1) || 
    !strcmp(buff,"0.0.0.0") || !strcmp(buff, "127.0.0.1"))
 {
  show_ok_notice("The referee's IP address is invalid. Can't save");
  return FAIL; 
 }
 
 if(page_size <= 0 || ((page_size / PAGE_SIZE) * PAGE_SIZE) != page_size)
 {
  show_ok_notice("Distributed Page Size is invalid. Can't save");
  return FAIL; 
 }
 
 if(atof(hold_time) <= 0)
 {
  show_ok_notice("Invalid SHM hold time. Can't save");
  return FAIL;
 }
 
 file = fopen(CONFIG_FILE,"w");
 if(file == NULL) 
 {
  char buff[80];
  
  sprintf(buff,"Can't open %s for writing", CONFIG_FILE);
  show_ok_notice(buff);
  return FAIL;
 }
 
 fprintf(file,"#This is the configuration file of DIPC. See the file dipcd.doc\n");
 fprintf(file,"#in DIPC's docs directory for some explanation of the entries\n\n");
 
 fprintf(file,"#If 'this_machine' is 'dipc_auto_find', then the address is determined\n");
 fprintf(file,"#using the 'hostname' command\n");
 fprintf(file,"this_machine = %s # Address of this computer\n\n", this_addr);
 
 fprintf(file,"referee = %s # Address of the referee computer\n", ref_addr);
 
 fprintf(file,"\n");
  
 fprintf(file,"transfer_mode = %s # Transfer shared memories a %s at a time\n",
                             (trans_mode == 0) ? "Segment" : "Page",
                             (trans_mode == 0) ? "segment" : "page");  
                             
 fprintf(file, "\n# If using page mode, transfer this much. Must be a multiple of VM page\n"); 
 fprintf(file, "# size, and should be the same in all the computers of a cluster\n");
 fprintf(file, "dipc_page_size = %d\n\n", page_size); 

 fprintf(file,"inhibit_shm_signal = %s # Should we disable read/write signals?\n",
                             (inhibit_signal == 0) ? "No" : "Yes");

 fprintf(file,"\n# The follwoing are the time-out values\n");
 
 fprintf(file,"employer_referee_timeout = %d\n", emp_ref);
 
 fprintf(file,"\n# Increase this if a lot of computers will access a shared memory\n");
 fprintf(file,"employer_shm_timeout = %d\n", emp_shm);
 
 fprintf(file,"\n# Increase this if there are slow computers or networks in the cluster\n");
 fprintf(file,"employer_worker_timeout = %d\n\n", emp_work);
 
 fprintf(file,"shm_ak_timeout = %d\n", shm_ak);
 
 fprintf(file,"referee_timeout = %d\n", ref_time);
 
 fprintf(file,"shm_hold_timeout = %s\n", hold_time);

 fflush(file);
 fclose(file);
 return SUCCESS;
}
 
 
int dipc_config(void)
{
 Rect *rect;

 clean_all();
 
 read_dipc_config();
 
 display_config = 1; 
 config_panel = (Panel) xv_create(frame, PANEL, NULL);

 this = xv_create(config_panel, PANEL_TEXT,
         PANEL_LABEL_STRING, "This Computer's Address:",
         PANEL_VALUE, this_addr,
         PANEL_NOTIFY_LEVEL, PANEL_NONE,
         PANEL_VALUE_DISPLAY_LENGTH, 28,
         PANEL_VALUE_STORED_LENGTH, 28,
         NULL);
  
 ref = xv_create(config_panel, PANEL_TEXT,
        PANEL_LABEL_STRING, "Referee's Address:",
        PANEL_VALUE, ref_addr,
        PANEL_NOTIFY_LEVEL, PANEL_NONE,
        PANEL_VALUE_DISPLAY_LENGTH, 28,
        PANEL_VALUE_STORED_LENGTH, 28,
        NULL);

 trans = xv_create(config_panel, PANEL_CHOICE,
          PANEL_LABEL_STRING, "Shared Memory Transfer Mode:",
          PANEL_CHOICE_STRINGS, "Segment", "Page", NULL,
          PANEL_VALUE, trans_mode,
          PANEL_NOTIFY_LEVEL, PANEL_NONE,
          NULL);

 psize = xv_create(config_panel, PANEL_NUMERIC_TEXT,
           PANEL_LABEL_STRING, "Distributed Page Size:",
           PANEL_MAX_VALUE, MAX_PAGE_SIZE,
           PANEL_VALUE, page_size,
           PANEL_NOTIFY_LEVEL, PANEL_NONE,
           PANEL_VALUE_DISPLAY_LENGTH, 6,
           PANEL_VALUE_STORED_LENGTH, 6,
           NULL);

 inhibit = xv_create(config_panel, PANEL_CHOICE,
            PANEL_LABEL_STRING, "Inhibit Shared Memory Read/Write Signals:",
            PANEL_CHOICE_STRINGS, "No", "Yes", NULL,
            PANEL_VALUE, inhibit_signal,
            PANEL_NOTIFY_LEVEL, PANEL_NONE,
            NULL);

 empref = xv_create(config_panel, PANEL_NUMERIC_TEXT,
           PANEL_LABEL_STRING, "Employer to Referee Time-out:",
           PANEL_MAX_VALUE, MAX_TIME_OUT,
           PANEL_VALUE, emp_ref,
           PANEL_NOTIFY_LEVEL, PANEL_NONE,
           PANEL_VALUE_DISPLAY_LENGTH, 7,
           PANEL_VALUE_STORED_LENGTH, 7,
           NULL);

 empshm = xv_create(config_panel, PANEL_NUMERIC_TEXT,
          PANEL_LABEL_STRING, "Employer to SHM-Manager Time-out:",
          PANEL_MAX_VALUE, MAX_TIME_OUT,
          PANEL_VALUE, emp_shm,
          PANEL_NOTIFY_LEVEL, PANEL_NONE,
          PANEL_VALUE_DISPLAY_LENGTH, 6,
          PANEL_VALUE_STORED_LENGTH, 6,
          NULL);
  
 empwork = xv_create(config_panel, PANEL_NUMERIC_TEXT,
            PANEL_LABEL_STRING, "Employer to Worker Time-out:",
            PANEL_MAX_VALUE, MAX_TIME_OUT,
            PANEL_VALUE, emp_work,
            PANEL_NOTIFY_LEVEL, PANEL_NONE,
            PANEL_VALUE_DISPLAY_LENGTH, 6,
            PANEL_VALUE_STORED_LENGTH, 6,
            NULL);

 shmak = xv_create(config_panel, PANEL_NUMERIC_TEXT,
          PANEL_LABEL_STRING, "SHM-Manager Acknowledge Time-out:",
          PANEL_MAX_VALUE, MAX_TIME_OUT,
          PANEL_VALUE, shm_ak,
          PANEL_NOTIFY_LEVEL, PANEL_NONE,
          PANEL_VALUE_DISPLAY_LENGTH, 6,
          PANEL_VALUE_STORED_LENGTH, 6,
          NULL);

 reftime = xv_create(config_panel, PANEL_NUMERIC_TEXT,
            PANEL_LABEL_STRING, "Referee Time-out:",
            PANEL_MAX_VALUE, MAX_TIME_OUT,
            PANEL_VALUE, ref_time,
            PANEL_NOTIFY_LEVEL, PANEL_NONE,
            PANEL_VALUE_DISPLAY_LENGTH, 6,
            PANEL_VALUE_STORED_LENGTH, 6,
            NULL);
  
 holdtime = xv_create(config_panel, PANEL_TEXT,
             PANEL_LABEL_STRING, "SHM Hold Time:",
             PANEL_VALUE, hold_time,
             PANEL_NOTIFY_LEVEL, PANEL_NONE,
             PANEL_VALUE_DISPLAY_LENGTH, 6,
             PANEL_VALUE_STORED_LENGTH, 6,
             NULL);
 
 window_fit(config_panel);
 window_fit(frame);

 rect = (Rect *) xv_get(frame, XV_RECT);

 (void) xv_create(config_panel, PANEL_BUTTON,
         XV_Y, rect->r_height + 10,
         PANEL_LABEL_STRING, "Save",
         PANEL_NOTIFY_PROC, save_dipc_config,
         NULL);

 window_fit(config_panel);
 window_fit(frame);

 return XV_OK;
}
