/* $Id: harmony.c,v 1.4 2004/05/10 15:38:03 aleigh Exp $ */

/*
 * Copyright (c) 1994, 2001 Alex Leigh, All Rights Reserved
 *
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 *
 * Use, disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */

#include "harmony.h"
#include "hproto.h"

#define MCAST_GROUP "225.0.0.37"

static int harGbcast_socket;
static int harGcid;
static unsigned int harGhostid;

static int harFhandle_pkt(const char *data, size_t data_len);
static void harFlisten_thread(void);
static uint32_t harFframe_crc(const harTframe_hdr *hdr);

#define MSGBUFSIZE 1024

static uint32_t harFframe_crc(const harTframe_hdr *hdr) {
  return htonl(crcFcrc32((const unsigned char *)hdr,sizeof(uint32_t)*4));
}

static void harFlisten_thread(void) 
{
    struct sockaddr_in addr;
    char msgbuf[MSGBUFSIZE];
    int nbytes,addrlen;

    logFmsg(3,"Started.");

    while (1) {
        addrlen=sizeof(addr);
        
        if ((nbytes=recvfrom(harGbcast_socket,msgbuf,MSGBUFSIZE,0,
                             NULL,0)) < 0) {
            logFmsg(2,"recvfrom() failed. [e: %d]", errno);
        }
        harFhandle_pkt(msgbuf,nbytes);
    }
}

static int harFhandle_pkt(const char *data, size_t data_len) {
  harTframe_hdr *frame;

  frame=(harTframe_hdr *)data;

  if(data_len<sizeof(harTframe_hdr)) {
    logFmsg(3,"discarding runt packet");
    return 0;
  }

  if(harFframe_crc(frame)!=frame->frame_crc) {
    logFmsg(3,"CRC does not match; discarding corrupt packet.");
    return 0;
  }

  logFmsg(3,"rcv'd packet %u -> %u", frame->from_addr, frame->rcpt_addr);

  if(frame->from_addr==harGhostid) {
    logFmsg(3,"discarding echo");
    return 0;
  }

  if(frame->rcpt_addr!=harGhostid && frame->rcpt_addr!=HARMONY_BROADCAST_ADDR) {
    logFmsg(3,"discarding 3rd party unicast packet");
    return 0;
  }

  logFmsg(3,"Received unicast packet for me.");

  return 0;
}

static int harFadvertise(void) 
{
  harTframe_hdr pkt;
  ssize_t ret;

  pkt.from_addr=harGhostid;
  pkt.rcpt_addr=HARMONY_BROADCAST_ADDR;
  pkt.msg_type=1;
  pkt.data_len=0;
  pkt.frame_crc=harFframe_crc(&pkt);

  if(ret=netFudp_send(harGbcast_socket,&pkt,sizeof(pkt),
                      harGcid,utlFstr_to_ip(MCAST_GROUP))==-1) {
    logFmsg(3,"netFudp_send() returns error. [e: %d]", errno);
  } else {
    logFmsg(3,"cluster advertisement sent. %d", ret);
  }

  return 0;
}

int harFinit(void *p, lstTset *opt) 
{
    const char *constellation;
    pthread_t listen_thread;

    logFmsg(0, "mod/harmony: Clustering Engine");
    logFmsg(0, "Copyright (c) 2004, Alex Leigh");

    constellation=lstFset_get(opt,"constellation");

    if(constellation==NULL) {
        logFmsg(2,"Constellation not configured. Aborting.");
        exit(1);
    }

    harGcid=atoi(constellation);
    harGhostid=utlFgethostid();

    logFmsg(0,"Rigging cluster constellation for id %u.", harGcid);
    logFmsg(0,"Assumed cluster address %u.",harGhostid);

    harGbcast_socket=netFipv4_mcast_create(harGcid,MCAST_GROUP);
    
    if(harGbcast_socket<0) {
      logFmsg(2,"Unable to create network transport. [e: %d]", errno);
      exit(1);
    }

    logFmsg(0,"Transport created.");

    harFadvertise();

    pthread_create(&listen_thread,NULL,harFlisten_thread,NULL);

    return STATUS_OK;
}
