/* Massage raw records from the gateways into something suitable for RS1 
   (or some other data manipulation program */

/*
 *------------------------------------------------------------------
 *
 * $Source: /mit/cgw/statd/src/RCS/dump_stats.c,v $
 * $Revision: 1.8 $
 * $Date: 89/11/06 23:32:46 $
 * $State: Exp $
 * $Author: jon $
 * $Locker: jon $
 *
 * $Log:	dump_stats.c,v $
 * Revision 1.8  89/11/06  23:32:46  jon
 * get the byte order correct when running on rt's and sun's
 * 
 *
 * Revision 1.7  89/11/06  23:24:50  jon
 * support old statd which truncated incoming packets at 576 bytes.
 * 
 * Revision 1.6  89/03/12  00:02:00  jon
 * I'd like to see my error messages, thank you
 * 
 * Revision 1.5  87/08/10  23:34:04  jon
 * Implements -from/-to (not quite right but functional), adds -raw (which
 * prints in ascii but doesn't do the subtraction), and fine tunes -binary.
 * 
 * Revision 1.4  87/08/07  00:26:54  jon
 * Now uses cgw statpkt structures instead of gw_status.  Include
 * ./time.h and uses new_tm instead of tm from time structures.
 * 
 * Revision 1.3  87/08/02  23:45:30  jon
 * Adds -from/-to (but ignores them), adds -binary/-ascii (and implements them)
 * 
 * Revision 1.2  87/07/06  23:14:29  jon
 * Adds lots of stuff.  This is now a useful program.
 * 
 * Revision 1.1  87/06/04  17:26:59  jon
 * Initial revision
 * 
 * 
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_dump_stats_c = "$Header: /mit/cgw/statd/src/RCS/dump_stats.c,v 1.8 89/11/06 23:32:46 jon Exp Locker: jon $";
#endif	lint

#include "./time.h"

#include <stdio.h>
#include <sys/file.h>
#include "statd.h"
#include "types.h"
#include "status.h"

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

static statpkt *status_cur = NULL;
static statpkt *status_prev = NULL;
static statpkt *status_reboot = NULL;
static statpkt *status_temp = NULL;

static char *whoami;

#if defined(ibm032) || defined(sun)
unsigned long swap(x)
     unsigned long x;
{
  unsigned char *foo, tmp;

  foo = (unsigned char *) &x;
  tmp = foo[0];
  foo[0] = foo[3];
  foo[3] = tmp;
  tmp = foo[1];
  foo[1] = foo[2];
  foo[2] = tmp;

  return (x);
}
#else
#define swap(x) (x)
#endif

main (argc, argv)
     int argc;
     char **argv;
{
  int i, ifd;
  FILE *out;
  char *infile;
  int binary, raw, every_n, from_given, to_given;
  struct new_tm from_tm, to_tm;

  whoami = argv[0];
  from_given = FALSE;
  to_given = FALSE;
  binary = FALSE;
  raw = FALSE;
  every_n = 1;
  out = stdout;

  if (argc == 1) usage();

  for (i = 1; i < argc; i++) {
    if (argv[i][0] != '-') {
      infile = argv[i];
      ifd = open (infile, O_RDONLY, 0);
      if (ifd < 0) {
	fprintf (stderr, "%s: While opening %s ", whoami, infile);
	perror (NULL);
	exit(1);
      }
      fprintf (stderr, "%s: Dumping %s\n", whoami, infile);
      fflush (stderr);
      dump_stats (ifd, out, every_n, binary, raw, from_given ? &from_tm : NULL,
		  to_given ? &to_tm : NULL);
      close (ifd);
      if (status_cur) free (status_cur);
      if (status_prev) free (status_prev);
      if (status_reboot) free (status_reboot);
      if (status_temp) free (status_temp);
      continue;
    }
    if (strcmp (argv[i], "-every") == 0) {
      if (++i > argc) usage ();
      every_n = atoi (argv[i]);
      continue;
    }
    if (strcmp (argv[i], "-from") == 0) {
      if (++i > argc) usage ();
      if (partime (argv[i], &from_tm) == 0) {
	fprintf (stderr, "%s: Invalid -from datetime %s.\n", whoami, argv[i]);
	exit (1);
      }
      from_given = TRUE;
      continue;
    }
    if (strcmp (argv[i], "-to") == 0) {
      if (++i > argc) usage ();
      if (partime (argv[i], &to_tm) == 0) {
	fprintf (stderr, "%s: Invalid -to datetime %s.\n", whoami, argv[i]);
	exit (1);
      }
      to_given = TRUE;
      continue;
    }
    if (strcmp (argv[i], "-file") == 0)  {
      if (++i > argc) usage ();
      if (out && (out != stdin)) fclose (out);
      if (argv[i][0] == '-') out = stdout;
      else {
	out = fopen (argv[i], "w+");
	if (out == NULL) {
	  fprintf (stderr, "While opening %s ", whoami, argv[i]);
	  perror (NULL);
	  exit(1);
	}
	continue;
      }
    }
    /* no subtraction, just raw numbers */
    if (strcmp (argv[i], "-raw") == 0) {
      raw = TRUE;
      continue;
    }
    /* no nicely formatted printf's just write out what you read in */
    if (strcmp (argv[i], "-binary") == 0) {
      binary = TRUE;
      continue;
    }
    if (strcmp (argv[i], "-ascii") == 0) {
      binary = FALSE;
      continue;
    }
	
    fprintf (stderr, "%s: Unknown arg %s\n.", whoami, argv[i]);
    usage ();
  }
}

usage ()
  {
    fprintf (stderr, 
	     "usage: %s [-every n] [-from datetime] [-to datetime] statfile ... {ctrl args} statfilen\n", whoami);
    exit (1);
  }

int tm_before (x_tm, y_tm)
     struct tm *x_tm;
     struct new_tm *y_tm;
{
  if (((x_tm->tm_year < y_tm->tm_year) && (y_tm->tm_year != TMNULL)) ||
      ((x_tm->tm_mon < y_tm->tm_mon) && (y_tm->tm_mon != TMNULL)) ||
       ((x_tm->tm_mday < y_tm->tm_mday) && (y_tm->tm_mday != TMNULL)) ||
      ((x_tm->tm_hour < y_tm->tm_hour) && (y_tm->tm_hour != TMNULL)) ||
      ((x_tm->tm_min  < y_tm->tm_min)  && (y_tm->tm_min != TMNULL))||
      ((x_tm->tm_sec  < y_tm->tm_sec)) && (y_tm->tm_sec != TMNULL))
    return (TRUE);
  else
    return (FALSE);
}

int tm_after (x_tm, y_tm)
     struct tm *x_tm;
     struct new_tm *y_tm;
{
   if (((x_tm->tm_year > y_tm->tm_year) && (y_tm->tm_year != TMNULL)) ||
       ((x_tm->tm_mon > y_tm->tm_mon) && (y_tm->tm_mon != TMNULL)) ||
       ((x_tm->tm_mday > y_tm->tm_mday) && (y_tm->tm_mday != TMNULL)) ||
       ((x_tm->tm_hour > y_tm->tm_hour) && (y_tm->tm_hour != TMNULL)) ||
       ((x_tm->tm_min  > y_tm->tm_min)  && (y_tm->tm_min != TMNULL))||
       ((x_tm->tm_sec  > y_tm->tm_sec)) && (y_tm->tm_sec != TMNULL))
    return (TRUE);
#ifdef notdef
   if (((x_tm->tm_year == y_tm->tm_year) || (y_tm->tm_year == TMNULL)) &&
       ((x_tm->tm_mon == y_tm->tm_mon) || (y_tm->tm_mon == TMNULL)) &&
       ((x_tm->tm_mday == y_tm->tm_mday) || (y_tm->tm_mday == TMNULL)) &&
       ((x_tm->tm_hour == y_tm->tm_hour) || (y_tm->tm_hour == TMNULL)) &&
       ((x_tm->tm_min  == y_tm->tm_min)  || (y_tm->tm_min == TMNULL)) &&
       ((x_tm->tm_sec  == y_tm->tm_sec)) || (y_tm->tm_sec == TMNULL)) 
    return (TRUE);
#endif
  else 
    return (FALSE);
}
  
dump_stats (in, out, every_n, binary, raw, from_tm, to_tm)
     int in, every_n, binary, raw;
     FILE *out;
     struct new_tm *from_tm, *to_tm;
{
  int n_read, n_records = 0, n_wrote;
  gw_status_header header;
  char gwname[STAT_NAME_LEN+1];
  netinfo *netip_cur, *netip_prev, *get_netinfo();
  char date[9];
  char time[6];
  long basetime;
  struct tm *a_tm;
  float numeric_time_abs, numeric_time_rel, numeric_date_rel;

  while (TRUE) {

    /* bump a generation ... */
    /* fool raw by realized the i - 0 = i */
    if (!raw) {
      status_temp = status_prev;
      status_prev = status_cur;
      status_cur = status_temp;
    }

    n_read = read (in, &header, sizeof (header));
    if (n_read == 0) break;
    if (n_read != sizeof (header)) {
      fprintf (stderr, "%s: Reading header record. ", whoami);
      perror (NULL);
      break;
    }

    if (strcmp (header.version, GW_STAT_HEADER_VERSION) != 0) {
      fprintf (stderr, "Bad header version (%s)\n", header.version);
      break;
    }

    if (strcmp(header.version, GW_STAT_HEADER_VERSION_1) == 0) {
      /* These are in VAX byte order */
      header.time_received.tv_sec = swap (header.time_received.tv_sec);
      header.time_received.tv_usec = swap (header.time_received.tv_usec);
      header.length_of_data = swap (header.length_of_data);
    }

    if ((header.length_of_data > 10000) || (header.length_of_data < 0)) {
      header.length_of_data = 576;  /* kludge, support too large records
				       read from gw_status_utils < r1.4 */
    }

    /* inside main loop because of length_of_data ... could read in one
       record, then rewind and start again, I suppose ... */

    if (status_cur == NULL) {
      /* these are big enough for the statpkt and the netinfo records */
      status_cur = (statpkt *) malloc (header.length_of_data);
      /* does malloc zero things? I'll play it safe ... */
      bzero (status_cur, header.length_of_data);
      status_prev = (statpkt *) malloc (header.length_of_data);
      bzero (status_prev, header.length_of_data);
      status_reboot = (statpkt *) malloc (header.length_of_data);
      bzero (status_reboot, header.length_of_data);
    }

    printf ("LENGTH = %d\n", header.length_of_data);
    n_read = read (in, status_cur, header.length_of_data);
    if (n_read != header.length_of_data) {
      fprintf (stderr, "%s: Reading status record. ", whoami);
      perror (NULL);
      break;
    }

    if (status_cur->version != STAT_VERSION) {
      fprintf (stderr, "Bad status packet version (%s)\n", status_cur->version);
      break;
    }

    if (status_cur->magic != STAT_MAGIC) {
      if (binary) {
	fprintf (stderr, "dump_stats: -binary will lose unless on a vax!\n.");
	exit(44);
      }
      status_cur->uptime = swap(status_cur->uptime);
      status_cur->free_mem = swap(status_cur->free_mem);
      status_cur->num_tsks = swap(status_cur->num_tsks);
      status_cur->max_tsks = swap(status_cur->max_tsks);
      status_cur->num_timers = swap(status_cur->num_timers);
      status_cur->max_timers = swap(status_cur->max_timers);
      status_cur->load1 = swap(status_cur->load1);
      status_cur->load5 = swap(status_cur->load5);
      status_cur->load15 = swap(status_cur->load15);
      status_cur->nnets = swap(status_cur->nnets);
      status_cur->nprots = swap(status_cur->nprots);
    }	

    if (n_records == 0) {
      basetime = header.time_received.tv_sec;
      strncpy (gwname, status_cur->name, STAT_NAME_LEN);
      gwname[STAT_NAME_LEN] = NULL;
    }

    if ((n_records == 0) && !(raw || binary)) {
      n_records++;
      continue;
    }

    /* ***** HAVE TO DO A BETTER JOB AT every_n and restarts!!!! *** */
    a_tm = localtime (&(header.time_received.tv_sec));

    if (from_tm)
      if (tm_before (a_tm, from_tm)) continue;
    if (to_tm)
      if (tm_after (a_tm, to_tm)) break;

    if (n_records++ % every_n != 0) continue;

    if (binary) {
      n_wrote = write (fileno (out), &header, sizeof (header));
      if (n_wrote != sizeof (header)) {
	fprintf (stderr, "%s: Writing header record. ", whoami);
	perror (NULL);
	break;
      }
      n_wrote = write (fileno (out), status_cur, header.length_of_data);
      if (n_wrote != header.length_of_data) {
	fprintf (stderr, "%s: Writing status record. ", whoami);
	perror (NULL);
	break;
      }
      continue;
    }

    if ((netip_cur = get_netinfo (status_cur, 0)) == NULL) {
      fprintf (stderr, "Couldn't find netinfo pointer for net %d\n", 0);
      break;
    }

    if ((netip_prev = get_netinfo (status_prev, 0)) == NULL) {
      fprintf (stderr, "Couldn't find netinfo pointer for net %d\n", 0);
      break;
    }

    sprintf (date, "%02d/%02d/%02d\0", ++(a_tm->tm_mon), a_tm->tm_mday, a_tm->tm_year);
    sprintf (time, "%02d:%02d\0", a_tm->tm_hour, a_tm->tm_min);
    numeric_time_abs = a_tm->tm_hour + (a_tm->tm_min / 60.0);
    numeric_time_rel = (header.time_received.tv_sec - basetime) / 3600.0;
    numeric_date_rel = numeric_time_rel / 24.0;
    
    fprintf (out, "%s %s %s %.1f %.1f %.2f %.2f ",
	    gwname, date, time, numeric_time_abs, numeric_time_rel, 
	    numeric_date_rel,
	    status_cur->uptime / (3600.0 * 24));
    
    if (status_cur->uptime < status_prev->uptime) {
      fprintf (stderr, "%s: Gateway %s reloaded/restarted at %s %s\n",
	       whoami, gwname, date, time);
      bzero (status_prev, header.length_of_data);
    }
    
    fprintf (out, "%u %u %u %u %u %u %u %u %u\n",
	    swap(netip_cur->pkti) - swap(netip_prev->pkti),
	    swap(netip_cur->pkto) - swap(netip_prev->pkto),
	    swap(netip_cur->byti) - swap(netip_prev->byti),
	    swap(netip_cur->byto) - swap(netip_prev->byto),
	    swap(netip_cur->ioin) - swap(netip_prev->ioin),
	    swap(netip_cur->ioout) - swap(netip_prev->ioout),
	    swap(netip_cur->ovfin) - swap(netip_prev->ovfin),
	    swap(netip_cur->ovfout) - swap(netip_prev->ovfout),
	    swap(netip_cur->disc) - swap(netip_prev->disc));
  }

  free (status_cur);
  free (status_prev);
  free (status_temp);
}


netinfo *get_netinfo (status, index)
     statpkt *status;
     int index;
{
  int i;
  netinfo *netip;
  if (index > status->nnets) return (NULL);
  
  for (i = 0, netip = (netinfo *) (status + 1); i < index; i++) 
      netip = (netinfo *) ((byte *) (netip + 1) + netip->devlen);
  return (netip);
}

