/***************************************
  $Revision: 1.12 $

  Wrapper for NRTM client

  Status: NOT REVUED, NOT TESTED

 Author(s):       Andrei Robachevsky

  ******************/ /******************
  Modification History:
        andrei (17/01/2000) Created.
  ******************/ /******************
  Copyright (c) 2000                              RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 ***************************************/
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include "ud.h"
#include "ud_int.h"
#include "constants.h"
#include "server.h"
#include "protocol_mirror.h"


/* Response time to swtching updates on and off */
#define TIMEOUT 60 


int get_current_serial()
{
 int fd;
 char buff[64];
 const char *filename;
 
 filename=CO_get_nrtm_cserialfile();
 fd=open(filename, O_RDONLY, (mode_t)0);
 if (fd < 0) { fprintf(stderr, "cannot open %s\n", filename); return(-1); }
 read(fd, buff, sizeof(buff));
 close(fd);
 return(atoi(buff));
}

int set_current_serial(int current_serial)
{
FILE *file;
const char *filename;

 filename=CO_get_nrtm_cserialfile();
 file=fopen(filename, "w+");
 if (file==NULL) { fprintf(stderr, "cannot open %s\n", filename); return(-1); }
 fprintf(file,"%d\n", current_serial);
 fflush(file);
 fclose(file);
 return(current_serial);
}


/*************************************
*
*
**************************************/

void UD_do_nrtm()
{
UD_stream_t ud_stream;
struct _nrtm *nrtm;
/*int dummy_allowed;*/
int delay;
int do_update=1;
int do_server;
const char *logfilename;
FILE *file;
int num_ok;
int upto_last;
int current_serial;

  nrtm=calloc(1, sizeof(struct _nrtm));
  if(nrtm==NULL) {
	  printf("Cannot allocate memory\n");
	  die;
  }	  
/* get mode of operation: protected/unprotected (dummy) */
  ud_stream.ud_mode=CO_get_update_mode();

  fprintf(stderr, "Mode of operation:\n");
  if(IS_DUMMY_ALLOWED(ud_stream.ud_mode))fprintf(stderr, "* dummy allowed\n"); 
   else fprintf(stderr, "* dummy not allowed\n");
  if(IS_UPDATE(ud_stream.ud_mode))fprintf(stderr, "* DBupdate\n");
   else fprintf(stderr, "* NRTM\n");
  if(IS_STANDALONE(ud_stream.ud_mode))fprintf(stderr, "* running standalone\n");
   else fprintf(stderr, "* running as a server\n");
  
/* get mirror server */
  nrtm->server=CO_get_nrtm_host();
  
/* get mirror port */
  nrtm->port = SK_atoport(CO_get_nrtm_port(), "tcp");
  printf("XXX nrtm_port=%d\n", nrtm->port);
  if(nrtm->port == -1) {
    printf("Invalid service/port: %d\n", nrtm->port);
    return;
  }
              
/* get mirror version */
  nrtm->version=CO_get_nrtm_version();
  
/* get error log facility */
  logfilename=CO_get_nrtm_logfile();
      
/* Connect to the database */
  fprintf(stderr, "D: Making SQL connection to %s@%s ...", CO_get_database(), CO_get_host());

  ud_stream.db_connection=SQ_get_connection2();
   
  if(! ud_stream.db_connection) {
   fprintf(stderr, "D: ERROR: no SQL connection\n");
    return;
  }
  	
  fprintf(stderr, "OK\n");

      
  
/*  ud_stream.dummy_allowed=dummy_allowed;*/
  ud_stream.num_skip=0;
  ud_stream.load_pass=0;
  ud_stream.nrtm=nrtm;
  
  upto_last=0; /* let's start gradually if the backlog is > SBUNCH (1000) serials*/

/*+++ main cycle +++*/

 do {
  do_update=CO_get_do_update();
  if(do_update) {
 

  /* get current serial */
   current_serial=get_current_serial();
   nrtm->current_serial=PM_get_current_serial(ud_stream.db_connection);
   
   if(nrtm->current_serial == -1) {
     printf("Error obtaining current serial: %ld(%d)\n", nrtm->current_serial, current_serial);
     return;
   }

   fprintf(stderr, "current_serial:\t%ld(%d)\n", nrtm->current_serial, current_serial);
   fprintf(stderr, "conecting to server...\n");
      
   
    file = get_NRTM_stream(nrtm, upto_last);
    fprintf(stderr, "OK\n");
    printf("OK\n");


    if (file==NULL) { 
     fprintf(stderr, "Cannot open data stream. Trying...\n");
     sleep(100);
     continue;
    }  


   ud_stream.stream=file;
   ud_stream.log.logfile = fopen(logfilename, "a+");
   ud_stream.log.num_ok=0; 
   ud_stream.log.num_failed=0;
  

   fprintf(stderr, "starting processing stream\n");

   num_ok=UD_process_stream(&ud_stream);
  
  
     /*Check for errors */
   if(num_ok<0) {
	 fprintf(stderr, "processing stream failed\n");
	 do_server=0;
	 break;    
   }	   
   else fprintf(stderr, "processing stream finished\n"); 
   
  /* Now we can process serials in normal way (upto LAST)*/ 
   if(num_ok==0) upto_last=1;

/* This is for test purposes, just to know if we are out of sync */
   set_current_serial((int)(current_serial+num_ok));
   fprintf(ud_stream.log.logfile, "forwarded to serial:\t%ld(%d)\n", (nrtm->current_serial+num_ok), current_serial+num_ok);
   fclose(ud_stream.log.logfile);
   fprintf(stderr, "forwarded to serial:\t%ld(%d)\n", (nrtm->current_serial+num_ok), current_serial+num_ok);
   printf("Objects received: %d\n-----------\n", num_ok);

  /* get delay */
   delay=CO_get_nrtm_delay();
   SV_sleep(LOCK_SHTDOWN, delay);
  }
 else SV_sleep(LOCK_SHTDOWN, TIMEOUT); 

  do_server=CO_get_do_server();
  
 } while(do_server);  /* main cycle */
   
 if(nrtm) free(nrtm);
 /* That's all. Close connection to the DB */ 
 SQ_close_connection(ud_stream.db_connection);
 fprintf(stderr, "NRTM stopped\n");  

} /* UD_do_nrtm() */




/*************************************
*
*
**************************************/
  
void UD_do_updates(void *arg)
{
int listening_socket = (int)arg;
int connected_socket;
UD_stream_t ud_stream;
int do_update=1;
int do_server;
const char *logfilename;
FILE *file, *file_ack;
int num_ok;


/* get mode of operation: protected/unprotected (dummy) */
  ud_stream.ud_mode=CO_get_update_mode();

  fprintf(stderr, "Mode of operation:\n");
  if(IS_DUMMY_ALLOWED(ud_stream.ud_mode))fprintf(stderr, "* dummy allowed\n"); 
   else fprintf(stderr, "* dummy not allowed\n");
  if(IS_UPDATE(ud_stream.ud_mode))fprintf(stderr, "* DBupdate\n");
   else fprintf(stderr, "* NRTM\n");
  if(IS_STANDALONE(ud_stream.ud_mode))fprintf(stderr, "* running standalone\n");
   else fprintf(stderr, "* running as a server\n");

  


/* get error log facility */
  logfilename=CO_get_nrtm_logfile();

/* Connect to the database */
  fprintf(stderr, "D: Making SQL connection to %s@%s ...", CO_get_database(), CO_get_host());

  ud_stream.db_connection=SQ_get_connection2();
   
  if(! ud_stream.db_connection) {
   fprintf(stderr, "D: ERROR: no SQL connection\n");
    return;
  }
  	
  fprintf(stderr, "OK\n");

  ud_stream.num_skip=0;
  ud_stream.load_pass=0;
  ud_stream.nrtm=NULL;
  
/*+++ main cycle +++*/

do { /* be alive while do_server is 1. do_server is turned off by SIGINT */
 
/* accept connection */
   connected_socket = SK_accept_connection(listening_socket);
   if(connected_socket==-1) break;
   
   file=fdopen(connected_socket, "r");
   file_ack=fdopen(connected_socket, "w");

 do_update=CO_get_do_update();
 if(do_update) {
 
   fprintf(stderr, "Connection accepted...\n");
   
  if ((file==NULL) || (file_ack==NULL)) {
    fprintf(stderr, "Cannot open data stream. Closing connction\n");
    return; 
  }
  fprintf(stderr, "Connection accepted...\n");

  ud_stream.stream=file;
  ud_stream.log.logfile = fopen(logfilename, "a+");
  ud_stream.log.num_ok=0; 
  ud_stream.log.num_failed=0;
  

  fprintf(stderr, "starting processing object\n");

  num_ok=UD_process_stream(&ud_stream);
  
  fprintf(stderr, "processing object finished\n");  

  if(num_ok==1) {
   fprintf(file_ack, "%%ERROR 0\n");
   fprintf(stderr, "%%ERROR 0\n");
  } 
   else {
      num_ok=(-1)*num_ok;
      fprintf(file_ack, "%%ERROR %d\n",num_ok);
      fprintf(stderr, "%%ERROR %d\n",num_ok);
      fprintf(file_ack, "Transaction had the following problems:\n");
      if(num_ok & ERROR_U_MEM) fprintf(file_ack, "Memory allocation error\n");
/*      if(num_ok & ERROR_U_DBS) fprintf(file_ack, "Database (SQL) error\n");*/
/*      if(num_ok & ERROR_U_OBJ) fprintf(file_ack, "Object (RF) error\n");*/
/*      if(num_ok & ERROR_U_AUT) fprintf(file_ack, "Object authentication error\n");*/
      if(num_ok & ERROR_U_BADOP) fprintf(file_ack, "Bad operation\n");
      if(num_ok & ERROR_U_COP) fprintf(file_ack, "Conflicting operation\n");
      if(num_ok & ERROR_U_NSUP) fprintf(file_ack, "Object of this type is not supported\n");
      if(num_ok & ERROR_U_BUG) fprintf(file_ack, "Software bug - report to <ripe-dbm@ripe.net>\n");
   }
   if(ud_stream.error_script)fprintf(file_ack, "%s\n", ud_stream.error_script);
   
   if(ud_stream.error_script) free(ud_stream.error_script);
   fclose(file_ack);   
   fclose(file);
   fclose(ud_stream.log.logfile);
 
 }  /* if do_update*/
else { /* Otherwise print a message*/
 fprintf(file_ack, "%%ERROR 1000\n%%Updates are suspended\n");
 fclose(file_ack);
 fclose(file);
}
  
  do_server=CO_get_do_server();  

} while (do_server);  /* main cycle */

 /* That's all. Close connection to the DB */ 
 SQ_close_connection(ud_stream.db_connection);

 fprintf(stderr, "server stopped\n");  

} /* UD_do_update() */




