/* admin.c

   Shane Eisenman - 11/2002

   This file implements the Administrator
   node used as the bootstrap server and
   registrar for group memberships. The
   Administrator code is self-contained in
   this file.
*/
/*--------------------------------------*/
#include "admin.h"
/*======================================*/
int main(void) {

  int sock_fd;

  Open_Socket(&sock_fd);
  Wait_For_Connect_Request(sock_fd);
  Reaper();
  Service_Connection_Request(sock_fd);

  return 0;
}
/*======================================*/
void Open_Socket(int *fd) {

  struct sockaddr_in admin_addr;   
  int yes=1;

  if ((*fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    Err_Exit("socket");

  if (setsockopt(*fd,SOL_SOCKET,SO_REUSEADDR,\
		 &yes,sizeof(int)) == -1)
    Err_Exit("setsockopt");

  admin_addr.sin_family = AF_INET;        
  admin_addr.sin_port = htons(ADMINPORT);    
  admin_addr.sin_addr.s_addr = INADDR_ANY;
  memset(&(admin_addr.sin_zero),'\0', 8);

  if (bind(*fd, (struct sockaddr *)&admin_addr,\
	   sizeof(struct sockaddr)) == -1)
    Err_Exit("bind");

}
/*--------------------------------------*/
void Wait_For_Connect_Request(int fd) {

  if (listen(fd, BACKLOG) == -1)
    Err_Exit("listen");

}
/*--------------------------------------*/
void Reaper() {

  struct sigaction sa;

  // reap all dead processes
  sa.sa_handler = SIGCHLD_Handler; 
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = SA_RESTART;

  if (sigaction(SIGCHLD, &sa, NULL) == -1)
    Err_Exit("sigaction");

}
/*--------------------------------------*/
void SIGCHLD_Handler(int s) {

  while(wait(NULL) > 0);

}
/*--------------------------------------*/
void Service_Connection_Request(int fd) {

  struct sockaddr_in requestor_addr;
  int new_fd,sin_size,bytes_read;
  unsigned char t[26];
  unsigned char buf[MSG_LEN];
  FILE *log;

  memset(buf,'\0',MSG_LEN);

  while(1) {
    sin_size = sizeof(struct sockaddr_in);
    if ((new_fd = accept(fd,(struct sockaddr *)&requestor_addr,\
			 &sin_size)) == -1) {
      perror("accept");
      continue;
    }
    log=fopen(LOGFILE,"a");

    printf("[%s] admin: conn. from %s\n",Get_Time_String(t),\
	   inet_ntoa(requestor_addr.sin_addr));    
    fprintf(log,"[%s] admin: conn. from %s\n",Get_Time_String(t),\
	    inet_ntoa(requestor_addr.sin_addr));

    if (!fork()) {
      close(fd);

      bytes_read=recv(new_fd,buf,MSG_LEN,MSG_NOSIGNAL);
      Handle_Msg(buf,inet_ntoa(requestor_addr.sin_addr),new_fd,log);

      printf("\tdiconnecting at [%s]\n",Get_Time_String(t));    
      fprintf(log,"\tdisconnecting at [%s]\n",Get_Time_String(t));

      close(new_fd);
      fclose(log);
      exit(0);
    }
    close(new_fd);
  }

}
/*--------------------------------------*/
void Handle_Msg(unsigned char* buf,unsigned char* sender,int skt,FILE* log) {

  grp_query_msg *gq = (grp_query_msg*)buf;
  reg_query_msg *rq = (reg_query_msg*)buf;
  gm_resign_msg *gr = (gm_resign_msg*)buf;
  unsigned char gm[16],grp[3],flag,tmp[16];
  msg_type type=buf[0];
  int req_rec;
  FILE* fp;

  memset(gm,'\0',16);

  switch (type) {
  case GRP_QUERY:
    printf("\tGRP_QUERY...");
    /* get group from msg */
    strncpy(grp,gq->grp,3);
    /* check if file from group exists */
    if ((fp=Is_File(grp))==NULL){
      printf("\tgroup is not registered\n");
      fprintf(log,"\tgroup is not registered\n");
      close(skt);
      fclose(log);
      exit(1);
    }
    /* check if requestor is a member of the group about which
       he is asking */
    if (!(req_rec=Is_Member(fp,sender))){
      printf("\trequestor IP not registered\n");
      fprintf(log,"\trequestor IP not registered\n");
      close(skt);
      fclose(fp);fclose(log);
      exit(1);
    }
    /* determine who is the group manager of the group in question */
    if (GM_Select(fp,gm,sender,req_rec)){
      printf("\trequestor set as GM\n");
      fprintf(log,"\tNew GM set\n");
    }
    /* report the GM back to the requestor */
    printf("\treporting results to requestor\n");
    Report_GM_To_Requestor(skt,gm,grp);
    
    fclose(fp);
    break;

  case REG_QUERY:
    printf("\tREG_QUERY...");
    /* get group from msg */
    strncpy(grp,rq->grp,3);
    /* check if file from group exists */
    if ((fp=Is_File(grp))==NULL){
      printf("\tgroup is not registered\n");
      fprintf(log,"\tgroup is not registered\n");
      close(skt);
      fclose(log);
      exit(1);
    }
    /* check if requestor is a member of the 
       group about which he is asking */
    if (!(req_rec=Is_Member(fp,sender))){
      printf("\trequestor IP not registered\n");
      fprintf(log,"\trequestor IP not registered\n");
      close(skt);
      fclose(fp);fclose(log);
      exit(1);
    }
    /* determine who is the group manager of the group in question */
    GM_Select(fp,gm,sender,req_rec);

    /* check if requestor is the GM of its group */
    if (!strncmp(gm,sender,ADDR_LEN)){
      printf("\tGM status validated.\n");
      /* check if entity GM is asking 
	 about is a member of the group
	 about which he is asking */
      memset(tmp,'\0',ADDR_LEN+1);
      strncpy(tmp,rq->addr,ADDR_LEN);
      printf("\tasking about: %s\n",tmp); 
      flag=(unsigned char)Is_Member(fp,tmp);//'0' if NO

      /* Send the result to the GM */
      printf("\treporting results (%d) to requestor\n",flag);
      Send_Reg_Reply(flag,gm,grp,rq->addr,skt);
    }

    fclose(fp);
    break;

  case GM_RESIGN:
    printf("\tGM_RESIGN...");
    /* get group from msg */
    strncpy(grp,gr->grp,3);
    /* check if file from group exists */
    if ((fp=Is_File(grp))==NULL){
      printf("\tgroup is not registered\n");
      fprintf(log,"\tgroup is not registered\n");
      close(skt);
      fclose(log);
      exit(1);
    }
    /* check if requestor is a member of the 
       group about which he is asking */
    if (!(req_rec=Is_Member(fp,sender))){
      printf("\trequestor IP not registered\n");
      fprintf(log,"\trequestor IP not registered\n");
      close(skt);
      fclose(fp);fclose(log);
      exit(1);
    }
    /* determine who is the group manager of the group in question */
    GM_Select(fp,gm,sender,req_rec);

    /* check if requestor is the GM of its group */
    if (!strncmp(gm,sender,ADDR_LEN)){
      /* remove GM from grp file */
      printf("\tGM status validated...removing status\n");
      Remove_GM(fp,req_rec);
    }

    fclose(fp);
    break;

  case GRP_REPLY:
    /* outgoing message-ignore */
    break;
  case REG_REPLY:
    /* outgoing message-ignore */
    break;
  }

}
/*--------------------------------------*/
FILE* Is_File(unsigned char* grp) {
  
  char file[12];

  memset(file,'\0',12);
  sprintf(file,"grp%c%c%c.stat",grp[0],grp[1],grp[2]);

  return (fopen(file,"r+"));

}
/*--------------------------------------*/
int Is_Member(FILE* fp,char* ip) {

  char line[50],*ptr=NULL;
  int cnt=0;

  rewind(fp);
  while ((fgets(line,50,fp)!=NULL)&&(ptr==NULL)){
    ptr=strstr(line,ip);
    cnt++;
  }

  if (ptr==NULL) 
    return 0;
  return cnt;

}
/*--------------------------------------*/
int GM_Select(FILE* fp,unsigned char* gm,char* ip,int cnt) {

  char line[50],newline[50],*pos,*ptr=NULL,*gptr=ip;
  int i,flag=0;
  unsigned char p,c,n;

  memset(line,'\0',50);
  memset(newline,'\0',50);

  rewind(fp);
  while ((gptr!=NULL)&&(ptr==NULL)){
    gptr=fgets(line,50,fp);
    ptr=strstr(line,"*");
  }
  if (ptr!=NULL){// GM already exists
    pos=strstr(line,"\t");
    strncpy(gm,line,pos-line);
  } else {//no GM yet

    /* make caller the GM */
    strncpy(gm,ip,ADDR_LEN);

    /* go to new GM line */
    rewind(fp);
    for (i=0;i<cnt-1;i++)
      fgets(line,50,fp);

    while ((c=(unsigned char)fgetc(fp))!=0xff) {

      if (!flag){// still searching for proper insert point
	if (c!='\t'){// before insert point
	fseek(fp,-1,SEEK_CUR);
	fputc(c,fp);
	} else {// insert point found
	  n=(unsigned char)fgetc(fp);
	  fseek(fp,-2,SEEK_CUR);
	  fputc(c,fp);
	  fputc('*',fp);
	  p=n;
	  flag=1;
	}
      } else {// after inserting '*'
	fseek(fp,-1,SEEK_CUR);
	fputc(p,fp);
	p=c;
      }
    }
    fputc('\n',fp);
  }
  return flag;

}
/*--------------------------------------*/
void Report_GM_To_Requestor(int fd,unsigned char* gm,unsigned char *grp) {

  unsigned char buf[MSG_LEN];
  grp_reply_msg *gq = (grp_reply_msg*)buf;

  gq->type = GRP_REPLY;
  strncpy(gq->addr,gm,ADDR_LEN);
  strncpy(gq->grp,grp,GRP_LEN);

  if (send(fd,buf,MSG_LEN,0)==-1)
    Err_Exit("send GM");

}
/*--------------------------------------*/
char* Get_Time_String(char* s) {

  time_t cal_time;

  time(&cal_time);
  s=ctime(&cal_time);
  s[24]='\0';
  return s;

}
/*--------------------------------------*/
void Err_Exit(char* str) {

  perror(str);
  exit(errno);

}
/*--------------------------------------*/
 void Send_Reg_Reply (unsigned char flag,\
		      unsigned char* gm,\
		      unsigned char* grp,\
		      unsigned char* addr,\
		      int sock){ 

   unsigned char buf[MSG_LEN];
   reg_reply_msg *rr = (reg_reply_msg*)buf;

   memset(buf,'\0',MSG_LEN);
   
   rr->type = REG_REPLY;
   rr->flag = flag;
   strncpy(rr->grp,grp,GRP_LEN);
   strncpy(rr->addr,addr,ADDR_LEN);

   if (send(sock,buf,MSG_LEN,0)==-1)
    Err_Exit("send Reg_Reply");  

 }
/*--------------------------------------*/
void Remove_GM (FILE* fp,int cnt) {

  unsigned char c;
  int flag=0,start,last;

  rewind(fp);
  start=ftell(fp);
  while ((c=(unsigned char)fgetc(fp))!=0xff){

    if (!flag){
      if (c!='*') {
	fseek(fp,-1,SEEK_CUR);
	fputc(c,fp);
      } else {     
	flag=1;
	continue;
      }
    } 
    else {
      fseek(fp,-2,SEEK_CUR);
      fputc(c,fp);
      fseek(fp,1,SEEK_CUR);
      last=ftell(fp);
    }
  }
  ftruncate(fileno(fp),(off_t)(last-start-1));

}
/*--------------------------------------*/
