File:  [MW Coherent from dump] / coherent / g / usr / lib / uucp / tay104 / contrib / uurate.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:35 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * @(#)uurate.c 1.2 - Thu Sep  3 18:32:46 1992
 *
 * This program digests log and stats files in the "Taylor" format
 * and outputs various statistical data to standard out.
 *
 * Author:
 *	Bob Denny ([email protected])
 *	Fri Feb  7 13:38:36 1992
 *
 * Original author:
 * 	Mark Pizzolato   [email protected]
 *
 * Edits:
 *	Bob Denny - Fri Feb  7 15:04:54 1992
 *	Heavy rework for Taylor UUCP. This was the (very old) uurate from
 *	DECUS UUCP, which had a single logfile for activity and stats.
 *      Personally, I would have done things differently, with tables
 *      and case statements, but in the interest of time, I preserved
 *      Mark Pizzolato's techniques and style.
 *
 *      Bob Denny - Sun Aug 30 14:18:50 1992
 *      Changes to report format suggested by Francois Pinard and others.
 *      Add summary report, format from uutraf.pl (perl script), again
 *      thanks to Francois. Integrate and checkout with 1.03 of Taylor UUCP.
 */

char version[] = "@(#) Taylor UUCP Log File Summary Filter, Version 1.2";

#include <ctype.h>		/* Character Classification	*/
#include <string.h>
#include <math.h>

#include "uucp.h"


#define _DEBUG_ 0
	
/*
 * Direction of Calling and Data Transmission
 */
#define IN	0		/* Inbound		*/
#define OUT	1		/* Outbound 		*/

/*
 * Data structures used to collect information
 */
struct File_Stats
    {
    int files;			/* Files Transferred	*/
    unsigned long bytes;	/* Data Size Transferred*/
    double time;		/* Transmission Time	*/
    };

struct Phone_Call
    {
    int calls;			/* Call Count		*/
    int succs;			/* Successful calls     */
    double connect_time;	/* Connect Time Spent	*/
    struct File_Stats flow[2];	/* Rcvd & Sent Data  	*/
    };

struct Execution_Command
    {
    struct Execution_Command *next;
    char Commandname[64];
    int count;
    };

struct Host_entry
    {
    struct Host_entry *next;
    char Hostname[32];
    struct Execution_Command *cmds;	/* Local Activities */
    struct Phone_Call call[2];		/* In & Out Activities */
    };

/*
 * Stuff for getopt()
 */
extern int optind;		/* GETOPT : Option Index		*/
extern char *optarg;		/* GETOPT : Option Value		*/
extern void *calloc();

static void fmtime();
static void fmbytes();

/*
 * Default files to read. Taken from Taylor compile-time configuration.
 * Must look like an argvec, hence the dummy argv[0].
 */
static char *(def_logs[3]) = { "", LOGFILE, STATFILE };

/*
 * Misc. strings for reports
 */
static char *(file_hdr[2]) = { "\nReceived file statistics:\n",
				 "\nSent file statistics\n" };
  
/*
 * BEGIN EXECUTION
 */
main(argc, argv)
int argc;
char *argv[];
{
  char c;
  char *p, *s;
  struct Host_entry *hosts = NULL;
  struct Host_entry *cur = NULL;
  struct Host_entry *e;
  struct Execution_Command *cmd;
  struct Execution_Command *ec;
  char Hostname[64];
  FILE *Log = NULL;
  char logline[1024];
  char *logmsg;
  int sent;
  int called;
  int show_files = 0;		/* I prefer boolean, but... */
  int show_calls = 0;
  int show_commands = 0;
  int show_efficiency = 0;
  int show_summary = 0;
  int have_files = 0;
  int have_calls = 0;
  int have_commands = 0;  
  int use_stdin = 0;
  Hostname[0] = '\0';

  /*
   * I wish the compiler had the #error directive!
   */
#if !HAVE_TAYLOR_LOGGING
  fprintf(stderr, "uurate cannot be used with your configuration of\n");
  fprintf(stderr, "Taylor UUCP. To use uurate you must be using the\n");
  fprintf(stderr, "TAYLOR_LOGGING configuration.\n");
  exit(1);
#endif

  /*
   * Process the command line arguments
   */
  while((c = getopt(argc, argv, "h:cfexai")) != EOF)
    {
      switch(c)
	{
	case 'h':
	  strcpy(Hostname, optarg);
	  break;
	case 'c':
	  show_calls = 1;
	  break;
	case 'f':
	  show_files = 1;
	  break;
	case 'x':
	  show_commands = 1;
	  break;
        case 'e':
	  show_efficiency = 1;
	  break;
	case 'a':
	  show_calls = show_files = show_commands = show_efficiency = 1;
	  break;
	case 'i':
	  use_stdin = 1;
	  break;
        default :
	  goto usage;
	}
    }

  /*
   * If no report switches given, show summary report.
   */
  if (show_calls == 0 && show_files == 0
      && show_efficiency == 0 && show_commands == 0)
    show_summary = 1;
  
  /*
   * Adjust argv and argc to account for the args processed above.
   */
  argc -= (optind - 1);
  argv += (optind - 1);

  /*
   * If further args present, Assume rest are logfiles for us to process,
   * otherwise, take input from Log and Stat files provided in the
   * compilation environment of Taylor UUCP. If -i was given, Log already
   * points to stdin and no file args are accepted.
   */
  if(argc == 1)			/* No file arguments */
    {
      if (use_stdin)		/* If -i, read from stdin */
	{
	  argc = 2;
	  Log = stdin;
	}
      else			/* Read from current logs */
	{
          argc = 3;	        /* Bash argvec to default log/stat files */
          argv = &def_logs[0];
        }
    }
  else if (use_stdin)		/* File args with -i is an error */
    {
      fprintf(stderr, "uurate (error): file args given with '-i'\n");
      goto usage;
    }

#if _DEBUG_
  printf("\n");
#endif

  /*
   * MAIN LOGFILE PROCESSING LOOP
   */
  while (argc > 1)
    {

      if (!use_stdin && (Log = fopen(argv[1], "r")) == NULL)
	{
	  perror(argv[1]);
	  return;
	}

#if _DEBUG_
      printf("Reading %s...\n", (use_stdin ? "stdin" : argv[1]));
#endif
      
      /*
       * Read each line of the logfile and collect information
       */
      while (fgets(logline, sizeof(logline), Log))
	{
	  /*
	   * The host name of the other end of the connection is
	   * always the second field of the log line, whether we
	   * are reading a Log file or a Stats file. Set 'p' to
	   * point to the second field, null-terminated. Skip
	   * the line if something is funny.
	   */
	  if (NULL == (p = strchr(logline, ' ')))
	    continue;
	  ++p;
	  if (NULL != (s = strchr(p, ' ')))
	    *s = '\0';
	  for (s = p; *s; ++s)
	    if (isupper(*s))
	      *s = tolower(*s);
	  /*
	   * Skip this line if we got -h <host> and
	   * this line does not contain that host name.
	   */
	  if (Hostname[0] != '\0')
	    if (0 != strcmp(p, Hostname))
	      continue;
	  /*
	   * We are within a call block now. If this line is a file
	   * transfer record, determine the direction. If not then
	   * skip the line if it is not interesting.
	   */
	  if ((s = strchr(++s, ')')) == NULL)
	    continue;
	  logmsg = s + 2;		/* Message is 2 characters after ')' */
	  if (0 == strncmp(logmsg, "sent", 4))
	    sent = OUT;
	  else
	    if (0 == strncmp(logmsg, "received", 8))
	      sent = IN;
	    else
	      if ((0 != strncmp(logmsg, "Call complete", 13)) &&
		  (0 != strncmp(logmsg, "Calling system", 14)) &&
		  (0 != strncmp(logmsg, "Incoming call", 13)) &&
		  (0 != strncmp(logmsg, "Executing", 9)))
		continue;
	  /*
	   * Find the Host_entry for this host, or create a new
	   * one and link it on to the list.
	   */
	  if ((cur == NULL) || (0 != strcmp(p, cur->Hostname)))
	    {
	      for (cur = hosts; cur != NULL ; cur = cur->next)
		if (0 == strcmp(cur->Hostname, p))
		  break;
	      if (cur == NULL)
		{
		  cur = (struct Host_entry *)calloc(1, sizeof(*hosts));
		  strcpy(cur->Hostname, p);
		  if (hosts == NULL)
		    hosts = cur;
		  else
		    {
		      for (e = hosts; e->next != NULL; e = e->next);
		      e->next = cur;
		    }
		}
	    }
	  /*
	   * OK, if this is a uuxqt record, find the Execution_Command
	   * structure for the command being executed, or create a new
	   * one. Then count an execution of this command.
	   */
	  if (0 == strncmp(logmsg, "Executing", 9))
	    {
	      if (NULL == (p = strchr(logmsg, '(')))
		continue;
	      if ((s = strpbrk(++p, " )")) == NULL)
		continue;
	      *s = '\0';
	      for (cmd = cur->cmds; cmd != NULL; cmd = cmd->next)
		if (0 == strcmp(cmd->Commandname, p))
		  break;
	      if (cmd == NULL)
		{
		  cmd = (struct Execution_Command *)calloc(1, sizeof(*cmd));
		  strcpy(cmd->Commandname, p);
		  if (cur->cmds == NULL)
		    cur->cmds = cmd;
		  else
		    {
		      for (ec = cur->cmds; ec->next != NULL; ec = ec->next);
		      ec->next = cmd;
		    }
		}
	      ++cmd->count;
	      have_commands = 1;
	      continue;
	    }
	  /*
	   * Count start of outgoing call.
	   */
	  if (0 == strncmp(logmsg, "Calling system", 14))
	    {
	      called = OUT;
	      cur->call[called].calls += 1;
	      have_calls = 1;
	      continue;
	    }
	  /*
	   * Count start of incoming call.
	   */
	  if (0 == strncmp(logmsg, "Incoming call", 13))
	    {
	      called = IN;
	      cur->call[called].calls += 1;
	      have_calls = 1;
	      continue;
	    }
	  /*
	   * Handle end of call. Pick up the connect time.
	   */
	  if (0 == strncmp(logmsg, "Call complete", 13))
	    {
	      cur->call[called].succs += 1;
	      if (NULL == (s = strchr(logmsg, '(')))
		continue;
	      cur->call[called].connect_time += atof(s+1);
	      continue;
	    }
	  /*
	   * If we reached here, this must have been a file transfer
	   * record. Count it in the field corresponding to the
	   * direction of the transfer. Count bytes transferred and
	   * the time to transfer as well.
	   */
	  have_files = 1;
	  cur->call[called].flow[sent].files += 1;
	  if (NULL == (s = strchr(logmsg, ' ')))
	    continue;
	  cur->call[called].flow[sent].bytes += atol(++s);
	  if (NULL == (s = strchr(s, ' ')))
	    continue;
	  if (NULL == (s = strpbrk(s, "0123456789")))
	    continue;
	  cur->call[called].flow[sent].time += atof(s);
	}
      argc -= 1;
      argv += 1;
      if(Log != stdin)
	fclose(Log);
    }
  
  /*
   *     ***********
   *     * REPORTS *
   *     ***********
   */

  /*
   * Truncate the Hostnames to 8 characters at most.
   */
  for (cur = hosts; cur != NULL; cur = cur->next)
    cur->Hostname[8] = '\0';

#if _DEBUG_
  printf("\n");
#endif

  /*
   * Summary report
   *
   * I know, this code could be tightened (rbd)...
   */
  if(show_summary)
    {
      char t1[32], t2[32], t3[32], t4[32], t5[32];
      long ib, ob, b, rf, sf;
      long t_ib=0, t_ob=0, t_b=0, t_rf=0, t_sf=0;
      double it, ot, ir, or;
      double t_it=0.0, t_ot=0.0;
      int nhosts = 0;

      printf("\n\
 Remote  ------- Bytes -------- --- Time ---- -- Avg CPS -- -- Files --\n");
      printf("\
  Host      Rcvd    Sent   Total   Rcvd   Sent   Rcvd   Sent  Rcvd  Sent\n");
      printf("\
-------- ------- ------- ------- ------ ------ ------ ------ ----- -----\n");
      for (cur = hosts; cur != NULL; cur = cur->next)
	{
	  ib = (cur->call[IN].flow[IN].bytes + 
		cur->call[OUT].flow[IN].bytes);
	  fmbytes(ib, t1);
	  t_ib += ib;

	  ob = (cur->call[IN].flow[OUT].bytes + 
		cur->call[OUT].flow[OUT].bytes);
	  fmbytes(ob, t2);
	  t_ob += ob;

	  b = ib + ob;
	  fmbytes(b, t3);
	  t_b += b;

	  it = cur->call[IN].flow[IN].time +
	    cur->call[OUT].flow[IN].time;
	  fmtime(it, t4);
	  t_it += it;

	  ot = cur->call[IN].flow[OUT].time +
	    cur->call[OUT].flow[OUT].time;
	  fmtime(ot, t5);
	  t_ot += ot;

	  rf = cur->call[IN].flow[IN].files +
	    cur->call[OUT].flow[IN].files;
	  t_rf += rf;

	  sf = cur->call[IN].flow[OUT].files +
	    cur->call[OUT].flow[OUT].files;
	  t_sf += sf;

	  ir = (it == 0.0) ? 0.0 : (ib / it);
	  or = (ot == 0.0) ? 0.0 : (ob / ot);

	  printf("%-8s %7s %7s %7s %6s %6s %6.1f %6.1f %5d %5d\n",
		 cur->Hostname,
		 t1, t2, t3, t4, t5, 
		 ir, or, rf, sf);
	}

      if(nhosts > 1)
	{
	  fmbytes(t_ib, t1);
	  fmbytes(t_ob, t2);
	  fmbytes(t_b, t3);
	  fmtime(t_it, t4);
	  fmtime(t_ot, t5);
	  ir = (t_it == 0.0) ? 0.0 : (t_ib / t_it);
	  or = (t_ot == 0.0) ? 0.0 : (t_ob / t_ot);

	  printf("\
-------- ------- ------- ------- ------ ------ ------ ------ ----- -----\n");
	  printf("\
Totals   %7s %7s %7s %6s %6s %6.1f %6.1f %5d %5d\n",
		 t1, t2, t3, t4, t5,
		 ir, or, t_rf, t_sf);
	}
    }

	  
  /*
   * Call statistics report
   */
  if(show_calls && have_calls)
    {
      char t1[32], t2[32];

      printf("\nCall statistics:\n");
      printf("\
     sysname   callto  failto    totime  callfm  failfm    fmtime\n");
      printf("\
     --------  ------  ------  --------  ------  ------  --------\n");
      for (cur = hosts; cur != NULL; cur = cur->next)
	{
	  fmtime(cur->call[OUT].connect_time, t1);
	  fmtime(cur->call[IN].connect_time, t2),
	  printf("     %-8s  %6d  %6d  %8s  %6d  %6d  %8s\n",
		 cur->Hostname,
		 cur->call[OUT].calls,
		 cur->call[OUT].calls - cur->call[OUT].succs,
		 t1,
		 cur->call[IN].calls,
		 cur->call[IN].calls - cur->call[IN].succs,
		 t2);
	}
    }

  /*
   * File statistics report
   */
  if(show_files && have_files)
    {
      char t1[32], t2[32];

      for (sent = IN; sent <= OUT; ++sent)
	{
	  printf(file_hdr[sent]);
	  printf("     sysname    files     bytes  xfr time  byte/s\n");
	  printf("     --------  ------  --------  --------  ------\n");
	  for (cur = hosts; cur != NULL; cur = cur->next)
	    {
	      double rate;
	      double time;
	      
	      time = cur->call[IN].flow[sent].time +
		cur->call[OUT].flow[sent].time;
	      if (time == 0.0)
		continue;
	      rate = (cur->call[IN].flow[sent].bytes +
		      cur->call[OUT].flow[sent].bytes) / time;
	      fmbytes((cur->call[IN].flow[sent].bytes + 
		      cur->call[OUT].flow[sent].bytes), t1);
	      fmtime((cur->call[IN].flow[sent].time + 
		     cur->call[OUT].flow[sent].time), t2);
	      printf("     %-8s  %6d  %8s  %8s  %6.1f\n",
		     cur->Hostname,
		     cur->call[IN].flow[sent].files + 
		     cur->call[OUT].flow[sent].files,
		     t1, t2, rate);
	    }
	}
    }

  /*
   * Efficiency report
   */
  if (show_efficiency && have_files)
    {
      char t1[32], t2[32], t3[32];
      double total, flow;

      printf("\nEfficiency:\n");
      printf("     sysname   conntime  flowtime  ovhdtime  eff. %%\n");
      printf("     --------  --------  --------  --------  ------\n");
      for (cur = hosts; cur != NULL; cur = cur->next)
	{
	  total = cur->call[IN].connect_time + cur->call[OUT].connect_time;
	  flow = cur->call[IN].flow[IN].time + cur->call[IN].flow[OUT].time +
	    cur->call[OUT].flow[IN].time + cur->call[OUT].flow[OUT].time;
	  fmtime(total, t1);
	  fmtime(flow, t2);
	  fmtime((total-flow), t3);
	  printf("     %-8s  %8s  %8s  %8s  %5.1f%%\n",
		 cur->Hostname, t1, t2, t3, ((flow / total) * 100.0));
	}
    }

  /*
   * Command execution report
   */  
  if (show_commands & have_commands)
    {
      printf("\nCommand executions:\n");
      printf("     sysname    rmail   rnews   other\n");
      printf("     --------  ------  ------  ------\n");
      for (cur = hosts; cur != NULL; cur = cur->next)
	{
          int rmail, rnews, other;
	  
	  if (cur->cmds == NULL)
	    continue;
          rmail = rnews = other = 0;
	  for (cmd = cur->cmds; cmd != NULL; cmd = cmd->next)
	    {
	      if (strcmp(cmd->Commandname, "rmail") == 0)
		rmail += cmd->count;
	      else if (strcmp(cmd->Commandname, "rnews") == 0)
		rnews += cmd->count;
	      else
		other += cmd->count;
	    }
	  printf("     %-8s  %6d  %6d  %6d\n", cur->Hostname,
		 rmail, rnews, other);
	}
    }
  return;
  
 usage:
  fprintf(stderr,
      "Usage uurate [-cfexai] [-h hostname] [logfile ... logfile]\n");
  fprintf(stderr,"where:\t-c\tReport call statistics\n");
  fprintf(stderr, "\t-f\tReport file transfer statistics\n");
  fprintf(stderr, "\t-e\tReport efficiency statistics\n");
  fprintf(stderr, "\t-x\tReport command execution statistics\n");
  fprintf(stderr, "\t-a\tAll of the above reports\n");  
  fprintf(stderr, "\t-h host\tReport activities involving ONLY host\n");
  fprintf(stderr, "\t-i\tRead log info from standard input\n");
  fprintf(stderr,
      "If no report options given, a compact summary report is given.\n");
  fprintf(stderr,
      "If neither -i nor logfiles given, defaults to reading from\n");
  fprintf(stderr, "%s and %s\n\n", LOGFILE, STATFILE);
}

/*
 * fmtime() - Format time in hours & minutes;
 */
static void fmtime(dsec, buf)
     double dsec;
     char *buf;
{
  long hrs, min, lsec;

  lsec = dsec;
  hrs = lsec / 3600L;
  min = (lsec - (hrs * 3600L)) / 60L;

  sprintf(buf, "%02ld:%02ld", hrs, min);
}

/*
 * fmbytes - Format size in bytes
 */
static void fmbytes(n, buf)
     unsigned long n;
     char *buf;
{
  char t;
  double s = n;

  if(s >= 10239897.6)		/* More than 9999.9K ? */
    {
      s = (double)n / 1048576.0;	/* Yes, display in Megabytes */
      t = 'M';
    }
  else
    {
      s = (double)n / 1024.0;	/* Display in Kilobytes */
      t = 'K';
    }

  sprintf(buf, "%.1f%c", s, t);
}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.