/*
 * $Id: journal.c,v 1.3 1999/03/10 19:14:48 labovit Exp $
 * originally Id: journal.c,v 1.13 1998/06/23 23:30:44 gerald Exp 
 */



#include <stdio.h>
#include <string.h>
#include "mrt.h"
#include "trace.h"
/*#include "interface.h"*/
#include <time.h>
#include <signal.h>
#include "config_file.h"
#include <sys/stat.h>
#include <fcntl.h>
#include "irrd.h"

extern trace_t *default_trace;
void make_journal_name (char * dbname, int journal_ext, char * journal_name);

/* journal_irr_update
 * Record what we're doing to the database in the <DB>.journal file
 * if mode == IRR_UPDATE then an add
*/
/* JW this routine can be speeded up.  We know the offset and length
   of the object, lets read it and write it with two sys calls
*/
void journal_write_serial (int mode, FILE *src_fd, int offset, irr_database_t *database) {
  char buffer[512];
  char *sadd = "ADD\n\n";
  char *sdelete = "DEL\n\n";
  /*
  char *snomode = "\n\n";
  */
  
  journal_log_serial_number (database);

  if (mode == IRR_UPDATE) 
    write (database->journal_fd, sadd, strlen (sadd));
  else if (mode == IRR_DELETE) 
    write (database->journal_fd, sdelete, strlen (sdelete));
  else {
    trace (ERROR, default_trace,"ERROR journal.c: journal_write_serial(): unrecognized mode (%d) db-(%s)\n", mode, database->name);
    return;
    /*
    write (database->journal_fd, snomode, strlen (snomode));
    */
  }

  fseek (src_fd, offset, SEEK_SET);
    
  while (fgets (buffer, sizeof (buffer)-1, src_fd) != NULL) {
    if (strlen (buffer) < 2) break;
    write (database->journal_fd, buffer, strlen (buffer));
  }
  strcpy (buffer, "\n");
  write (database->journal_fd, buffer, strlen (buffer));

  /* whew! The change is on disk, so we can save the new serial number */
  write_irr_serial (database); 
  
  return;
}


void journal_irr_update (irr_database_t *db, irr_object_t *object, 
                         int mode, u_long db_offset, int del_obj) {

  db->serial_number++;

  if (del_obj > 0)
    mark_deleted_irr_object (db, db_offset);

  /* until we have our syntax checker, just copy the serial as is */
  journal_write_serial (mode, object->fd, object->offset, db);
}

/* journal_log_serial_number
 * For crash recovery and when we act as a mirror server,
 * stamp the <DB>.journal file with the current serial 
 * number for the database
 */
void journal_log_serial_number (irr_database_t *database) {
  char buffer[512];
  sprintf (buffer, "%s SERIAL %ld\n", "%", database->serial_number);
  write (database->journal_fd, buffer, strlen (buffer));
  return;
}


/* if the journal file is too big, roll it over and create a <db>.JOURNAL.old
 * or something like that 
 */
void journal_maybe_rollover (irr_database_t *database) {
  struct stat buf;
  char file_old[MAXLINE], file_new[MAXLINE];

  fstat(database->journal_fd, &buf);
  if (buf.st_size > IRR_MAX_JOURNAL_SIZE) {

    trace (NORM, default_trace, "Rolling Journal file (> %d bytes) for %s\n",
	   IRR_MAX_JOURNAL_SIZE, database->name);
    
    make_journal_name (database->name, JOURNAL_NEW, file_new);
    make_journal_name (database->name, JOURNAL_OLD, file_old);

    close (database->journal_fd);
    rename(file_new, file_old);

    if ((database->journal_fd = open (file_new, O_RDWR | O_CREAT, 0774)) < 0) 
      trace (NORM, default_trace, "**** ERROR **** Could not open %s (%s)!\n", 
	     file_new, strerror (errno));
  }
  return;
}

/*
 * return 1 if *.JOURNAL file exists and a valid serial # header is found
 * otherwise return 0
 * return value of first good serial found if possible
 */
int find_oldest_serial (char *dbname, int journal_ext, u_long *oldestserial) {
  char file[MAXLINE], buf[MAXLINE];
  FILE *fd;

  make_journal_name (dbname, journal_ext, file);

  if ((fd = fopen (file, "r")) != NULL) {
    while (fgets (buf, sizeof (buf) - 1, fd) != NULL) { 
      if (sscanf (buf, "%% SERIAL %s", file) == 1) {
	fclose (fd);
        if (convert_to_lu (file, oldestserial) < 0)
          return (0);
	return (1);
      }
    }
  }

  if (fd != NULL)
    fclose (fd);

  return (0);
}


/*
 * return 1 if *.CURRENTSERIAL is read without error
 * else return -1
 */
int get_current_serial (char *dbname, u_long *currserial) {
  char tmp[MAXLINE], file[MAXLINE];
  int ret_val = -1;
  FILE *fd;

  strcpy (tmp, dbname);
  convert_toupper(tmp);

  sprintf (file, "%s/%s.CURRENTSERIAL", IRR.database_dir, tmp);

  if ((fd = fopen (file, "r")) != NULL) {
    memset (tmp, 0, sizeof (tmp));
    if (fgets (tmp, sizeof (tmp) - 1, fd) != NULL && 
        convert_to_lu (tmp, currserial) > 0)
      ret_val = 1;
    fclose (fd);
  }

  return (ret_val);
}

void make_journal_name (char * dbname, int journal_ext, char * journal_name) {
  
 if (journal_ext == JOURNAL_NEW)
   sprintf (journal_name, "%s/%s.%s", IRR.database_dir, dbname, SJOURNAL_NEW); 
 else
   sprintf (journal_name, "%s/%s.%s", IRR.database_dir, dbname, SJOURNAL_OLD); 
}
