/*
 * The olsr.org Optimized Link-State Routing daemon(olsrd)
 * Copyright (c) 2004, Andreas Tønnesen(andreto@olsr.org)
 *                     includes code by Bruno Randolf
 * 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: olsrd_pgraph.c,v 1.8 2007/09/13 15:31:59 bernd67 Exp $
 */

/*
 * Dynamic linked library for the olsr.org olsr daemon
 */

#include "olsrd_pgraph.h"
#include "socket_parser.h"
#include "olsrd_plugin.h"
#include "plugin_util.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#ifdef WIN32
#define close(x) closesocket(x)
#endif

#define PLUGIN_NAME    "OLSRD pgraph plugin"
#define PLUGIN_VERSION "0.1"
#define PLUGIN_AUTHOR   "Richard Gopaul"
#define MOD_DESC PLUGIN_NAME " " PLUGIN_VERSION " by " PLUGIN_AUTHOR
#define PLUGIN_INTERFACE_VERSION 5

static union olsr_ip_addr ipc_accept_ip;
static int ipc_port;

static int ipc_socket;
static int ipc_connection;

void my_init(void) __attribute__((constructor));

void my_fini(void) __attribute__((destructor));

/*
 * Defines the version of the plugin interface that is used
 * THIS IS NOT THE VERSION OF YOUR PLUGIN!
 * Do not alter unless you know what you are doing!
 */
int 
olsrd_plugin_interface_version(void)
{
   return PLUGIN_INTERFACE_VERSION;
}


/**
 *Constructor
 */
void
my_init(void)
{
  /* Print plugin info to stdout */
  printf("%s\n", MOD_DESC);

  /* defaults for parameters */
  ipc_port = 2004;
  if (olsr_cnf->ip_version == AF_INET) {
    ipc_accept_ip.v4 = htonl(INADDR_LOOPBACK);
  } else {
    ipc_accept_ip.v6 = in6addr_loopback;
  }
  ipc_socket = -1;
  ipc_connection = -1;
}

/**
 *Destructor
 */
void
my_fini(void)
{
  if(ipc_socket >= 0) {
    close(ipc_socket);
    ipc_socket = -1;
  }
  if(ipc_connection >= 0) {
    close(ipc_connection);
    ipc_connection = -1;
  }

}

static const struct olsrd_plugin_parameters plugin_parameters[] = {
    { .name = "port",   .set_plugin_parameter = &set_plugin_port,      .data = &ipc_port },
    { .name = "accept", .set_plugin_parameter = &set_plugin_ipaddress, .data = &ipc_accept_ip },
};

void olsrd_get_plugin_parameters(const struct olsrd_plugin_parameters **params, int *size)
{
    *params = plugin_parameters;
    *size = sizeof(plugin_parameters)/sizeof(*plugin_parameters);
}

/* Event function to register with the sceduler */
static int pcf_event(int, int, int);

static void ipc_action(int);

#if 0
static struct link_entry *olsr_neighbor_best_link(union olsr_ip_addr *main);
#endif

static void ipc_print_neigh_link(struct neighbor_entry *neighbor);

static void ipc_print_tc_link(struct tc_entry *entry, struct tc_edge_entry *dst_entry);

#if 0
static void ipc_print_net(union olsr_ip_addr *, union olsr_ip_addr *, union hna_netmask *);
#endif

static int ipc_send(const char *, int);

static void ipc_print_neigh_link(struct neighbor_entry *);

static int plugin_ipc_init(void);


static void ipc_print_neigh_link(struct neighbor_entry *neighbor)
{
  char buf[256];
  int len;
  const char* main_adr;
  const char* adr;
//  double etx=0.0;
//  char* style = "solid";
//  struct link_entry* link;

  main_adr = olsr_ip_to_string(&olsr_cnf->main_addr);
  adr = olsr_ip_to_string(&neighbor->neighbor_main_addr);
  len = sprintf( buf, "add link %s %s\n", main_adr, adr );
  ipc_send(buf, len);
  
//  if (neighbor->status == 0) { // non SYM
//  	style = "dashed";
//  }
//  else {
    /* find best link to neighbor for the ETX */
    //? why cant i just get it one time at fetch_olsrd_data??? (br1)
//    if(olsr_plugin_io(GETD__LINK_SET, &link, sizeof(link)) && link)
//    {
//      link_set = link; // for olsr_neighbor_best_link    
//      link = olsr_neighbor_best_link(&neighbor->neighbor_main_addr);
//      if (link) {
//        etx = calc_etx( link->loss_link_quality, link->neigh_link_quality);
//      }
//    }
//  }
    
  //len = sprintf( buf, "\"%s\"[label=\"%.2f\", style=%s];\n", adr, etx, style );
  //len = sprintf( buf, "%s\n", adr );
  //ipc_send(buf, len);
  
   //if (neighbor->is_mpr) {
   //	len = sprintf( buf, "\"%s\"[shape=box];\n", adr );
   //	ipc_send(buf, len);
   //}
}

/**
 *Do initialization here
 *
 *This function is called by the my_init
 *function in uolsrd_plugin.c
 */
int olsrd_plugin_init(void)
{

  /* Initial IPC value */
  ipc_socket = -1;

  /* Register the "ProcessChanges" function */
  register_pcf(&pcf_event);

  return 1;
}

static int plugin_ipc_init(void)
{
  struct sockaddr_in sin;
  olsr_u32_t yes = 1;

  /* Init ipc socket */
  if ((ipc_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
    {
      olsr_printf(1, "(DOT DRAW)IPC socket %s\n", strerror(errno));
      return 0;
    }
  else
    {
      if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0) 
      {
	perror("SO_REUSEADDR failed");
	return 0;
      }

#ifdef __FreeBSD__
      if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *)&yes, sizeof(yes)) < 0) 
      {
	perror("SO_NOSIGPIPE failed");
	return 0;
      }
#endif

      /* Bind the socket */
      
      /* complete the socket structure */
      memset(&sin, 0, sizeof(sin));
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = INADDR_ANY;
      sin.sin_port = htons(ipc_port);
      
      /* bind the socket to the port number */
      if (bind(ipc_socket, (struct sockaddr *) &sin, sizeof(sin)) == -1) 
	{
	  olsr_printf(1, "(DOT DRAW)IPC bind %s\n", strerror(errno));
	  return 0;
	}
      
      /* show that we are willing to listen */
      if (listen(ipc_socket, 1) == -1) 
	{
	  olsr_printf(1, "(DOT DRAW)IPC listen %s\n", strerror(errno));
	  return 0;
	}


      /* Register with olsrd */
      add_olsr_socket(ipc_socket, &ipc_action);
    }

  return 1;
}

static void ipc_action(int fd __attribute__((unused)))
{
  struct sockaddr_in pin;
  socklen_t addrlen;
  char *addr;  
  char buf[256] ;
  int len ;
   
  addrlen = sizeof(struct sockaddr_in);

  if ((ipc_connection = accept(ipc_socket, (struct sockaddr *)  &pin, &addrlen)) == -1)
    {
      olsr_printf(1, "(DOT DRAW)IPC accept: %s\n", strerror(errno));
      exit(1);
    }
  else
    {
      addr = inet_ntoa(pin.sin_addr);
/*
      if(ntohl(pin.sin_addr.s_addr) != ntohl(ipc_accept_ip.s_addr))
	{
	  olsr_printf(1, "Front end-connection from foregin host(%s) not allowed!\n", addr);
	  close(ipc_connection);
	  return;
	}
      else
	{
*/
	  olsr_printf(1, "(DOT DRAW)IPC: Connection from %s\n",addr);
          len = sprintf(buf, "add node %s\n", olsr_ip_to_string(&olsr_cnf->main_addr));
  	  ipc_send(buf, len);
	  pcf_event(1, 1, 1);
//	}
    }

}

/**
 *Scheduled event
 */
static int pcf_event(int changes_neighborhood,
                     int changes_topology,
                     int changes_hna __attribute__((unused)))
{
  int res;
  olsr_u8_t index;
  struct neighbor_entry *neighbor_table_tmp;
  struct tc_entry *tc;
  struct tc_edge_entry *tc_edge;

  res = 0;

  //if(changes_neighborhood || changes_topology || changes_hna)
  if(changes_neighborhood || changes_topology)
    {
      /* Print tables to IPC socket */

      //ipc_send("start ", strlen("start "));

      /* Neighbors */
      for(index=0;index<HASHSIZE;index++)
	{
	  
	  for(neighbor_table_tmp = neighbortable[index].next;
	      neighbor_table_tmp != &neighbortable[index];
	      neighbor_table_tmp = neighbor_table_tmp->next)
	    {
	      ipc_print_neigh_link( neighbor_table_tmp );
	    }
	}

      /* Topology */  
      OLSR_FOR_ALL_TC_ENTRIES(tc) {
          OLSR_FOR_ALL_TC_EDGE_ENTRIES(tc, tc_edge) {
              ipc_print_tc_link(tc, tc_edge);
          } OLSR_FOR_ALL_TC_EDGE_ENTRIES_END(tc, tc_edge);
      } OLSR_FOR_ALL_TC_ENTRIES_END(tc);

      ipc_send(" end ", strlen(" end "));

      /* HNA entries */
//      for(index=0;index<HASHSIZE;index++)
//	{
//	  tmp_hna = hna_set[index].next;
//	  /* Check all entrys */
//	  while(tmp_hna != &hna_set[index])
//	    {
//	      /* Check all networks */
//	      tmp_net = tmp_hna->networks.next;
//	      
//	      while(tmp_net != &tmp_hna->networks)
//		{
//		  ipc_print_net(&tmp_hna->A_gateway_addr, 
//				&tmp_net->A_network_addr, 
//				&tmp_net->A_netmask);
//		  tmp_net = tmp_net->next;
//		}
//	      
//	      tmp_hna = tmp_hna->next;
//	    }
//	}

//      ipc_send("}\n\n", strlen("}\n\n"));

      res = 1;
    }


  if(ipc_socket == -1)
    plugin_ipc_init();

  return res;
}

#if 0
#define MIN_LINK_QUALITY 0.01
static double calc_etx(double loss, double neigh_loss) 
{
  if (loss < MIN_LINK_QUALITY || neigh_loss < MIN_LINK_QUALITY)
    return 0.0;
  else
    return 1.0 / (loss * neigh_loss);
}
#endif

static void ipc_print_tc_link(struct tc_entry *entry, struct tc_edge_entry *dst_entry)
{
  char buf[256];
  int len;
  const char* main_adr;
  const char* adr;
//  double etx = calc_etx( dst_entry->link_quality, dst_entry->inverse_link_quality );

  main_adr = olsr_ip_to_string(&entry->addr);
  adr = olsr_ip_to_string(&dst_entry->T_dest_addr);
  len = sprintf( buf, "add link %s %s\n", main_adr, adr );
  ipc_send(buf, len);
}

#if 0
static void
ipc_print_net(union olsr_ip_addr *gw, union olsr_ip_addr *net, union hna_netmask *mask)
{
  const char *adr;

  adr = olsr_ip_to_string(gw);
  ipc_send("\"", 1);
  ipc_send(adr, strlen(adr));
  ipc_send("\" -> \"", strlen("\" -> \""));
  adr = olsr_ip_to_string(net);
  ipc_send(adr, strlen(adr));
  ipc_send("/", 1);
  adr = olsr_netmask_to_string(mask);
  ipc_send(adr, strlen(adr));
  ipc_send("\"[label=\"HNA\"];\n", strlen("\"[label=\"HNA\"];\n"));
  ipc_send("\"", 1);
  adr = olsr_ip_to_string(net);
  ipc_send(adr, strlen(adr));
  ipc_send("/", 1);
  adr = olsr_netmask_to_string(mask);
  ipc_send(adr, strlen(adr));
  ipc_send("\"", 1);
  ipc_send("[shape=diamond];\n", strlen("[shape=diamond];\n"));
}
#endif


static int ipc_send(const char *data, int size)
{
  if(ipc_connection == -1)
    return 0;

#if defined __FreeBSD__ || defined __MacOSX__
#define FLAG 0
#else
#define FLAG MSG_NOSIGNAL
#endif
  if (send(ipc_connection, data, size, FLAG) < 0) 
    {
      olsr_printf(1, "(DOT DRAW)IPC connection lost!\n");
      close(ipc_connection);
      ipc_connection = -1;
      return -1;
    }

  return 1;
}

#if 0
static struct link_entry *olsr_neighbor_best_link(union olsr_ip_addr *main)
{
  struct link_entry *walker;
  double best = 0.0;
  double curr;
  struct link_entry *res = NULL;

  // loop through all links

  for (walker = link_set; walker != NULL; walker = walker->next)
  {
    // check whether it's a link to the requested neighbor and
    // whether the link's quality is better than what we have
    if(COMP_IP(main, &walker->neighbor->neighbor_main_addr))
    {
      curr = walker->loss_link_quality * walker->neigh_link_quality;

      if (curr >= best)
      {
        best = curr;
        res = walker;
      }
    }
  }

  return res;
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1