/*
 * The olsr.org Optimized Link-State Routing daemon(olsrd)
 * Copyright (c) 2005, Andreas Tønnesen(andreto@olsr.org)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 * * Redistributions of source code must retain the above copyright 
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright 
 *   notice, this list of conditions and the following disclaimer in 
 *   the documentation and/or other materials provided with the 
 *   distribution.
 * * Neither the name of olsr.org, olsrd nor the names of its 
 *   contributors may be used to endorse or promote products derived 
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Visit http://www.olsr.org for more information.
 *
 * If you find this software useful feel free to make a donation
 * to the project. For more information see the website or contact
 * the copyright holders.
 *
 * $Id: ohs_cmd.c,v 1.24 2007/07/28 12:58:23 bernd67 Exp $
 */

#include "olsr_host_switch.h"
#include "olsr_types.h"
#include "commands.h"
#include "link_rules.h"
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>


#define TOK_BUF_SIZE 500
static char tok_buf[TOK_BUF_SIZE];

#define MAX_OLSRD_ARGS 10
static char olsrd_path[FILENAME_MAX];

static int
get_next_token(char *src, char *dst, size_t buflen)
{
  int i = 0, j = 0;

  dst[0] = 0;
  /* Skip leading spaces */
  while(src[j] == ' ' && src[j] != 0)
    {
      j++;
    }

  src += j;
  i = 0;
  while((src[i] != ' ') && (src[i] != 0) && (i < ((int)buflen - 1)))
    {
      dst[i] = src[i];
      i++;
    }
  dst[i] = 0;

  //if(strlen(dst))
  //printf("Extracted token: %s\n", dst);
  return i + j;
}

int
ohs_set_olsrd_path(char *path)
{
  strncpy(olsrd_path, path, FILENAME_MAX);
  return 0;
}

#ifdef WIN32
int
ohs_cmd_olsrd(char *args __attribute__((unused)))
{
  printf("olsrd command not available in windows version\nStart instances manually\n");
  return 0;
}
#else
int
ohs_cmd_olsrd(char *args)
{
  char *olsrd_args[MAX_OLSRD_ARGS];
  struct in_addr iaddr;

  args += get_next_token(args, tok_buf, TOK_BUF_SIZE);

  if(!strlen(tok_buf))
    goto print_usage;

  /* Start olsrd instance */
  if(!strncmp(tok_buf, "start", strlen("start")))
    {
      int argc = 0, i = 0;

      args += get_next_token(args, tok_buf, TOK_BUF_SIZE);
      
      if(!strlen(tok_buf))
	goto print_usage;

      if(!inet_aton(tok_buf, &iaddr))
	{
	  printf("Invalid IP %s\n", tok_buf);
	  goto print_usage;
	}

      olsrd_args[argc++] = olsrd_path;

      if(1) /* config file is set */
	{
	  olsrd_args[argc++] = "-f";
	  olsrd_args[argc++] = "/etc/olsrd-emu.conf";
	}
      olsrd_args[argc++] = "-hemu";
      olsrd_args[argc++] = tok_buf;

      olsrd_args[argc++] = "-d";
      olsrd_args[argc++] = "0";
      olsrd_args[argc++] = "-nofork";
      olsrd_args[argc] = NULL;

      printf("Executing: %s", olsrd_path);
      for(i = 0; i < argc; i++)
	printf(" %s", olsrd_args[i]);
      printf("\n");

      if(fork())
	return 1;

      if(execve(olsrd_path, olsrd_args, NULL) < 0)
	{
	  printf("Error executing olsrd: %s\n", strerror(errno));
	  exit(1);
	}
    }
  /* Stop olsrd instance */
  else if(!strncmp(tok_buf, "stop", strlen("stop")))
    {
      struct ohs_connection *oc;

      args += get_next_token(args, tok_buf, TOK_BUF_SIZE);
      
      if(!strlen(tok_buf))
	goto print_usage;

      if(!inet_aton(tok_buf, &iaddr))
	{
	  printf("Invalid IP %s\n", tok_buf);
	  goto print_usage;
	}

      oc = get_client_by_addr((union olsr_ip_addr *)&iaddr.s_addr);

      if(!oc)
	{
	  printf("No such client: %s\n", tok_buf);
	  return -1;
	}
      ohs_delete_connection(oc);
      
      return 1;
    }
  /* Set olsrd binary path */
  else if(!strncmp(tok_buf, "setb", strlen("setb")))
    {
      struct stat sbuf;

      args += get_next_token(args, tok_buf, TOK_BUF_SIZE);
      
      if(!strlen(tok_buf))
	goto print_usage;

      if(stat(tok_buf, &sbuf) < 0)
	{
	  printf("Error setting binary \"%s\": %s\n",
		 tok_buf, strerror(errno));
	  return -1;
	}

      if((sbuf.st_mode & S_IFDIR) || !(sbuf.st_mode & S_IXUSR))
	{
	  printf("Error setting binary \"%s\": Not a regular execuatble file!\n",
		 tok_buf);
	  return -1;
	}

      printf("New olsrd binary path:\"%s\"\n", tok_buf);
      ohs_set_olsrd_path(tok_buf);

      return 1;

    }
  /* Set arguments */
  else if(!strncmp(tok_buf, "seta", strlen("seta")))
    {
	printf("Error - NOT IMPLEMENTED YET\n");
	return 1;
    }
  /* Show settings */
  else if(!strncmp(tok_buf, "show", strlen("show")))
    {
      printf("olsrd command settings:\n\tBinary path: %s\n\tArguments  : \n",
	     olsrd_path);
      return 1;
    }

 print_usage:
  printf("Usage: olsrd [start|stop|show|setb|seta] [IP|path|args]\n");
  return 0;
}
#endif

int
ohs_cmd_link(char *args)
{
  olsr_u8_t bi = 0, wildc_src = 0, wildc_dst = 0;
  struct ohs_connection *src, *dst;
  struct in_addr iaddr;
  int qual;
  struct ohs_ip_link *link, *inv_link;

  args += get_next_token(args, tok_buf, TOK_BUF_SIZE);

  if(!strlen(tok_buf))
    goto print_usage;

  if(!strncmp(tok_buf, "bi", strlen("bi")))
    {
      bi = 1;
      args += get_next_token(args, tok_buf, TOK_BUF_SIZE);

      if(!strlen(tok_buf))
	goto print_usage;
    }

  if(tok_buf[0] == '*')
    {
      wildc_src = 1;
      src = ohs_conns;
    }
  else
    {
      if(!inet_aton(tok_buf, &iaddr))
	{
	  printf("Invalid src IP %s\n", tok_buf);
	  return -1;
	}

      src = get_client_by_addr((union olsr_ip_addr *)&iaddr.s_addr);

      if(!src)
	{
	  printf("No such client: %s!\n", tok_buf);
	  return -1;
	}
    }

  args += get_next_token(args, tok_buf, TOK_BUF_SIZE);
  
  if(!strlen(tok_buf))
    goto print_usage;

  if(tok_buf[0] == '*')
    {
      wildc_dst = 1;
      dst = ohs_conns;
    }
  else
    {
      
      if(!inet_aton(tok_buf, &iaddr))
	{
	  printf("Invalid src IP %s\n", tok_buf);
	  return -1;
	}
      
      dst = get_client_by_addr((union olsr_ip_addr *)&iaddr.s_addr);
      if(!dst)
	{
	  printf("No such client: %s!\n", tok_buf);
	  return -1;
	}
    }

  args += get_next_token(args, tok_buf, TOK_BUF_SIZE);
  
  if(!strlen(tok_buf))
    goto print_usage;

  /* No use for bi if both src and dst are widcards */
  if(wildc_src && wildc_dst)
    {
      bi = 0;
    }

  qual = atoi(tok_buf);

  if(qual < 0 || qual > 100)
    {
      printf("Link quality out of range(0-100)\n");
      return -1;
    }

  while(src)
    {

      while(dst)
	{

	  if(src == dst)
	    goto next_iteration;

	  link = get_link(src, &dst->ip_addr);
	  
	  inv_link = bi ? get_link(dst, &src->ip_addr) : NULL;
	  
	  if(qual == 100)
	    {
	      /* Remove link entry */
	      if(link)
		remove_link(src, link);
	      if(inv_link)
		remove_link(dst, inv_link);
	    }
	  else 
	    {
	      if(!link)
		{
		  /* Create new link */
		  link = add_link(src, dst);
		}
	      
	      link->quality = qual;
	      
	      if(bi)
		{
		  if(!inv_link)
		    {
		      /* Create new link */
		      inv_link = add_link(dst, src);
		    }
		  inv_link->quality = qual;
		}
	    }

	  printf("%s %sdirectional link(s) %s %c=> %s quality %d\n", 
		 (qual == 100) ? "Removing" : "Setting", bi ? "bi" : "uni",
		 olsr_ip_to_string(&src->ip_addr), bi ? '<' : '=', 
		 olsr_ip_to_string(&dst->ip_addr), qual);

	next_iteration:
	  if(wildc_dst)
	    dst = dst->next;
	  else
	    break;
	}
      dst = wildc_dst ? ohs_conns : dst;
      src = wildc_src ? src->next : NULL;
    }

  return 1;
 print_usage:
  printf("link <bi> srcIP dstIP [0-100]");
  return -1;
}

int
ohs_cmd_list(char *args)
{
  struct ohs_connection *oc = ohs_conns;

  args += get_next_token(args, tok_buf, TOK_BUF_SIZE);
  
  if(!strlen(tok_buf) || 
     !strncmp(tok_buf, "clients", strlen("clients")))
    {
      printf("All connected clients:\n");
      
      while(oc)
	{
	  printf("\t%s - Rx: %d Tx: %d LinkCnt: %d\n", olsr_ip_to_string(&oc->ip_addr), 
		 oc->rx, oc->tx, oc->linkcnt);
	  oc = oc->next;
	}
    }
  else if(!strncmp(tok_buf, "links", strlen("links")))
    {
      printf("All configured links:\n");
      
      while(oc)
	{
	  struct ohs_ip_link *links = oc->links;

	  while(links)
	    {
	      printf("\t%s => %s Quality: %d\n", 
		     olsr_ip_to_string(&oc->ip_addr),
		     olsr_ip_to_string(&links->dst),
		     links->quality);

	      links = links->next;
	    }
	  oc = oc->next;
	}
    }
  else
    {

      printf("list [clients|links]");
      return -1;
    }

  return 1;
}

int
ohs_cmd_help(char *args)
{
  int i;

  args += get_next_token(args, tok_buf, TOK_BUF_SIZE);
  
  if(!strlen(tok_buf))
    {
      printf("Olsrd host switch version %s\n", OHS_VERSION);
      printf("Available commands:\n");
      
      for(i = 0; ohs_commands[i].cmd; i++)
	{
	  if(ohs_commands[i].helptext_brief)
	    printf("\t%s - %s\n", 
		   ohs_commands[i].cmd,
		   ohs_commands[i].helptext_brief);
	}
      printf("\nType 'help cmd' for help on a specific command\n");
    }
  else
    {
      for(i = 0; ohs_commands[i].cmd; i++)
	{
	  if(!strncmp(tok_buf, ohs_commands[i].cmd, strlen(ohs_commands[i].cmd)))
	    {
	      printf("Usage: %s\nDescription:\n%s\n", 
		     ohs_commands[i].syntax,
		     ohs_commands[i].helptext_long);
	      return 1;
	    }
	}

      printf("Usage: help <command>\n");
    }

  return i;
}

int
ohs_cmd_log(char *args)
{
  olsr_u8_t set = 0;

  args += get_next_token(args, tok_buf, TOK_BUF_SIZE);
  
  if(strlen(tok_buf) &&
     ((set = !strncmp(tok_buf, "set", strlen("set"))) || 
      !strncmp(tok_buf, "unset", strlen("unset"))))
    {
        olsr_u32_t new_bit = 0;
        
        args += get_next_token(args, tok_buf, TOK_BUF_SIZE);
  
        if(!strlen(tok_buf))
            goto print_usage;
        
        
        if(!strncmp(tok_buf, "CON", strlen("CON")))
	    new_bit = LOG_CONNECT;
        else if(!strncmp(tok_buf, "FOR", strlen("FOR")))
	    new_bit = LOG_FORWARD;
        else if(!strncmp(tok_buf, "LIN", strlen("LIN")))
	    new_bit = LOG_LINK;
	  
        if(!new_bit)
	    goto print_usage;

        if(set)
	    logbits |= new_bit;
        else
	    logbits &= ~new_bit;

        printf("%s log bit: 0x%08x, new log: 0x%08x\n", set ? "Setting" : "Removing",
               new_bit, logbits);

    }
  else
    {
      if(strlen(tok_buf))
	goto print_usage;

      printf("Log: (0x%08x) ", logbits);
      if(logbits & LOG_CONNECT)
	printf("CONNECT ");
      if(logbits & LOG_FORWARD)
	printf("FORWARD ");
      if(logbits & LOG_LINK)
	printf("LINK ");

      printf("\n");
    }
  return 1;

 print_usage:
  printf("Usage: log <[set|unset] [CONNECT|FORWARD|LINK]>\n");
  return 0;

}

int
ohs_cmd_exit(char *args __attribute__((unused)))
{

  printf("Exitting... bye-bye!\n");

  ohs_close(0);
}

void
ohs_parse_command(void)
{
  static char cmd_line[500];
  static int cmd_len = 0;
  char *args;
  char cmd_token[20];
  int i;
#if defined WIN32
  char c;
  unsigned long Read;
  INPUT_RECORD InRec;
  KEY_EVENT_RECORD *KeyEventRec;
#endif

#if defined WIN32
  if (!ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &InRec, sizeof (InRec), &Read))
  {
    fprintf(stderr, "ReadConsoleInput failed: %s\n", strerror(GetLastError()));
    return;
  }

  if (InRec.EventType != KEY_EVENT)
    return;

  KeyEventRec = &InRec.Event.KeyEvent;

  if (!KeyEventRec->bKeyDown)
    return;

  c = KeyEventRec->uChar.AsciiChar;

  if (c == 8)
  {
    if (cmd_len == 0)
      return;

    cmd_len--;

    fputc(8, stdout);
    fputc(32, stdout);
    fputc(8, stdout);

    fflush(stdout);

    return;
  }

  fputc(c, stdout);
  fflush(stdout);

  if (c != '\n' && c != '\r' && cmd_len < (int)sizeof (cmd_line) - 1)
    cmd_line[cmd_len++] = (char)c;

  else
#else
  if(fgets(cmd_line, sizeof (cmd_line), stdin) == NULL) {
    ohs_cmd_exit(NULL);
  }

  for (cmd_len = 0; cmd_line[cmd_len] != 0 && cmd_line[cmd_len] != '\n';
       cmd_len++);
#endif

  {
    cmd_line[cmd_len] = 0;
    cmd_len = 0;

    args = cmd_line + get_next_token(cmd_line, cmd_token, sizeof (cmd_token));

    for (i = 0; ohs_commands[i].cmd != NULL; i++)
    {
      if (strcmp(cmd_token, ohs_commands[i].cmd) == 0)
      {
        if(ohs_commands[i].cmd_cb != NULL)
          ohs_commands[i].cmd_cb(args);

        else
          printf("No action registered on cmd %s!\n", cmd_token);

        break;
      }
    }
  
    if(ohs_commands[i].cmd == NULL)
      printf("%s: no such cmd!\n", cmd_token);

    printf("\n> ");
    fflush(stdout);
  }
}


syntax highlighted by Code2HTML, v. 0.9.1