/* * @(#)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 (denny@alisa.com) * Fri Feb 7 13:38:36 1992 * * Original author: * Mark Pizzolato mark@infopiz.UUCP * * 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. * * Stephan Niemz - Fri Apr 9 1993 * - Print totals in summary report, * - show all commands in execution report, * - count incoming calls correctly, * - suppress empty tables, * - don't divide by zero in efficiency report, * - limit the efficiency to 100% (could be more with the i-protocol), * - suppress some zeros to improve readability, * - check for failure of calloc, * - -h option changed to -s for consistency with all other uucp commands * (but -h was left in for comptibility). * * Scott Boyd - Thu Aug 26 13:21:34 PDT 1993 * - Changed hosts linked-list insertion routine so that hosts * are always listed in alphabetical order on reports. * * Klaus Dahlenburg - Fri Jun 18 1993 (1.2.2) * - redesigned the printed layout (sticked to those 80 column tubes). * - 'Retry time not ...' and ' ERROR: All matching ports ...' will now be * counted as calls and will raise the failed-call counter. * - times now shown as hh:mm:ss; the fields may hold up to 999 hrs * (a month equals 744 hrs at max). Printing will be as follows: * * hrs > 0 hh:mm:ss * min > 0 mm:ss * sec > 0 ss * leading zeroes are suppressed. * * - the bytes xfered will be given in thousands only (we're counting * so 1K is 1000 bytes!). Sums up to 9,999,999.9 thousand can be shown. * - dropped the fractions of a byte in columns: bytes/second (avg cps). * - File statistic changed to display in/out in one row instead of 2 * separate reports. * - eliminated the goto in command report and tightened the code; also * the 'goto usage' has been replaced by a call to void usage() with no * return (exit 1). * - a totaling is done for all reports now; the total values are held * within the structure; after finishing read there will be an alloc * for a site named 'Total' so that the totals line will be printed * more or less automatically. * - option -t implemented: that is every possible report will be given. * - the start and end date/time of the input files are printed; can be * dropped by the -q option at run time. * - it is now possible to use Log/Stats files from V2_LOGGING configs. * They must however not be mixed together (with each other). * - the Log/Stats files are taken from config which is passed via * Makefile at compile time. Variable to set is: newconfigdir. If the * default config can't be read the default values are used * (the config is optional!). * Note: keyword/filename must be on the same line (no continuation). * - -I option implemented to run with a different config file. In case * the file can't be opened the run is aborted! * - -q option implemented to run without environment report (default is * FALSE: print the report). * - -p option added to print protocol statistics: one for the packets * and one for the errors encountered * - reapplied patch by Scott Boyd that I did not * get knowledge of */ /* $Log: uurate.c,v $ /* Revision 1.1 1995/10/18 08:38:24 deraadt /* Initial revision /* /* Revision 1.3 1995/08/24 05:19:21 jtc /* Upgraded to Taylor UUCP 1.06.1 (Thanks to John Kohl). /* * Revision 1.15 1994/04/07 21:47:11 kdburg * printed 'no data avail' while there was data; layout chnaged * (cosmetic only) * * Revision 1.14 1994/04/07 21:16:32 kdburg * the layout of the protocol-used line within the LOGFILE changed * from 1.04 to 1.05; both formats may be used together; output * changed for packet report (columns adjusted) * * Revision 1.13 1994/04/04 10:04:35 kdburg * cosmetic change to the packet-report (separator lines) * * Revision 1.12 1994/03/30 19:52:04 kdburg * incorporated patch by Scott Boyd which was missing from this version * of uurate.c. Left the comment in cronological order. * * Revision 1.11 1994/03/28 18:53:22 kdburg * config not checked properly for 'logfile/statsfile' overwrites, bail-out * possible; wrong file name written to log for statsfile when found * * Revision 1.10 1993/09/28 16:46:51 kdburg * transmission failures denoted by: failed after ... in stats file * have not been counted at all. * * Revision 1.9 1993/08/17 23:38:36 kdburg * sometimes a line(site) was missing from the protocol stats due * to a missing +; added option -d and -v reassing option -h to print * the help; a zero was returned instead of a null-pointer by * prot_sum * * Revision 1.8 1993/07/03 06:58:55 kdburg * empty input not handled properly; assigned some buffer to input; msg * not displayed when no protocol data was available * * Revision 1.7 1993/06/27 10:31:53 kdburg * rindex was replaced by strchr must be strrchr * * Revision 1.6 1993/06/26 06:59:18 kdburg * switch hdr_done not reset at beginning of protocol report * * Revision 1.5 1993/06/25 22:22:30 kdburg * changed rindex to strchr; if there is no NEWCONFIG defined take * appropriate action * * Revision 1.4 1993/06/25 20:04:07 kdburg * added comment about -p option; inserted proto for rindex * * Revision 1.3 1993/06/25 19:31:14 kdburg * major rework done; added protocol reports (normal/errors) * * Revision 1.2 1993/06/21 19:53:54 kdburg * init * */ char version[] = "@(#) Taylor UUCP Log File Summary Filter, Version 1.2.2"; static char rcsid[] = "$Id: uurate.c,v 1.1 1995/10/18 08:38:24 deraadt Exp $"; #include /* Character Classification */ #include #include "uucp.h" /* uucp.h includes string.h or strings.h, no include here. */ #if HAVE_SYS_PARAM_H #include #endif #define _DEBUG_ 0 /* * Direction of Calling and Data Transmission */ #define IN 0 /* Inbound */ #define OUT 1 /* Outbound */ /* * define some limits */ #define MAXCOLS 8 /* report has this # of columns incl. 'name' */ #define MAXREP 6 /* number of reports available */ #define MAXFNAME 64 /* max input file name length incl. path*/ #define MAXDNAME 8 /* max display (Hostname) name length */ /* * 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 Protocol_Summary { struct Protocol_Summary *next; char type[3]; long int pr_cnt; long int pr_psent; long int pr_present; long int pr_preceived; long int pr_eheader; long int pr_echksum; long int pr_eorder; long int pr_ereject; long int pr_pwinmin; long int pr_pwinmax; long int pr_psizemin; long int pr_psizemax; }; struct Host_entry { struct Host_entry *next; char Hostname[32]; struct Execution_Command *cmds; /* Local Activities */ struct Phone_Call call[2]; /* In & Out Activities */ struct Protocol_Summary *proto; }; struct Host_entry *hosts = NULL; struct Host_entry *tot = NULL; struct Host_entry *cur = NULL; struct Execution_Command *cmd, *t_cmds = NULL; struct Protocol_Summary *prot, *t_prot, *s_prot, *ss_prot = NULL; /* * Stuff for getopt() */ extern int optind; /* GETOPT : Option Index */ extern char *optarg; /* GETOPT : Option Value */ #if ! HAVE_STDLIB_H extern pointer *calloc(); #endif /* HAVE_STDLIB_H */ /* * Default files to read. Taken from Taylor compile-time configuration. * def_logs must look like an argvec, hence the dummy argv[0]. * Maybe later modified by scanning the config */ static char *def_logs[3] = { NULL, NULL, NULL}; char *I_conf = NULL; /* points to config lib given by -I option */ char *D_conf = NULL; /* points to config lib from makefile */ char *Tlog = NULL; /* points to Log-file */ char *Tstat = NULL; /* points to Stats-file */ char Pgm_name[64]; /* our pgm-name */ char logline[BUFSIZ+1]; /* input area */ char noConf[] = "- not defined -"; char buff[16*BUFSIZ]; char sbuff[2*BUFSIZ]; /* * Boolean switches for various decisions */ int p_done = FALSE; /* TRUE: start date/time of file printed */ int hdr_done = FALSE; /* TRUE: report header printed */ int show_files = FALSE; /* TRUE: -f option given */ int show_calls = FALSE; /* TRUE: -c option given */ int show_commands = FALSE; /* TRUE: -x option given */ int show_efficiency = FALSE; /* TRUE: -e option given */ int show_all = FALSE; /* TRUE: -t option given */ int show_proto = FALSE; /* TRUE: -p option given */ int use_stdin = FALSE; /* TRUE: -i option given */ int be_quiet = FALSE; /* TRUE: -q option given */ int have_files[2]; /* TRUE: [IN] or [OUT] files found */ int have_calls = FALSE; /* TRUE: in/out calls found */ int have_commands = FALSE; /* TRUE: found uuxqt records */ int have_proto = FALSE; /* TRUE: protocol data found */ int no_records = TRUE; /* FALSE: got one record from file */ /* * protos */ static pointer *getmem(unsigned n); static void inc_cmd(struct Execution_Command **, char *name); static void fmtime(double sec, char *buf); static void fmbytes(unsigned long n, char *buf); static void usage(); static int chk_config(char *conf, int n, int type); static void hdrprt(char c, int bot); struct Protocol_Summary *prot_sum(struct Protocol_Summary **, char *, int); /* * BEGIN EXECUTION */ int main(argc, argv) int argc; char *argv[]; { FILE *Log = NULL; int c; char *p, *s, *stt, *flq = NULL; char Hostname[MAXHOSTNAMELEN]; /* def taken from */ char Filename[15]; /* filename to be printed */ char in_date[14]; /* holds the date info of record read*/ char in_time[14]; /* holds the time info of record read */ char dt_info[31]; /* holds the date info from the last record read */ char *logmsg; int sent, called = IN; int report = 0; /* if <= 0 give msg that no report was avail. */ int junk; /* -------------------------------------------------------------------- * P r o l o g * -------------------------------------------------------------------- */ Hostname[0] = '\0'; have_files[IN]= have_files[OUT]= FALSE; setvbuf(stdout,sbuff,_IOFBF,sizeof(sbuff)); /* * get how we've been called isolate the name from the path */ if ((stt = strrchr(argv[0],'/')) != NULL) strcpy(Pgm_name,++stt); else strcpy(Pgm_name,argv[0]); def_logs[0] = Pgm_name; /* * I wish the compiler had the #error directive! */ #if !HAVE_TAYLOR_LOGGING && !HAVE_V2_LOGGING fprintf(stderr,"\a%s: (E) %s\n",Pgm_name,"Your config of Taylor UUCP is not yet supported."); fprintf(stderr,"%s: (E) %s\n",Pgm_name,"Current support is for V2 or TAYLOR logging only."); puts(" Run aborted due to errors\n") exit(1); #endif /* * get some mem to store the default config name (def's are in * policy.h ) */ if (sizeof(NEWCONFIGLIB) > 1) /* defined at compile time */ { D_conf = (char *)getmem((sizeof(NEWCONFIGLIB) + sizeof("/config"))); strcpy(D_conf,NEWCONFIGLIB); /* passed by makefile */ strcat(D_conf,"/config"); } Tlog = (char *)getmem(sizeof(LOGFILE)); Tstat = (char *)getmem(sizeof(STATFILE)); Tlog = LOGFILE; Tstat = STATFILE; /* * Process the command line arguments */ while((c = getopt(argc, argv, "I:s:cfdexaitphv")) != EOF) { switch(c) { case 'h': (void) usage(); case 's': strcpy(Hostname, optarg); break; case 'c': show_calls = TRUE; ++report; break; case 'd': printf("%s: (I) config-file default: %s\n",Pgm_name,D_conf); exit (0); break; case 'f': show_files = TRUE; ++report; break; case 'x': show_commands = TRUE; ++report; break; case 'e': show_efficiency = TRUE; ++report; break; case 'a': show_calls = show_files = show_commands = show_efficiency = TRUE; report = 4; break; case 'i': use_stdin = TRUE; break; case 't': show_all = TRUE; report = MAXREP; break; case 'p': show_proto = TRUE; ++report; break; case 'I': I_conf = (char *)getmem(sizeof(optarg)); I_conf = optarg; break; case 'q': be_quiet = TRUE; break; case 'v': printf("%s\n",rcsid); exit (0); default : (void) usage(); } } if (report == 0) /* no options given */ ++report; /* at least summary can be printed */ if (! be_quiet) hdrprt('i',0); /* print header for environment info */ /* * 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 * which should be given in pairs (log plus stat) otherwise the results may * not come out as expected! If no further args are present 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 (use_stdin) /* If -i, read from stdin */ { if (argc != 1) /* No file arguments allowed */ { fprintf(stderr,"\a%s: (E) %s\n",Pgm_name, "it's not posssible to give file args with '-i'"); (void) usage(); } else { argc = 2; Log = stdin; if (! be_quiet) puts(" Input from stdin; no other files will be used\n"); } } else { if (argc != 1) /* file arguments are present */ { if (! be_quiet) puts(" No defaults used; will use passed file arguments\n"); } else /* Read from current logs */ { def_logs[1] = Tlog; /* prime the */ def_logs[2] = Tstat; /* file names */ if (! be_quiet) printf(" Config for this run: "); if (I_conf != NULL) { junk = 0; if (! be_quiet) printf("%s\n",I_conf); if (0 != (chk_config(I_conf,be_quiet,junk))) return (8); } else { if (D_conf != NULL) { junk = 1; /* indicate default (compiled) config */ if (! be_quiet) printf("%s\n",D_conf); chk_config(D_conf,be_quiet,junk); } else if (! be_quiet) printf("%s\n",noConf); } def_logs[1] = Tlog; /* final setting of */ def_logs[2] = Tstat; /* file names */ argv = def_logs; /* Bash argvec to log/stat files */ argc = sizeof(def_logs) / sizeof(def_logs[0]); } } /* -------------------------------------------------------------------- * MAIN LOGFILE PROCESSING LOOP * -------------------------------------------------------------------- */ if (!use_stdin) { if (argc < 3 && ! be_quiet) { puts(" (W) there is only one input file!"); puts(" (W) some reports may not be printed"); } if (! be_quiet) hdrprt('d',0); /* give subheaderline */ } while (argc > 1) { if (!use_stdin && (Log = fopen(argv[1], "r")) == NULL) { perror(argv[1]); exit (8); } setvbuf(Log,buff,_IOFBF,sizeof(buff)); if ((flq = strrchr(argv[1], '/')) == NULL) strncpy(Filename,argv[1],sizeof(Filename)-1); else strncpy(Filename,++flq,sizeof(Filename)-1); strcpy(in_date," n/a"); strcpy(in_time," n/a"); p_done = FALSE; /* no info printed yet */ no_records = TRUE; /* not read any record yet */ /* * 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. V2 and Taylor ar identical * up to this part. Put out the start/end date of the files read; */ if (NULL == (p = strchr(logline, ' '))) continue; no_records = FALSE; /* got one (usable) record at least */ ++p; if (NULL != (stt = strchr(p, '('))) { if (! p_done && ! use_stdin && ! be_quiet) { #if HAVE_TAYLOR_LOGGING sscanf(++stt,"%s%*c%[^.]",in_date,in_time); #endif /* HAVE_TAYLOR_LOGGING */ #if HAVE_V2_LOGGING sscanf(++stt,"%[^-]%*c%[1234567890:]",in_date,in_time); #endif /* HAVE_V2_LOGGING */ printf(" %-14s %10s %8s",Filename, in_date, in_time); strcpy(in_date," n/a"); /* reset to default */ strcpy(in_time," n/a"); p_done = TRUE; } else { if (! use_stdin && ! be_quiet) /* save for last time stamp prt. */ strncpy(dt_info,++stt,sizeof(dt_info)-1); } } if (NULL != (s = strchr(p, ' '))) *s = '\0'; for (s = p; *s; ++s) if (isupper(*s)) *s = tolower(*s); /* * Skip this line if we got -s and * this line does not contain that host name. * Don't skip the `incoming call' line with the system name `-'. */ if (Hostname[0] != '\0') if ( (p[0] != '-' || p[1] != '\0') && 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; #if ! HAVE_TAYLOR_LOGGING #if HAVE_V2_LOGGING if ((strncmp(s,") (",3)) == 0) /* are we in stats file ?) */ if ((s = strchr(++s, ')')) == NULL) continue; /* yes but strange layout */ #endif /* HAVE_V2_LOGGING */ #endif /* ! HAVE_TAYLOR_LOGGING */ logmsg = s + 2; /* Message is 2 characters after ')' */ if ((0 != strncmp(logmsg, "Call complete", 13)) && (0 != strncmp(logmsg, "Calling system", 14)) && (0 != strncmp(logmsg, "Incoming call", 13)) && (0 != strncmp(logmsg, "Handshake successful", 20)) && (0 != strncmp(logmsg, "Retry time not", 14)) && (0 != strncmp(logmsg, "ERROR: All matching ports", 25)) && (0 != strncmp(logmsg, "Executing", 9)) && (0 != strncmp(logmsg, "Protocol ", 9)) && (0 != strncmp(logmsg, "sent ", 5)) && (0 != strncmp(logmsg, "received ", 9)) && (0 != strncmp(logmsg, "failed after ", 13)) && (0 != strncmp(logmsg, "Errors: ", 8))) 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))) { struct Host_entry *e, *last; for (e= cur= hosts; cur != NULL ; e= cur, cur= cur->next) if (0 == strcmp(cur->Hostname, p)) break; if (cur == NULL) { cur= (struct Host_entry *)getmem(sizeof(*hosts)); strcpy(cur->Hostname, p); if (hosts == NULL) e= hosts= cur; else { e = hosts; last = NULL; while (e != NULL) { if (strcmp(e->Hostname, cur->Hostname) <= 0) { if (e->next == NULL) { e->next = cur; break; } last = e; e = e->next; } else { cur->next = e; if (last == NULL) hosts = cur; else last->next = cur; break; } } /* while (e != NULL) */ } /* hosts == NULL */ } /* cur == NULL */ } /* * 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. * (Log file only) */ if (0 == strncmp(logmsg, "Executing", 9)) { if (NULL == (p = strchr(logmsg, '('))) continue; if ((s = strpbrk(++p, " )")) == NULL) continue; *s = '\0'; inc_cmd(&cur->cmds, p); inc_cmd(&t_cmds, p); have_commands = TRUE; continue; } /* * Count start of outgoing call. */ if ((0 == strncmp(logmsg, "Calling system", 14)) || (0 == strncmp(logmsg, "Retry time not", 14)) || (0 == strncmp(logmsg, "ERROR: All matching ports", 25))) { called = OUT; cur->call[OUT].calls++; have_calls = TRUE; s_prot = NULL; /* destroy pointer to protocol */ continue; } /* * Count start of incoming call. */ if (0 == strncmp(logmsg, "Incoming call", 13)) { called = IN; s_prot = NULL; /* destroy pointer to protocol */ continue; } /* * On an incoming call, get system name from the second line. * Get protocol type and size/window too */ if (0 == strncmp(logmsg, "Handshake successful", 20)) { if ( called==IN ) cur->call[IN].calls++; have_calls = TRUE; s_prot = NULL; /* destroy pointer to protocol */ if (NULL == (p = strchr(logmsg, '('))) continue; if (0 == strncmp(p, "(protocol ", 10)) { if (NULL == (p = strchr(p, '\''))) continue; ss_prot = prot_sum(&cur->proto, ++p, 1); s_prot = prot_sum(&t_prot, p, 1); continue; } } /* * check protocol type and get stats * */ if (0 == strncmp(logmsg, "Protocol ", 9)) { s_prot = NULL; /* destroy pointer to protocol */ if (NULL == (p = strchr(logmsg, '\''))) continue; ss_prot = prot_sum(&cur->proto, ++p, 2); s_prot = prot_sum(&t_prot, p, 2); continue; } /* * check protocol errors. Unfortunately the line does not contain * the used protocol, so if any previous line did contain that * information and we did process that line we will save the pointer * to that particular segment into s_prot. If this pointer is not set * the error info is lost for we don't know where to store. * */ if ((0 == strncmp(logmsg, "Errors: header", 14)) && s_prot != NULL) { int i1,i2,i3,i4 = 0; sscanf(logmsg,"%*s %*s %d%*c%*s %d%*c%*s %d%*c%*s %*s%*c %d",&i1,&i2,&i3,&i4); ss_prot->pr_eheader += i1; ss_prot->pr_echksum += i2; ss_prot->pr_eorder += i3; ss_prot->pr_ereject += i4; s_prot->pr_eheader += i1; s_prot->pr_echksum += i2; s_prot->pr_eorder += i3; s_prot->pr_ereject += i4; s_prot = NULL; continue; } /* * Handle end of call. Pick up the connect time. * position is on the closing paren of date/time info * i.e: ) text.... */ if (0 == strncmp(logmsg, "Call complete", 13)) { cur->call[called].succs++; s_prot = NULL; /* destroy pointer to protocol */ if (NULL == (s = strchr(logmsg, '('))) continue; cur->call[called].connect_time += atof(s+1); continue; } /* * We are definitely in a Stats file now. * 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. * Position within the record is at the word 'received' or 'sent' * depending on the direction. */ sent = IN; /* give it an initial value */ if (0 == strncmp(logmsg, "failed after ",13)) logmsg += 13; /* the transmission failed for any reason */ /* so advance pointer */ if (0 == strncmp(logmsg, "sent", 4)) sent = OUT; else if (0 == strncmp(logmsg, "received", 8)) sent = IN; have_files[sent] = TRUE; cur->call[called].flow[sent].files++; if (NULL == (s = strchr(logmsg, ' '))) /* point past keyword */ continue; /* nothing follows */ /* we should be at the bytes column now*/ #if HAVE_TAYLOR_LOGGING cur->call[called].flow[sent].bytes += atol(++s); #endif /* HAVE_TAYLOR_LOGGING */ #if HAVE_V2_LOGGING if (NULL == (s = strpbrk(s, "0123456789"))) /* point to # bytes */ continue; cur->call[called].flow[sent].bytes += atol(s); #endif /* HAVE_V2_LOGGING */ if (NULL == (s = strchr(s, ' '))) /* point past # of bytes */ continue; if (NULL == (s = strpbrk(s, "0123456789"))) /* point to # of seconds */ continue; cur->call[called].flow[sent].time += atof(s); } /* end of while (fgets(logline...)) */ if (stt != NULL && ! use_stdin && ! be_quiet && ! no_records) { #if HAVE_TAYLOR_LOGGING sscanf(dt_info,"%s%*c%[^.]",in_date,in_time); #endif /* HAVE_TAYLOR_LOGGING */ #if HAVE_V2_LOGGING sscanf(dt_info,"%[^-]%*c%[1234567890:]",in_date,in_time); #endif /* HAVE_V2_LOGGING */ printf(" %10s %8s\n",in_date, in_time); p_done = FALSE; } if (Log != stdin) { if (0 != ferror(Log)) { if (! be_quiet) printf(" %-14s data is incomplete; read error"," "); else fprintf(stderr,"%s (W) data is incomplete; read error on %s\n", Pgm_name,argv[1]); } else { if (! be_quiet && no_records) printf(" %-14s %10s\n",Filename, " is empty "); } } fclose(Log); argc--; argv++; } /* end of while (for (argv ....) */ /* * do we have *any* data ? */ if (cur == NULL) { puts("\n(I) Sorry! No data is available for any requested report\n"); exit(0); } /* * truncate hostname, alloc the structure holding the totals and * collect the totals data */ for (cur = hosts; cur != NULL;cur = cur->next) { cur->Hostname[MAXDNAME] = '\0'; if (cur->next == NULL) /* last so will have to alloc totals */ { cur->next = (struct Host_entry *)getmem(sizeof(*hosts)); strcpy(cur->next->Hostname,"Totals"); tot = cur->next; for (cur = hosts; cur != NULL; cur = cur->next) { if (cur->next != NULL) /* don't count totals to totals */ { tot->call[IN].flow[IN].bytes += cur->call[IN].flow[IN].bytes; tot->call[OUT].flow[IN].bytes += cur->call[OUT].flow[IN].bytes; tot->call[IN].flow[OUT].bytes += cur->call[IN].flow[OUT].bytes; tot->call[OUT].flow[OUT].bytes += cur->call[OUT].flow[OUT].bytes; tot->call[IN].flow[IN].time += cur->call[IN].flow[IN].time; tot->call[OUT].flow[IN].time += cur->call[OUT].flow[IN].time; tot->call[IN].flow[OUT].time += cur->call[IN].flow[OUT].time; tot->call[OUT].flow[OUT].time += cur->call[OUT].flow[OUT].time; tot->call[IN].flow[IN].files += cur->call[IN].flow[IN].files; tot->call[OUT].flow[IN].files += cur->call[OUT].flow[IN].files; tot->call[IN].flow[OUT].files += cur->call[IN].flow[OUT].files; tot->call[OUT].flow[OUT].files += cur->call[OUT].flow[OUT].files; tot->call[OUT].succs += cur->call[OUT].succs; tot->call[OUT].calls += cur->call[OUT].calls; tot->call[OUT].connect_time += cur->call[OUT].connect_time; tot->call[IN].succs += cur->call[IN].succs; tot->call[IN].calls += cur->call[IN].calls; tot->call[IN].connect_time += cur->call[IN].connect_time; } } break; /* totals is last in Host_Entry */ } } /* * *********** * * REPORTS * * *********** */ #if _DEBUG_ putchar('\n'); #endif /* ------------------------------------------------------------------ * * Summary report only when no other report except option -t is given * * I know, this code could be tightened (rbd)... * ------------------------------------------------------------------ */ if ( !(show_calls || show_files || show_efficiency || show_commands || show_proto) || show_all) { if (have_calls || have_files[IN] || have_files[OUT]) { char t1[32], t2[32], t3[32], t4[32], t5[32]; long ib, ob, b, rf, sf; double it, ot, ir, or; hdr_done = FALSE; for (cur = hosts; cur != NULL; cur = cur->next) { ib = (cur->call[IN].flow[IN].bytes + cur->call[OUT].flow[IN].bytes); fmbytes(ib, t1); ob = (cur->call[IN].flow[OUT].bytes + cur->call[OUT].flow[OUT].bytes); fmbytes(ob, t2); /* Don't print null-lines. */ if (( b= ib+ob ) == 0 ) continue; /* Don't print the header twice. */ if (! hdr_done) { hdrprt('s',0); /* print the header line(s) */ hdr_done = TRUE; } fmbytes(b, t3); it = cur->call[IN].flow[IN].time + cur->call[OUT].flow[IN].time; fmtime(it, t4); ot = cur->call[IN].flow[OUT].time + cur->call[OUT].flow[OUT].time; fmtime(ot, t5); rf = cur->call[IN].flow[IN].files + cur->call[OUT].flow[IN].files; sf = cur->call[IN].flow[OUT].files + cur->call[OUT].flow[OUT].files; ir = (it == 0.0) ? 0.0 : (ib / it); or = (ot == 0.0) ? 0.0 : (ob / ot); if (cur->next == NULL) /* totals line reached ? */ hdrprt('s',1); /* print the separator line */ printf("%-8s %4d %4d %9s %9s %9s %9s %9s %5.0f %5.0f\n", cur->Hostname, rf, sf, t1, t2, t3, t4, t5, ir, or); } if (! hdr_done) { puts("\n(I) No data found to print Compact summary report"); } } else { puts("\n(I) No data available for Compact summary report"); --report; } } /* ------------------------------------------------------------------ * Protocol statistics report * ------------------------------------------------------------------ */ if (show_proto || show_all) { if (have_proto) { /* --------------------- */ /* protocol packet report */ /* --------------------- */ char *type = NULL; hdr_done = FALSE; for (cur = hosts; cur != NULL; cur = cur->next) { type = cur->Hostname; if (cur->next == NULL) { if (hdr_done) puts("-------------------------------------------------------------------"); cur->proto = t_prot; } for (prot = cur->proto; prot != NULL; prot = prot->next) { if (! hdr_done) { hdrprt('p',0); /* print the header line(s) */ hdr_done = TRUE; } printf("%-8s %3s %4d %4d %5d %4d %10d %7d %10d\n", type == NULL ? " ":cur->Hostname, prot->type, prot->pr_psizemin, prot->pr_psizemax, prot->pr_pwinmin, prot->pr_pwinmax, prot->pr_psent, prot->pr_present, prot->pr_preceived); type = NULL; } } if (! hdr_done) puts("\n(I) No data found to print Protocol packet report"); /* --------------------- */ /* protocol error report */ /* --------------------- */ type = NULL; hdr_done = FALSE; if (t_prot != NULL) { for (cur = hosts; cur != NULL; cur = cur->next) { type = cur->Hostname; if (cur->next == NULL) { if (hdr_done) puts("--------------------------------------------------------------"); cur->proto = t_prot; } for (prot = cur->proto; prot != NULL; prot = prot->next) { if ((prot->pr_eheader + prot->pr_echksum + prot->pr_eorder + prot->pr_ereject) != 0) { if (! hdr_done) { hdrprt('p',1); /* print the header line(s) */ hdr_done = TRUE; } printf("%-8s %3s %11d %11d %11d %11d\n", type == NULL ? " ":cur->Hostname, prot->type, prot->pr_eheader, prot->pr_echksum, prot->pr_eorder, prot->pr_ereject); type = NULL; } } } } if (! hdr_done) puts("\n(I) No data found to print Protocol error report"); } else { puts("\n(I) No data available for Protocol reports"); --report; } } /* ------------------------------------------------------------------ * Call statistics report * ------------------------------------------------------------------ */ if (show_calls || show_all) { if (have_calls) { char t1[32], t2[32]; hdr_done = FALSE; for (cur = hosts; cur != NULL; cur = cur->next) { if (cur->next == NULL) { if (hdr_done) hdrprt('c',1); /* print the separator line */ } else { /* Don't print null-lines on deatail lines */ if ( cur->call[OUT].calls + cur->call[IN].calls == 0 ) continue; /* Don't print the header twice. */ if (! hdr_done) { hdrprt('c',0); /* print the header line(s) */ hdr_done = TRUE; } } if ( cur->call[OUT].calls > 0 || cur->next == NULL) { fmtime(cur->call[OUT].connect_time, t1); printf( " %-8s %7d %7d %7d %9s", cur->Hostname, cur->call[OUT].succs, cur->call[OUT].calls - cur->call[OUT].succs, cur->call[OUT].calls, t1 ); } else { printf( " %-42s", cur->Hostname ); } if ( cur->call[IN].calls > 0 || cur->next == NULL ) { fmtime(cur->call[IN].connect_time, t2); printf( " %7d %7d %7d %9s", cur->call[IN].succs, cur->call[IN].calls - cur->call[IN].succs, cur->call[IN].calls, t2 ); } putchar('\n'); } if (! hdr_done) { puts("\n(I) No data found to print Call statistics report"); } } else { puts("\n(I) No data available for Call statistics report"); --report; } } /* ------------------------------------------------------------------ * File statistics report * ------------------------------------------------------------------ */ if (show_files || show_all) { if (have_files[IN] || have_files[OUT]) { char t1[32], t2[32]; double rate = 0, time = 0; int b = 0; int lineOut = 0; hdr_done = FALSE; for (cur = hosts; cur != NULL; cur = cur->next) { lineOut = 0; for (sent= IN; sent <= OUT; ++sent) { b = cur->call[IN].flow[sent].bytes + cur->call[OUT].flow[sent].bytes; time = cur->call[IN].flow[sent].time + cur->call[OUT].flow[sent].time; /* Don't print null-lines on detail lines. */ if ( (b != 0 && time != 0.0) || cur->next == NULL) { /* Don't print the header twice. */ if (! hdr_done) { hdrprt('f',0); /* print the header line(s) */ hdr_done = TRUE; } fmbytes(b, t1); rate = (cur->call[IN].flow[sent].bytes + cur->call[OUT].flow[sent].bytes) / time; fmtime((cur->call[IN].flow[sent].time + cur->call[OUT].flow[sent].time), t2); if (lineOut == 0) /* first half not printed yet ? */ { if (cur->next == NULL) /* totals line ? */ hdrprt('f',1); /* print the separator line */ printf(" %-8s", cur->Hostname); if (sent == OUT) /* can't happen whith totals line */ printf("%34s", " "); } printf(" %5d %11s %9s %5.0f", cur->call[IN].flow[sent].files + cur->call[OUT].flow[sent].files, t1, t2, rate); lineOut = 1; } } /* end: for (sent ... ) */ if (lineOut) printf("\n"); } /* end: for (cur= ... ) */ if (! hdr_done) { puts("\n(I) No data found to print File statistics report"); } } else { puts("\n(I) No data available for File statistics report"); --report; } } /* ------------------------------------------------------------------ * Efficiency report * ------------------------------------------------------------------ */ if (show_efficiency || show_all) { if (have_files[IN] || have_files[OUT]) { char t1[32], t2[32], t3[32]; double total, flow; hdr_done = FALSE; for (cur = hosts; cur != NULL; cur = cur->next) { /* Don't print null-lines. */ if ( 0 == cur->call[IN].flow[IN].files + cur->call[IN].flow[OUT].files + cur->call[OUT].flow[IN].files + cur->call[OUT].flow[OUT].files || 0.0 == (total= cur->call[IN].connect_time + cur->call[OUT].connect_time)) { continue; } if (! hdr_done) { hdrprt('e',0); /* print the header line(s) */ hdr_done = TRUE; } 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); if (cur->next == NULL) hdrprt('e',1); /* print the separator line */ printf(" %-8s %10s %10s %10s %7.2f\n", cur->Hostname, t1, t2, t3, flow >= total ? 100.0: flow*100.0/total); } /* end: for (cur= .. */ if (! hdr_done) { puts("\n(I) No data found to print Efficiency report"); } } else { puts("\n(I) No data available for Efficiency report"); --report; } } /* ------------------------------------------------------------------ * Command execution report * ------------------------------------------------------------------ */ if (show_commands || show_all) { if (have_commands) { int ncmds, i, match; /* * layout the header line. The column's header is the command name */ hdr_done = FALSE; for (ncmds= 0, cmd= t_cmds; cmd != NULL && ncmds <= MAXCOLS-1; ncmds++, cmd= cmd->next) { if (! hdr_done) { puts("\nCommand executions:"); puts("-------------------"); puts(" Name of "); fputs(" site ", stdout); hdr_done = TRUE; } printf(" %7s", cmd->Commandname); } if (! hdr_done) { puts("\n(I) No data found to print Command execution report"); } else { fputs("\n --------", stdout); for (i= 0; inext) { if (cur->next == NULL) break; /* Don't print null-lines. */ if (cur->cmds == NULL) continue; printf(" %-8s", cur->Hostname); for (cmd= t_cmds; cmd != NULL; cmd= cmd->next) { struct Execution_Command *ec; match = FALSE; for(ec= cur->cmds; ec != NULL; ec= ec->next) { if ( 0 == strcmp(cmd->Commandname, ec->Commandname) ) { printf(" %7d", ec->count); match = TRUE; break; } } if (! match) printf("%8s"," "); /* blank out column */ } putchar('\n'); } /* * print the totals line */ fputs(" --------", stdout); for (i= 0; iHostname); for (cmd= t_cmds; cmd != NULL; cmd= cmd->next) { printf(" %7d", cmd->count); } putchar('\n'); } } else { puts("\n(I) No data available for Command execution report"); --report; } } if (report <= 0 ) /* any reports ? */ { puts("\n(I) Sorry! No data is available for any requested report\n"); exit(1); } puts("\n(I) End of reports\n"); exit (0); } /* end of main */ /* ------------------------------------------------------------------ * * Functions * * ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ * display the help * ------------------------------------------------------------------ */ void usage() { fprintf(stderr,"Usage uurate [-acdefhiptvx] [-s hostname] [-I config file] [logfile(s) ... logfile(s)]\n"); fprintf(stderr,"where:\t-a\tPrint reports c,e,f,x\n"); fprintf(stderr,"\t-c\tReport call statistics\n"); fprintf(stderr,"\t-d\tPrint the name of the default config file\n"); fprintf(stderr,"\t-e\tReport efficiency statistics\n"); fprintf(stderr,"\t-f\tReport file transfer statistics\n"); fprintf(stderr,"\t-h\tPrint this help\n"); fprintf(stderr,"\t-i\tRead log info from standard input\n"); fprintf(stderr,"\t-p\tReport protocol statistics\n"); fprintf(stderr,"\t-t\tAll available reports plus compact summary report\n"); fprintf(stderr,"\t-v\tPrint version number\n"); fprintf(stderr,"\t-x\tReport command execution statistics\n"); fprintf(stderr,"\t-s host\tReport activities involving HOST only\n"); fprintf(stderr,"\t-I config Use config instead of standard config file\n"); fprintf(stderr,"If no report options given, a compact summary report is printed.\n"); fprintf(stderr,"log files should be given as pairs that is Log/Stats ... .\n"); fprintf(stderr,"If neither -i nor logfiles given, those names found in config will be used\n"); exit (1); } /* ------------------------------------------------------------------ * getmem - get some memory * ------------------------------------------------------------------ */ static pointer *getmem(n) unsigned n; { pointer *p; if( NULL== (p= calloc(1, n)) ) { fprintf(stderr,"\a%s (C) %s\n",Pgm_name, "out of memory\n"); exit (8); } return p; } /* ------------------------------------------------------------------ * inc_cmd - increment command count * ------------------------------------------------------------------ */ static void inc_cmd(cmds, name) struct Execution_Command **cmds; char *name; { int cnt = 0; struct Execution_Command *cmd, *ec; for (ec = cmd = *cmds; cmd != NULL; ec= cmd, cmd= cmd->next, cnt++) if ( (0 == strcmp(cmd->Commandname, name)) || (0 == strcmp(cmd->Commandname, "Misc.")) ) break; if (cmd == NULL) { cmd= (struct Execution_Command *)getmem(sizeof(*cmd)); if (cnt <= MAXCOLS-1) /* first col prints site name therefore < max-1 */ { strcpy(cmd->Commandname, name); if (*cmds == NULL) ec = *cmds = cmd; else ec->next= cmd; } else { strcpy(ec->Commandname, "Misc."); /* reached high-water-mark */ cmd = ec; /* backtrack */ } } cmd->count++; } /* ------------------------------------------------------------------ * prot_sum - collect protocol data * ------------------------------------------------------------------ */ struct Protocol_Summary * prot_sum(proto, ptype, ind) struct Protocol_Summary **proto; char *ptype; int ind; { int cnt = 0; int i1, i2, i3 = 0; struct Protocol_Summary *cur, *first; for (first = cur = *proto; cur != NULL; first= cur, cur= cur->next, cnt++) { if ( (0 == strncmp(cur->type, ptype,strlen(cur->type)))) break; } if (cur == NULL) { cur= (struct Protocol_Summary *)getmem(sizeof(*cur)); sscanf(ptype,"%[^\' ]3",cur->type); if (*proto == NULL) first = *proto = cur; else first->next= cur; } if (NULL == (ptype = strchr(ptype, ' '))) return (NULL); cur->pr_cnt++; have_proto = TRUE; ++ptype; switch(ind) { case 1: /* used protocol line */ /* * uucp-1.04 format: .... packet size ssss window ww) * uucp-1.05 format: .... remote packet/window ssss/ww local ssss/ww) * (the remote packet/window will be used!) */ i1 = i2 = 0; /* reset */ if (NULL == (strchr(ptype, '/'))) sscanf(ptype,"%*s %*s %d %*s %d",&i1,&i2); else sscanf(ptype,"%*s %*s %d/%d",&i1,&i2); if (i1 > cur->pr_psizemax) cur->pr_psizemax = i1; if (i1 < cur->pr_psizemin || cur->pr_psizemin == 0) cur->pr_psizemin = i1; if (i2 > cur->pr_pwinmax) cur->pr_pwinmax = i2; if (i2 < cur->pr_pwinmin || cur->pr_pwinmin == 0) cur->pr_pwinmin = i2; break; case 2: /* protocol statistics line */ i1 = i2 = i3 = 0; /* reset */ sscanf(ptype,"%*s %*s %d%*c %*s %d%*c %*s %d",&i1,&i2,&i3); cur->pr_psent += i1; cur->pr_present += i2; cur->pr_preceived += i3; break; default: break; } return (cur); } /* ------------------------------------------------------------------ * fmtime() - Format time in hours & minutes & seconds; * ------------------------------------------------------------------ */ static void fmtime(dsec, buf) double dsec; char *buf; { long hrs, min, lsec; if( dsec <= 0 ) { strcpy(buf, "0" ); return; } lsec = fmod(dsec+0.5, 60L); /* round to the next full second */ hrs = dsec / 3600L; min = ((long)dsec / 60L) % 60L; if (hrs == 0) if (min == 0) sprintf(buf,"%6s%2ld"," ",lsec); else sprintf(buf,"%3s%2ld:%02ld"," ",min,lsec); else sprintf(buf,"%2ld:%02ld:%02ld",hrs,min,lsec); } /* ------------------------------------------------------------------ * fmbytes - Format size in bytes * ------------------------------------------------------------------ */ static void fmbytes(n, buf) unsigned long n; char *buf; { if ( n == 0 ) { strcpy( buf, "0.0" ); return; } sprintf(buf, "%.1f", (double)n / 1000.0); /* Display in Kilobytes */ } /* ------------------------------------------------------------------ * chk_config - Read the config file * check on keywords: logfile and statfile. When found override * the corresponding default * ------------------------------------------------------------------ */ int chk_config(char *T_conf,int be_quiet, int type) { FILE *Conf; char keywrd[9]; char name[MAXPATHLEN+1]; char *pos1, *pos2; int i = 0; int logf = FALSE; int statf = FALSE; if ((Conf = fopen(T_conf, "r")) == NULL) { if (! be_quiet) { puts(" Could not open config"); if (type == 0) { puts(" The run will be aborted\n"); return (8); } } else { fprintf(stderr,"%s (E) %s %s \n",Pgm_name, "could not open config:", T_conf); if (type != 0) fprintf(stderr,"%s (W) defaults used for all files\n", Pgm_name); else { fprintf(stderr,"%s (C) ended due to errors\n", Pgm_name); return (8); } } } else { while (fgets(logline, sizeof(logline), Conf)) { if (logline[0] == '#') continue; sscanf(logline,"%8s %s",keywrd,name); if (0 == strncmp(keywrd,"logfile",7)) { pos1 = pos2 = name; for (i=0;(i<=MAXPATHLEN && *pos1 != '\0');pos1++,pos2++,i++) { if (*pos1 == '#') /* name immed followed by comment */ break; if (*pos1 == '\\') /* quoted comment (filename has #) */ { ++pos1; /* skip escape char */ if (*pos1 != '#') /* continuation ? */ { puts(" Config error:"); puts(" Found filename continuation; bailing out\n"); exit (8); } } *pos2 = *pos1; /* move char */ } *pos2 = '\0'; /* terminate string */ Tlog = (char *)getmem(strlen(name)+1); strcpy(Tlog,name); if (! be_quiet) printf(" logfile used: %s\n",Tlog); logf = TRUE; if (statf) /* statsfile still to come ? */ break; /* no finished */ continue; } if (0 == strncmp(keywrd,"statfile",8)) { pos1 = pos2 = name; for (i=0;(i<=MAXPATHLEN && *pos1 != '\0');pos1++,pos2++,i++) { if (*pos1 == '#') /* name immed followed by comment */ break; if (*pos1 == '\\') /* quoted comment (filename has #) */ { ++pos1; /* skip escape char */ if (*pos1 != '#') /* continuation ? */ { puts(" Config error:"); puts(" Found filename continuation; bailing out\n"); exit (8); } } *pos2 = *pos1; /* move char */ } *pos2 = '\0'; /* terminate string */ Tstat = (char *)getmem(strlen(name)+1); strcpy(Tstat,name); if (! be_quiet) printf(" statfile used: %s\n",Tstat); statf = TRUE; if (logf) /* logfile still to come ? */ break; /* no finished */ continue; } } fclose(Conf); } if (! be_quiet) { if (! logf) puts(" logfile used: - default -"); if (! statf) puts(" statfile used: - default -"); } return 0; } /* ------------------------------------------------------------------ * hdrprt - Print Header/Trailer lines (constant data) * ------------------------------------------------------------------ */ static void hdrprt(char head, int bot) { switch(head) { case('s'): /* standard summary report */ if (bot == 0) { puts("\nCompact summary:"); puts("----------------"); puts("\ Name of + Files + +------- Bytes/1000 --------+ +------ Time -----+ + Avg CPS +\n\ site in out inbound outbound total inbound outbound in out\n\ -------- ---- ---- --------- --------- --------- --------- --------- ----- -----"); } else puts("\ --------------------------------------------------------------------------------"); break; case('f'): /* file statistic report */ if (bot == 0) { puts("\nFile statistics:"); puts("----------------"); puts(" Name of +----------- Inbound -----------+ +---------- Outbound -----------+"); puts(" site files Bytes/1000 xfr time B/sec files Bytes/1000 xfr time B/sec"); puts(" -------- ----- ----------- --------- ----- ----- ----------- --------- -----"); } else puts("\ ----------------------------------------------------------------------------"); break; case('c'): /* calls statistic report */ if (bot == 0) { puts("\nCall statistics:"); puts("----------------"); puts(" Name of +------- Outbound Calls -------+ +-------- Inbound Calls ------+"); puts(" site succ. failed total time succ. failed total time"); puts(" -------- ------ ------ ------ --------- ------ ------ ------ ---------"); } else puts("\ ----------------------------------------------------------------------------"); break; case('e'): /* efficiency statistic report */ if (bot == 0) { puts("\nEfficiency:"); puts("-----------"); puts(" Name of +------ Times inbound/outbound -------+"); puts(" site connected xfr time overhead eff. %"); puts(" -------- --------- --------- --------- ------"); } else puts(" -------------------------------------------------"); break; case('i'): /* Environment information */ if (bot == 0) { puts("\nEnvironment Information:"); puts("------------------------"); printf(" Default config: %s\n",D_conf == NULL ? noConf:D_conf); printf(" Default logfile: %s\n",Tlog); printf(" Default statfile: %s\n\n",Tstat); } break; case('d'): /* Date/time coverage */ if (bot == 0) { puts("\n Date coverage of input files:"); puts(" Name of +----- Start -----+ +------ End ------+"); puts(" file date time date time"); puts(" -------- ---------- -------- ---------- --------"); } break; case('p'): /* Protocol stats */ if (bot == 0) { puts("\nProtocol packet report:"); puts("-----------------------"); puts(" +------- protocol -----+ +--------- Packets ----------+"); puts("Name of packet window "); puts("site typ min max min max sent resent received"); puts("-------- --- ---- ---- ---- ---- ----------- ------- ----------"); } else { puts("\nProtocol error report:"); puts("----------------------"); puts("Name of +----------------- Error Types --------------------+"); puts("site typ header checksum order rem-reject"); puts("-------- --- ----------- ---------- ----------- ----------"); } break; default: if (bot == 0) { puts("\nNo header for this report defined:"); } else puts(" "); break; } }