/* $Id: mhost.c,v 1.1 1999/08/23 16:18:34 naamato Exp $ */
/*
 * Copyright(c) 1998 by Hitachi.Ltd All rights reserved.
 *
 */

#include "defs.h"
#include "igmp_def.h"
#include "./config2/config.h"

#define FILE_NAME_SIZE 256

#define CNF_QUIT   3 

char usage[] = "Usage: mhost [-f conf]\n";
char version[]="IGMP tool (mhost) --  Version 1.0 Beta1";
char copyright[]="All Right Reserved, Copyright(c) 1998, Hitachi,Ltd";

extern int igmp_sock;  /* in igmp.c */
extern int insock;     /* in config2.c */
extern char *recv_buf; /* in igmp.c */
extern int trace_mode; /* in trace.c */

/* local function */
void wait_command();
void wait_timeout();
void print_usage();

/* external function */
char *timer_init();

char *if_name;
FILE *fp;
char *config;
CONFIG *configp;

char *wait_timer;
int wait_flag=0;

#define FILE_MODE        0
#define INTERACTIVE_MODE 1

int mode;
int debug = 0;

main(argc, argv)
int argc;
char **argv;
{
    char opt;
    extern char *optarg;
    u_long src,dst,grp;
    int recv_len;
    int dummy;
    fd_set read_fd,igmp_fd,tmp_fd;
    struct timeval  t;
    int rtn;
    char *log_file;
    int n;
    int fd_val, fd_wid;

    grp = 0;
    dst = src = 0;
    if_name=log_file=NULL;
    dummy = rtn = 0;
    
    while((opt = getopt(argc,argv,"f:l:d:h")) != -1) {
        switch(opt) {
	case 'f' : configp=init_config(optarg);
	           mode = FILE_MODE;
	           rtn = 0;
	           break;
	case 'l' : log_file = malloc(FILE_NAME_SIZE);
	           strcpy(log_file,optarg);
	           break;
	case 'd' : debug = atoi(optarg);
	           break;
	case 'h' : fprintf(stderr,usage);
	           exit(1);
	default:   fprintf(stderr,usage);
	           exit(1);
	}
    }

    printf("\n %s \n\n",version);
/*    printf(" %s \n\n",copyright);*/

    if(!configp) {
	configp = init_config(NULL);
	mode = INTERACTIVE_MODE;
	printf(" Running interactive mode , Enter 'help' or command:\n");
	printf(" mhost> ");
	fflush(stdout);
    }

    interface_init();
    igmp_init();

    multicast_init();
    host_init(log_file);

    time_init();

    FD_ZERO(&read_fd);
    FD_SET(igmp_sock, &read_fd);
    FD_SET(igmp_sock, &igmp_fd);

    if(igmp_sock >= insock) fd_wid = igmp_sock+1;
    else fd_wid = insock+1;

    t.tv_sec = 0;
    t.tv_usec = 10000;
    for(;;) {
        timer_check();
	FD_ZERO(&read_fd);
	FD_SET(igmp_sock, &read_fd);
	if(mode == INTERACTIVE_MODE)
	    FD_SET(insock, &read_fd);

	if((n=select(fd_wid,&read_fd,NULL,NULL,&t))<0) {
	    perror("select");
	    continue;
	}

	for(fd_val=0; n--; fd_val++) {

	    while(!FD_ISSET(fd_val, &read_fd))
		fd_val++;

	    if(FD_ISSET(fd_val,&igmp_fd)) {
	        recv_len = recvfrom(igmp_sock,recv_buf,1024,0,NULL,&dummy);
		igmp_recv(recv_len);
	    }

	    if(mode == INTERACTIVE_MODE && fd_val == insock) {
	        rtn = do_command();
		if(rtn == CNF_QUIT) break;
		printf(" mhost> ");
		fflush(stdout);
	    }
	}
	
	if(mode == FILE_MODE) {
	    if(!wait_flag) {
	        rtn = do_command();
		if(rtn == CNF_QUIT) break;
		else if(rtn == CNF_EOF) {
		    end_config(configp);
		    configp = init_config(NULL);
		    mode = INTERACTIVE_MODE;
		    printf(" \n Transit to interactive mode, Enter 'help' or command:\n");
		    printf(" mhost> ");
		    fflush(stdout);
		}
	    }
	}
	else
	    if(rtn == CNF_QUIT) break;

    }

    end_config(configp);

    multicast_term();
}

typedef enum {
    C_HOST_LIST=1,
    C_CONF_FILE,
    C_TRACE_FILE,
    C_FILTER_LIST,
    C_HOST,
    C_QUERY,
    C_REPORT,
    C_LEAVE,
    C_WAIT,
    C_DUMP,
    C_DEBUG,
    C_NODEBUG,
    C_QUIT,
    C_HELP
} COMMAND;

typedef enum {
    HC_JOIN=1,
    HC_LEAVE,
    HC_JOIN_LEAVE,
    HC_UP,
    HC_DOWN,
    HC_FILTER,
    HC_NO_FILTER
} HOST_COMMAND;

typedef enum {
    PC_SEND=1,
    PC_RECV
} PKT_COMMAND;

typedef enum {
    P_ADDR=1,
    P_VER,
    P_GEN,
    P_TIMEOUT,
    P_GROUP,
    P_CODE,
    P_NORPLY,
    P_PKT,
    P_SRC,
    P_DST,
    P_NOALERT,
    P_TRACEFILE
} HOST_PARAM;

typedef enum {
    P_PKT_QUERY=1,
    P_PKT_REPORT,
    P_PKT_LEAVE
} P_PKT_TYPE;

typedef enum {
    DBG_DETAIL=1,
    DBG_TTY
} DBG_COMMAND;

struct token {
    int type;
    char *name;
};

struct token cmd_token[]= {
    {C_HOST_LIST,  "host-list"},
    {C_CONF_FILE,  "config-file"},
    {C_TRACE_FILE, "trace-file"},
    {C_FILTER_LIST, "filter-list"},
    {C_HOST,       "host"},
    {C_QUERY,      "query"},
    {C_REPORT,     "report"},
    {C_LEAVE,      "leave"},
    {C_WAIT,       "wait"},
    {C_DUMP,       "dump"},
    {C_DEBUG,      "debug"},
    {C_NODEBUG,    "nodebug"},
    {C_QUIT,       "quit"},
    {C_HELP,       "help"}
};

struct token host_cmd_token[]={
    {HC_JOIN, "join"},
    {HC_LEAVE, "leave"},
    {HC_JOIN_LEAVE, "join-leave"},
    {HC_UP,    "up"},
    {HC_DOWN,  "down"},
    {HC_FILTER, "filter"},
    {HC_NO_FILTER, "no-filter"}
};

struct token pkt_cmd_token[]={
    {PC_SEND, "send"},
    {PC_RECV, "recv"}
};

struct token param_token[]={
    {P_ADDR,  "addr"},
    {P_VER,   "v1"},
    {P_GEN,   "generate"},
    {P_TIMEOUT, "timeout"},
    {P_GROUP, "group"},
    {P_CODE, "code"},
    {P_NORPLY,"noreply"},
    {P_PKT,  "pkt"},
    {P_SRC,  "src"},
    {P_DST,  "dst"},
    {P_NOALERT,"noalert"},
    {P_TRACEFILE, "tracefile"}
};

struct token pkt_type[]={
    {P_PKT_QUERY, "query"},
    {P_PKT_REPORT, "report"},
    {P_PKT_LEAVE, "leave"}
};

struct token dbg_cmd_token[]={
    {DBG_DETAIL, "detail"},
    {DBG_TTY, "tty"}
};

#define FILTER_QUERY  0x01
#define FILTER_REPORT 0x02
#define FILTER_LEAVE  0x04

#define TRACE_COMMAND(tbl)  \
{ \
      int c; \
      trace(stderr," %s ",tbl.command); \
      for(c=0;c<tbl.paramcount;c++) trace(stderr,"%s ",tbl.paramtbl[c]); \
      trace(stderr,"\n"); \
}

#define PARAMTBL(cnt)   gettbl.paramtbl[cnt++]

log_command(cmd,tbl)
int cmd;
CONF_PARAGET *tbl;
{
      int c;
      char log_buf[256], temp[16];

      if(cmd == C_QUIT) {
	  strcpy(log_buf,"CMD quit \n");
      }
      else {
	  sprintf(log_buf,"CMD %s ",tbl->command); 
	  for(c=0;c<tbl->paramcount;c++) {
	      sprintf(temp,"%s ",tbl->paramtbl[c]);
	      strcat(log_buf,temp);
	  }
      }
      log(LOG_INFO,0," %s\n",log_buf); 
}

int token_type(list,token)
struct token *list;
char *token;
{
    struct token *p;

    for(p=list;p->name != NULL; p++) {
	if(!strcmp(token,p->name)) {
	    return p->type;
	}
    }

    return 0;
}

int do_command()
{
    char c,buf[256],*bufp,if_name[8];
    char cmd,sub_cmd;
    char code;
    u_long addr,id,src;
    CONF_PARAGET gettbl;
    int rt;
    int cnt;
    int ver,gen,host_num;
    int interval,period;
    int param;
    int type,type2;
    int isalert;

    if((rt = get_config_command(configp,&gettbl))<0) {
	printf(" Config read error !\n");
	return 0;
    }

    if(rt == CNF_EOF) {
	return CNF_EOF;
    }
    else {
	cmd = token_type(cmd_token,gettbl.command);
	if(mode == FILE_MODE)
	    TRACE_COMMAND(gettbl);
    }

    log_command(cmd,&gettbl);
    switch(cmd) {
    case C_HOST_LIST : 
	cnt = 0;
	host_num = 1;
        ver = IGMP_VERSION_2;
	id = atol(PARAMTBL(cnt));
	param = token_type(param_token,PARAMTBL(cnt));
	if(P_ADDR == param) {
	    addr = inet_stoa(PARAMTBL(cnt));
        }
	else if(P_TRACEFILE == param) {
	    host_trace(id,PARAMTBL(cnt));
	    break;
	}
	else {
	    printf(" Unknown parameter at '%s' command\n",gettbl.command);
	    break;
	}
	for(;cnt<gettbl.paramcount;) {
	    param = token_type(param_token,PARAMTBL(cnt));
	    if(P_VER == param)
		ver = IGMP_VERSION_1;
	    else if(P_GEN == param)
		host_num = atoi(PARAMTBL(cnt));
	    else {
		printf(" Unknown parameter at '%s %d' command\n",gettbl.command,param);
		break;
	    }
	}
	while(host_num > 0) {
	    host_add(id,addr,ver);
	    --host_num;
	    id++;
	    addr++;
	}
	break;
    case C_CONF_FILE :
	printf(" config-file \n");
	break;
    case C_FILTER_LIST:
	break;
    case C_HOST : 
	cnt = 0;
	sub_cmd = token_type(host_cmd_token,PARAMTBL(cnt));
	id = atol(PARAMTBL(cnt));
	param = token_type(param_token,PARAMTBL(cnt));
	if(param == P_GROUP)
	   addr = inet_stoa(PARAMTBL(cnt));
	else if(param == P_PKT)
	   type = token_type(pkt_type,PARAMTBL(cnt));
	else {
	    printf(" Unknown parameter at '%s' command\n",gettbl.command);
	    return rt;
	}
	if(sub_cmd == HC_JOIN) {
	    join_group(id,addr);
	}
	else if(sub_cmd == HC_LEAVE) {
	    leave_group(id,addr);
	}
	else if(sub_cmd == HC_JOIN_LEAVE) {
	    period = atoi(PARAMTBL(cnt));
	    interval = atoi(PARAMTBL(cnt));
	    join_leave_group(id,addr,period,interval);
	}
	else if(sub_cmd == HC_UP) {
	    host_state(id,1);
	}
	else if(sub_cmd == HC_DOWN) {
	    host_state(id,0);
	}
	else if(sub_cmd == HC_FILTER) {
	    if(param == P_PKT) {
		if(type == P_PKT_QUERY) type2 = FILTER_QUERY;
		else if(type == P_PKT_REPORT) type2 = FILTER_REPORT;
		else if(type == P_PKT_LEAVE) type2 = FILTER_LEAVE;
		host_filter_packet(id,type2,1);
	    }
	}
	else if(sub_cmd == HC_NO_FILTER) {
	    if(param == P_PKT) {
		if(type == P_PKT_QUERY) type2 = FILTER_QUERY;
		else if(type == P_PKT_REPORT) type2 = FILTER_REPORT;
		else if(type == P_PKT_LEAVE) type2 = FILTER_LEAVE;
		host_filter_packet(id,type2,0);
	    }
	}
	else {
	    printf(" Unknown operation at '%s' command \n",gettbl.command);
	}
	break;
    case C_QUERY :
	cnt = 0;
	addr = 0;
	code = DEFAULT_RESPONSE_TIME;
	isalert = ROUTER_ALERT_ON;
	sub_cmd = token_type(pkt_cmd_token,PARAMTBL(cnt));
	strcpy(if_name,PARAMTBL(cnt));
	if(sub_cmd == PC_RECV) {
	    for(;cnt<gettbl.paramcount;) {
		param = token_type(param_token,PARAMTBL(cnt));
		if(P_TIMEOUT == param) {
		    interval = atoi(PARAMTBL(cnt));
		    wait_command(interval);
		}
		else if(P_GROUP == param) {
		    addr = inet_stoa(PARAMTBL(cnt));
		}
		else if(P_CODE == param) {
		    code = (char)atoi(PARAMTBL(cnt));
		}
		else {
		    printf(" Unknown parameter at '%s' command\n",gettbl.command);
		    return rt;
		}
	    }
	}
	else {
	    printf(" Unknown operation at '%s' command\n",gettbl.command);
	}

	packet_recv(code,addr,interval);
	break;
    case C_REPORT:
    case C_LEAVE:
	cnt = 0;
	addr = src = 0;
	isalert = ROUTER_ALERT_ON;
	sub_cmd = token_type(pkt_cmd_token,PARAMTBL(cnt));
	strcpy(if_name,PARAMTBL(cnt));
	if(sub_cmd == PC_SEND) {
	    ver = IGMP_VERSION_2;
	    for(;cnt<gettbl.paramcount;) {
		param = token_type(param_token,PARAMTBL(cnt));
		if(P_VER == param) {
		    ver = IGMP_VERSION_1;
		}
		else if(P_GROUP == param) {
		    addr = inet_stoa(PARAMTBL(cnt));
		}
		else if(P_NOALERT == param) {
		    isalert = ROUTER_ALERT_OFF;
		}
		else if(P_SRC == param) {
		    src = inet_stoa(PARAMTBL(cnt));
		}
		else {
		    printf(" Unknown parameter at '%s' command\n",gettbl.command);
		    return rt;
		}
	    }
	    if(addr) {
		if(cmd == C_REPORT) {
		    igmp_report_send(if_name,src,addr,addr,ver,isalert);
		}
		else {
		    igmp_leave_send(if_name,src,0,addr,isalert);
		}
	    }
	    else printf(" Syntax Error \n");
	}
	else {
	    printf(" Unknown operation at '%s' command\n",gettbl.command);
	}
	break;
    case C_WAIT : 
	cnt = 0;
	interval = atol(PARAMTBL(cnt));
	if(cnt < gettbl.paramcount) {
	    if(P_NORPLY == token_type(param_token,PARAMTBL(cnt))) {
	  	for(;cnt<=gettbl.paramcount;) {
		    id = atol(PARAMTBL(cnt));
		    printf(" %d noresponse \n");
		}
	    }
	}

	wait_command(interval);
	break;
    case C_TRACE_FILE:
	id = atol(gettbl.paramtbl[0]);
	host_trace(id,gettbl.paramtbl[1]);
	break;
    case C_DUMP:
	id = atol(gettbl.paramtbl[0]);	
	if(id) {
	    host_dump(id,gettbl.paramtbl[1]);
	}
	else 
	    dump();
	break;
    case C_DEBUG:
	cnt = 0;
	trace_mode = TRACE_MODE_PKT;
	for(;cnt<gettbl.paramcount;) {
	    sub_cmd = token_type(dbg_cmd_token,PARAMTBL(cnt));
	    if(sub_cmd == DBG_DETAIL) {
		trace_mode = TRACE_MODE_DETAIL;
	    }
	    else if(sub_cmd == DBG_TTY) {
		open_tty(PARAMTBL(cnt));
		trace2(NULL,"\n");
	    }
	}
	break;
    case C_NODEBUG:
	trace_mode = TRACE_MODE_NONE;
	close_tty();
	break;
    case C_QUIT : 
	rt = CNF_QUIT;
	break;
    case C_HELP : print_usage();
	break;
    default : 
	if(strlen(gettbl.command))
	    printf(" Unknown command '%s'\n",gettbl.command);
	break;
    }

    return rt;
}

void wait_command(interval)
int interval;
{
    if(IS_DEBUG(DEBUG_TRACE))
	log(LOG_DEBUG,0," wait %d\n",interval);

     if(!wait_timer)
	 wait_timer = timer_init(0,NULL,wait_timeout);

     wait_flag = 1;
     timer_set(wait_timer,interval);
}

void wait_timeout(id,data)
u_long id;
char *data;
{
    timer_stop(wait_timer);
    wait_flag = 0;
}

void print_usage()
{
    printf(" \n");
    printf(" host-list <list> addr <host_addr> [v1] \n");
    printf(" trace-file <host_list> <file> \n");
    printf(" host join | leave <host_list> group <group_addr> \n");
    printf(" host up | down <host_lis> \n");
    printf("\n");
    printf(" query recv <interface> [group <group>] [code <code>] timeout <time>\n");
    printf(" report send <interface> group <group> [v1] [noalert] \n");
    printf(" leave send <interface> group <group> [noalert] \n");
    printf(" \n");
    printf(" dump <host_list> <file> \n");
    printf(" wait <time> \n");
    printf(" debug [detail] \n");
    printf(" quit \n");
    printf(" \n");
}
