2 * @(#)uurate.c 1.2 - Thu Sep 3 18:32:46 1992
4 * This program digests log and stats files in the "Taylor" format
5 * and outputs various statistical data to standard out.
8 * Bob Denny (denny@alisa.com)
9 * Fri Feb 7 13:38:36 1992
12 * Mark Pizzolato mark@infopiz.UUCP
15 * Bob Denny - Fri Feb 7 15:04:54 1992
16 * Heavy rework for Taylor UUCP. This was the (very old) uurate from
17 * DECUS UUCP, which had a single logfile for activity and stats.
18 * Personally, I would have done things differently, with tables
19 * and case statements, but in the interest of time, I preserved
20 * Mark Pizzolato's techniques and style.
22 * Bob Denny - Sun Aug 30 14:18:50 1992
23 * Changes to report format suggested by Francois Pinard and others.
24 * Add summary report, format from uutraf.pl (perl script), again
25 * thanks to Francois. Integrate and checkout with 1.03 of Taylor UUCP.
27 * Stephan Niemz <stephan@sunlab.ka.sub.org> - Fri Apr 9 1993
28 * - Print totals in summary report,
29 * - show all commands in execution report,
30 * - count incoming calls correctly,
31 * - suppress empty tables,
32 * - don't divide by zero in efficiency report,
33 * - limit the efficiency to 100% (could be more with the i-protocol),
34 * - suppress some zeros to improve readability,
35 * - check for failure of calloc,
36 * - -h option changed to -s for consistency with all other uucp commands
37 * (but -h was left in for comptibility).
39 * Scott Boyd <scott@futures.com> - Thu Aug 26 13:21:34 PDT 1993
40 * - Changed hosts linked-list insertion routine so that hosts
41 * are always listed in alphabetical order on reports.
43 * Klaus Dahlenburg <kdburg@incoahe.hanse.de> - Fri Jun 18 1993 (1.2.2)
44 * - redesigned the printed layout (sticked to those 80 column tubes).
45 * - 'Retry time not ...' and ' ERROR: All matching ports ...' will now be
46 * counted as calls and will raise the failed-call counter.
47 * - times now shown as hh:mm:ss; the fields may hold up to 999 hrs
48 * (a month equals 744 hrs at max). Printing will be as follows:
53 * leading zeroes are suppressed.
55 * - the bytes xfered will be given in thousands only (we're counting
56 * so 1K is 1000 bytes!). Sums up to 9,999,999.9 thousand can be shown.
57 * - dropped the fractions of a byte in columns: bytes/second (avg cps).
58 * - File statistic changed to display in/out in one row instead of 2
60 * - eliminated the goto in command report and tightened the code; also
61 * the 'goto usage' has been replaced by a call to void usage() with no
63 * - a totaling is done for all reports now; the total values are held
64 * within the structure; after finishing read there will be an alloc
65 * for a site named 'Total' so that the totals line will be printed
66 * more or less automatically.
67 * - option -t implemented: that is every possible report will be given.
68 * - the start and end date/time of the input files are printed; can be
69 * dropped by the -q option at run time.
70 * - it is now possible to use Log/Stats files from V2_LOGGING configs.
71 * They must however not be mixed together (with each other).
72 * - the Log/Stats files are taken from config which is passed via
73 * Makefile at compile time. Variable to set is: newconfigdir. If the
74 * default config can't be read the default values are used
75 * (the config is optional!).
76 * Note: keyword/filename must be on the same line (no continuation).
77 * - -I option implemented to run with a different config file. In case
78 * the file can't be opened the run is aborted!
79 * - -q option implemented to run without environment report (default is
80 * FALSE: print the report).
81 * - -p option added to print protocol statistics: one for the packets
82 * and one for the errors encountered
83 * - reapplied patch by Scott Boyd <scott@futures.com> that I did not
87 * Revision 1.15 1994/04/07 21:47:11 kdburg
88 * printed 'no data avail' while there was data; layout chnaged
91 * Revision 1.14 1994/04/07 21:16:32 kdburg
92 * the layout of the protocol-used line within the LOGFILE changed
93 * from 1.04 to 1.05; both formats may be used together; output
94 * changed for packet report (columns adjusted)
96 * Revision 1.13 1994/04/04 10:04:35 kdburg
97 * cosmetic change to the packet-report (separator lines)
99 * Revision 1.12 1994/03/30 19:52:04 kdburg
100 * incorporated patch by Scott Boyd which was missing from this version
101 * of uurate.c. Left the comment in cronological order.
103 * Revision 1.11 1994/03/28 18:53:22 kdburg
104 * config not checked properly for 'logfile/statsfile' overwrites, bail-out
105 * possible; wrong file name written to log for statsfile when found
107 * Revision 1.10 1993/09/28 16:46:51 kdburg
108 * transmission failures denoted by: failed after ... in stats file
109 * have not been counted at all.
111 * Revision 1.9 1993/08/17 23:38:36 kdburg
112 * sometimes a line(site) was missing from the protocol stats due
113 * to a missing +; added option -d and -v reassing option -h to print
114 * the help; a zero was returned instead of a null-pointer by
117 * Revision 1.8 1993/07/03 06:58:55 kdburg
118 * empty input not handled properly; assigned some buffer to input; msg
119 * not displayed when no protocol data was available
121 * Revision 1.7 1993/06/27 10:31:53 kdburg
122 * rindex was replaced by strchr must be strrchr
124 * Revision 1.6 1993/06/26 06:59:18 kdburg
125 * switch hdr_done not reset at beginning of protocol report
127 * Revision 1.5 1993/06/25 22:22:30 kdburg
128 * changed rindex to strchr; if there is no NEWCONFIG defined take
131 * Revision 1.4 1993/06/25 20:04:07 kdburg
132 * added comment about -p option; inserted proto for rindex
134 * Revision 1.3 1993/06/25 19:31:14 kdburg
135 * major rework done; added protocol reports (normal/errors)
137 * Revision 1.2 1993/06/21 19:53:54 kdburg
141 char version[] = "@(#) Taylor UUCP Log File Summary Filter, Version 1.2.2";
142 static char rcsid[] = "$FreeBSD$";
143 #include <ctype.h> /* Character Classification */
146 /* uucp.h includes string.h or strings.h, no include here. */
149 #include <sys/param.h>
155 * Direction of Calling and Data Transmission
158 #define IN 0 /* Inbound */
159 #define OUT 1 /* Outbound */
164 #define MAXCOLS 8 /* report has this # of columns incl. 'name' */
165 #define MAXREP 6 /* number of reports available */
166 #define MAXFNAME 64 /* max input file name length incl. path*/
167 #define MAXDNAME 8 /* max display (Hostname) name length */
170 * Data structures used to collect information
174 int files; /* Files Transferred */
175 unsigned long bytes; /* Data Size Transferred*/
176 double time; /* Transmission Time */
181 int calls; /* Call Count */
182 int succs; /* Successful calls */
183 double connect_time; /* Connect Time Spent */
184 struct File_Stats flow[2]; /* Rcvd & Sent Data */
187 struct Execution_Command
189 struct Execution_Command *next;
190 char Commandname[64];
194 struct Protocol_Summary
196 struct Protocol_Summary *next;
201 long int pr_preceived;
208 long int pr_psizemin;
209 long int pr_psizemax;
214 struct Host_entry *next;
216 struct Execution_Command *cmds; /* Local Activities */
217 struct Phone_Call call[2]; /* In & Out Activities */
218 struct Protocol_Summary *proto;
221 struct Host_entry *hosts = NULL;
222 struct Host_entry *tot = NULL;
223 struct Host_entry *cur = NULL;
224 struct Execution_Command *cmd, *t_cmds = NULL;
225 struct Protocol_Summary *prot, *t_prot, *s_prot, *ss_prot = NULL;
230 extern int optind; /* GETOPT : Option Index */
231 extern char *optarg; /* GETOPT : Option Value */
233 extern pointer *calloc();
234 #endif /* HAVE_STDLIB_H */
236 * Default files to read. Taken from Taylor compile-time configuration.
237 * def_logs must look like an argvec, hence the dummy argv[0].
238 * Maybe later modified by scanning the config
241 static char *def_logs[3] = { NULL, NULL, NULL};
242 char *I_conf = NULL; /* points to config lib given by -I option */
243 char *D_conf = NULL; /* points to config lib from makefile */
244 char *Tlog = NULL; /* points to Log-file */
245 char *Tstat = NULL; /* points to Stats-file */
246 char Pgm_name[64]; /* our pgm-name */
247 char logline[BUFSIZ+1]; /* input area */
248 char noConf[] = "- not defined -";
249 char buff[16*BUFSIZ];
250 char sbuff[2*BUFSIZ];
253 * Boolean switches for various decisions
256 int p_done = FALSE; /* TRUE: start date/time of file printed */
257 int hdr_done = FALSE; /* TRUE: report header printed */
258 int show_files = FALSE; /* TRUE: -f option given */
259 int show_calls = FALSE; /* TRUE: -c option given */
260 int show_commands = FALSE; /* TRUE: -x option given */
261 int show_efficiency = FALSE; /* TRUE: -e option given */
262 int show_all = FALSE; /* TRUE: -t option given */
263 int show_proto = FALSE; /* TRUE: -p option given */
264 int use_stdin = FALSE; /* TRUE: -i option given */
265 int be_quiet = FALSE; /* TRUE: -q option given */
266 int have_files[2]; /* TRUE: [IN] or [OUT] files found */
267 int have_calls = FALSE; /* TRUE: in/out calls found */
268 int have_commands = FALSE; /* TRUE: found uuxqt records */
269 int have_proto = FALSE; /* TRUE: protocol data found */
270 int no_records = TRUE; /* FALSE: got one record from file */
276 static pointer *getmem(unsigned n);
277 static void inc_cmd(struct Execution_Command **, char *name);
278 static void fmtime(double sec, char *buf);
279 static void fmbytes(unsigned long n, char *buf);
281 static int chk_config(char *conf, int n, int type);
282 static void hdrprt(char c, int bot);
283 struct Protocol_Summary *prot_sum(struct Protocol_Summary **, char *, int);
295 char *p, *s, *stt, *flq = NULL;
296 char Hostname[MAXHOSTNAMELEN]; /* def taken from <sys/param.h> */
297 char Filename[15]; /* filename to be printed */
298 char in_date[14]; /* holds the date info of record read*/
299 char in_time[14]; /* holds the time info of record read */
300 char dt_info[31]; /* holds the date info from the last record read */
302 int sent, called = IN;
303 int report = 0; /* if <= 0 give msg that no report was avail. */
306 /* --------------------------------------------------------------------
308 * --------------------------------------------------------------------
312 have_files[IN]= have_files[OUT]= FALSE;
313 setvbuf(stdout,sbuff,_IOFBF,sizeof(sbuff));
316 * get how we've been called isolate the name from the path
319 if ((stt = strrchr(argv[0],'/')) != NULL)
320 strcpy(Pgm_name,++stt);
322 strcpy(Pgm_name,argv[0]);
323 def_logs[0] = Pgm_name;
326 * I wish the compiler had the #error directive!
329 #if !HAVE_TAYLOR_LOGGING && !HAVE_V2_LOGGING
330 fprintf(stderr,"\a%s: (E) %s\n",Pgm_name,"Your config of Taylor UUCP is not yet supported.");
331 fprintf(stderr,"%s: (E) %s\n",Pgm_name,"Current support is for V2 or TAYLOR logging only.");
332 puts(" Run aborted due to errors\n")
337 * get some mem to store the default config name (def's are in
341 if (sizeof(NEWCONFIGLIB) > 1) /* defined at compile time */
343 D_conf = (char *)getmem((sizeof(NEWCONFIGLIB) + sizeof("/config")));
344 strcpy(D_conf,NEWCONFIGLIB); /* passed by makefile */
345 strcat(D_conf,"/config");
347 Tlog = (char *)getmem(sizeof(LOGFILE));
348 Tstat = (char *)getmem(sizeof(STATFILE));
353 * Process the command line arguments
356 while((c = getopt(argc, argv, "I:s:cfdexaitphv")) != EOF)
363 strcpy(Hostname, optarg);
370 printf("%s: (I) config-file default: %s\n",Pgm_name,D_conf);
378 show_commands = TRUE;
382 show_efficiency = TRUE;
386 show_calls = show_files = show_commands = show_efficiency = TRUE;
401 I_conf = (char *)getmem(sizeof(optarg));
408 printf("%s\n",rcsid);
414 if (report == 0) /* no options given */
415 ++report; /* at least summary can be printed */
417 hdrprt('i',0); /* print header for environment info */
420 * Adjust argv and argc to account for the args processed above.
423 argc -= (optind - 1);
424 argv += (optind - 1);
427 * If further args present, Assume rest are logfiles for us to process
428 * which should be given in pairs (log plus stat) otherwise the results may
429 * not come out as expected! If no further args are present take input from
430 * Log and Stat files provided in the compilation environment of Taylor UUCP.
431 * If -i was given, Log already points to stdin and no file args are accepted.
434 if (use_stdin) /* If -i, read from stdin */
436 if (argc != 1) /* No file arguments allowed */
438 fprintf(stderr,"\a%s: (E) %s\n",Pgm_name,
439 "it's not posssible to give file args with '-i'");
447 puts(" Input from stdin; no other files will be used\n");
452 if (argc != 1) /* file arguments are present */
455 puts(" No defaults used; will use passed file arguments\n");
457 else /* Read from current logs */
459 def_logs[1] = Tlog; /* prime the */
460 def_logs[2] = Tstat; /* file names */
462 printf(" Config for this run: ");
468 printf("%s\n",I_conf);
469 if (0 != (chk_config(I_conf,be_quiet,junk)))
476 junk = 1; /* indicate default (compiled) config */
478 printf("%s\n",D_conf);
479 chk_config(D_conf,be_quiet,junk);
483 printf("%s\n",noConf);
485 def_logs[1] = Tlog; /* final setting of */
486 def_logs[2] = Tstat; /* file names */
487 argv = def_logs; /* Bash argvec to log/stat files */
488 argc = sizeof(def_logs) / sizeof(def_logs[0]);
492 /* --------------------------------------------------------------------
493 * MAIN LOGFILE PROCESSING LOOP
494 * --------------------------------------------------------------------
499 if (argc < 3 && ! be_quiet)
501 puts(" (W) there is only one input file!");
502 puts(" (W) some reports may not be printed");
505 hdrprt('d',0); /* give subheaderline */
510 if (!use_stdin && (Log = fopen(argv[1], "r")) == NULL)
515 setvbuf(Log,buff,_IOFBF,sizeof(buff));
516 if ((flq = strrchr(argv[1], '/')) == NULL)
517 strncpy(Filename,argv[1],sizeof(Filename)-1);
519 strncpy(Filename,++flq,sizeof(Filename)-1);
521 strcpy(in_date," n/a");
522 strcpy(in_time," n/a");
523 p_done = FALSE; /* no info printed yet */
524 no_records = TRUE; /* not read any record yet */
527 * Read each line of the logfile and collect information
530 while (fgets(logline, sizeof(logline), Log))
533 * The host name of the other end of the connection is
534 * always the second field of the log line, whether we
535 * are reading a Log file or a Stats file. Set 'p' to
536 * point to the second field, null-terminated. Skip
537 * the line if something is funny. V2 and Taylor ar identical
538 * up to this part. Put out the start/end date of the files read;
541 if (NULL == (p = strchr(logline, ' ')))
543 no_records = FALSE; /* got one (usable) record at least */
546 if (NULL != (stt = strchr(p, '(')))
548 if (! p_done && ! use_stdin && ! be_quiet)
551 #if HAVE_TAYLOR_LOGGING
552 sscanf(++stt,"%s%*c%[^.]",in_date,in_time);
553 #endif /* HAVE_TAYLOR_LOGGING */
556 sscanf(++stt,"%[^-]%*c%[1234567890:]",in_date,in_time);
557 #endif /* HAVE_V2_LOGGING */
559 printf(" %-14s %10s %8s",Filename, in_date, in_time);
560 strcpy(in_date," n/a"); /* reset to default */
561 strcpy(in_time," n/a");
566 if (! use_stdin && ! be_quiet) /* save for last time stamp prt. */
567 strncpy(dt_info,++stt,sizeof(dt_info)-1);
571 if (NULL != (s = strchr(p, ' ')))
578 * Skip this line if we got -s <host> and
579 * this line does not contain that host name.
580 * Don't skip the `incoming call' line with the system name `-'.
583 if (Hostname[0] != '\0')
584 if ( (p[0] != '-' || p[1] != '\0') && 0 != strcmp(p, Hostname) )
588 * We are within a call block now. If this line is a file
589 * transfer record, determine the direction. If not then
590 * skip the line if it is not interesting.
593 if ((s = strchr(++s, ')')) == NULL)
596 #if ! HAVE_TAYLOR_LOGGING
598 if ((strncmp(s,") (",3)) == 0) /* are we in stats file ?) */
599 if ((s = strchr(++s, ')')) == NULL)
600 continue; /* yes but strange layout */
601 #endif /* HAVE_V2_LOGGING */
602 #endif /* ! HAVE_TAYLOR_LOGGING */
604 logmsg = s + 2; /* Message is 2 characters after ')' */
605 if ((0 != strncmp(logmsg, "Call complete", 13)) &&
606 (0 != strncmp(logmsg, "Calling system", 14)) &&
607 (0 != strncmp(logmsg, "Incoming call", 13)) &&
608 (0 != strncmp(logmsg, "Handshake successful", 20)) &&
609 (0 != strncmp(logmsg, "Retry time not", 14)) &&
610 (0 != strncmp(logmsg, "ERROR: All matching ports", 25)) &&
611 (0 != strncmp(logmsg, "Executing", 9)) &&
612 (0 != strncmp(logmsg, "Protocol ", 9)) &&
613 (0 != strncmp(logmsg, "sent ", 5)) &&
614 (0 != strncmp(logmsg, "received ", 9)) &&
615 (0 != strncmp(logmsg, "failed after ", 13)) &&
616 (0 != strncmp(logmsg, "Errors: ", 8)))
620 * Find the Host_entry for this host, or create a new
621 * one and link it on to the list.
624 if ((cur == NULL) || (0 != strcmp(p, cur->Hostname)))
626 struct Host_entry *e, *last;
628 for (e= cur= hosts; cur != NULL ; e= cur, cur= cur->next)
629 if (0 == strcmp(cur->Hostname, p))
633 cur= (struct Host_entry *)getmem(sizeof(*hosts));
634 strcpy(cur->Hostname, p);
641 if (strcmp(e->Hostname, cur->Hostname) <= 0) {
642 if (e->next == NULL) {
657 } /* while (e != NULL) */
658 } /* hosts == NULL */
663 * OK, if this is a uuxqt record, find the Execution_Command
664 * structure for the command being executed, or create a new
665 * one. Then count an execution of this command.
669 if (0 == strncmp(logmsg, "Executing", 9))
671 if (NULL == (p = strchr(logmsg, '(')))
673 if ((s = strpbrk(++p, " )")) == NULL)
676 inc_cmd(&cur->cmds, p);
678 have_commands = TRUE;
683 * Count start of outgoing call.
686 if ((0 == strncmp(logmsg, "Calling system", 14)) ||
687 (0 == strncmp(logmsg, "Retry time not", 14)) ||
688 (0 == strncmp(logmsg, "ERROR: All matching ports", 25)))
691 cur->call[OUT].calls++;
693 s_prot = NULL; /* destroy pointer to protocol */
698 * Count start of incoming call.
701 if (0 == strncmp(logmsg, "Incoming call", 13))
704 s_prot = NULL; /* destroy pointer to protocol */
709 * On an incoming call, get system name from the second line.
710 * Get protocol type and size/window too
713 if (0 == strncmp(logmsg, "Handshake successful", 20))
716 cur->call[IN].calls++;
718 s_prot = NULL; /* destroy pointer to protocol */
719 if (NULL == (p = strchr(logmsg, '(')))
721 if (0 == strncmp(p, "(protocol ", 10))
723 if (NULL == (p = strchr(p, '\'')))
725 ss_prot = prot_sum(&cur->proto, ++p, 1);
726 s_prot = prot_sum(&t_prot, p, 1);
732 * check protocol type and get stats
736 if (0 == strncmp(logmsg, "Protocol ", 9))
738 s_prot = NULL; /* destroy pointer to protocol */
739 if (NULL == (p = strchr(logmsg, '\'')))
741 ss_prot = prot_sum(&cur->proto, ++p, 2);
742 s_prot = prot_sum(&t_prot, p, 2);
747 * check protocol errors. Unfortunately the line does not contain
748 * the used protocol, so if any previous line did contain that
749 * information and we did process that line we will save the pointer
750 * to that particular segment into s_prot. If this pointer is not set
751 * the error info is lost for we don't know where to store.
755 if ((0 == strncmp(logmsg, "Errors: header", 14)) && s_prot != NULL)
758 sscanf(logmsg,"%*s %*s %d%*c%*s %d%*c%*s %d%*c%*s %*s%*c %d",&i1,&i2,&i3,&i4);
759 ss_prot->pr_eheader += i1;
760 ss_prot->pr_echksum += i2;
761 ss_prot->pr_eorder += i3;
762 ss_prot->pr_ereject += i4;
763 s_prot->pr_eheader += i1;
764 s_prot->pr_echksum += i2;
765 s_prot->pr_eorder += i3;
766 s_prot->pr_ereject += i4;
772 * Handle end of call. Pick up the connect time.
773 * position is on the closing paren of date/time info
777 if (0 == strncmp(logmsg, "Call complete", 13))
779 cur->call[called].succs++;
780 s_prot = NULL; /* destroy pointer to protocol */
781 if (NULL == (s = strchr(logmsg, '(')))
783 cur->call[called].connect_time += atof(s+1);
788 * We are definitely in a Stats file now.
789 * If we reached here, this must have been a file transfer
790 * record. Count it in the field corresponding to the
791 * direction of the transfer. Count bytes transferred and
792 * the time to transfer as well.
793 * Position within the record is at the word 'received' or 'sent'
794 * depending on the direction.
797 sent = IN; /* give it an initial value */
798 if (0 == strncmp(logmsg, "failed after ",13))
799 logmsg += 13; /* the transmission failed for any reason */
800 /* so advance pointer */
801 if (0 == strncmp(logmsg, "sent", 4))
803 else if (0 == strncmp(logmsg, "received", 8))
805 have_files[sent] = TRUE;
806 cur->call[called].flow[sent].files++;
807 if (NULL == (s = strchr(logmsg, ' '))) /* point past keyword */
808 continue; /* nothing follows */
809 /* we should be at the bytes column now*/
810 #if HAVE_TAYLOR_LOGGING
811 cur->call[called].flow[sent].bytes += atol(++s);
812 #endif /* HAVE_TAYLOR_LOGGING */
814 if (NULL == (s = strpbrk(s, "0123456789"))) /* point to # bytes */
816 cur->call[called].flow[sent].bytes += atol(s);
817 #endif /* HAVE_V2_LOGGING */
818 if (NULL == (s = strchr(s, ' '))) /* point past # of bytes */
820 if (NULL == (s = strpbrk(s, "0123456789"))) /* point to # of seconds */
822 cur->call[called].flow[sent].time += atof(s);
824 } /* end of while (fgets(logline...)) */
826 if (stt != NULL && ! use_stdin && ! be_quiet && ! no_records)
829 #if HAVE_TAYLOR_LOGGING
830 sscanf(dt_info,"%s%*c%[^.]",in_date,in_time);
831 #endif /* HAVE_TAYLOR_LOGGING */
834 sscanf(dt_info,"%[^-]%*c%[1234567890:]",in_date,in_time);
835 #endif /* HAVE_V2_LOGGING */
837 printf(" %10s %8s\n",in_date, in_time);
842 if (0 != ferror(Log))
845 printf(" %-14s data is incomplete; read error"," ");
847 fprintf(stderr,"%s (W) data is incomplete; read error on %s\n",
852 if (! be_quiet && no_records)
853 printf(" %-14s %10s\n",Filename, " is empty ");
860 } /* end of while (for (argv ....) */
863 * do we have *any* data ?
868 puts("\n(I) Sorry! No data is available for any requested report\n");
873 * truncate hostname, alloc the structure holding the totals and
874 * collect the totals data
877 for (cur = hosts; cur != NULL;cur = cur->next)
879 cur->Hostname[MAXDNAME] = '\0';
880 if (cur->next == NULL) /* last so will have to alloc totals */
882 cur->next = (struct Host_entry *)getmem(sizeof(*hosts));
883 strcpy(cur->next->Hostname,"Totals");
885 for (cur = hosts; cur != NULL; cur = cur->next)
887 if (cur->next != NULL) /* don't count totals to totals */
889 tot->call[IN].flow[IN].bytes += cur->call[IN].flow[IN].bytes;
890 tot->call[OUT].flow[IN].bytes += cur->call[OUT].flow[IN].bytes;
891 tot->call[IN].flow[OUT].bytes += cur->call[IN].flow[OUT].bytes;
892 tot->call[OUT].flow[OUT].bytes += cur->call[OUT].flow[OUT].bytes;
893 tot->call[IN].flow[IN].time += cur->call[IN].flow[IN].time;
894 tot->call[OUT].flow[IN].time += cur->call[OUT].flow[IN].time;
895 tot->call[IN].flow[OUT].time += cur->call[IN].flow[OUT].time;
896 tot->call[OUT].flow[OUT].time += cur->call[OUT].flow[OUT].time;
897 tot->call[IN].flow[IN].files += cur->call[IN].flow[IN].files;
898 tot->call[OUT].flow[IN].files += cur->call[OUT].flow[IN].files;
899 tot->call[IN].flow[OUT].files += cur->call[IN].flow[OUT].files;
900 tot->call[OUT].flow[OUT].files += cur->call[OUT].flow[OUT].files;
901 tot->call[OUT].succs += cur->call[OUT].succs;
902 tot->call[OUT].calls += cur->call[OUT].calls;
903 tot->call[OUT].connect_time += cur->call[OUT].connect_time;
904 tot->call[IN].succs += cur->call[IN].succs;
905 tot->call[IN].calls += cur->call[IN].calls;
906 tot->call[IN].connect_time += cur->call[IN].connect_time;
909 break; /* totals is last in Host_Entry */
923 /* ------------------------------------------------------------------
925 * Summary report only when no other report except option -t is given
927 * I know, this code could be tightened (rbd)...
928 * ------------------------------------------------------------------
931 if ( !(show_calls || show_files ||
932 show_efficiency || show_commands || show_proto) || show_all)
934 if (have_calls || have_files[IN] || have_files[OUT])
936 char t1[32], t2[32], t3[32], t4[32], t5[32];
937 long ib, ob, b, rf, sf;
938 double it, ot, ir, or;
941 for (cur = hosts; cur != NULL; cur = cur->next)
943 ib = (cur->call[IN].flow[IN].bytes +
944 cur->call[OUT].flow[IN].bytes);
947 ob = (cur->call[IN].flow[OUT].bytes +
948 cur->call[OUT].flow[OUT].bytes);
951 /* Don't print null-lines. */
952 if (( b= ib+ob ) == 0 )
954 /* Don't print the header twice. */
957 hdrprt('s',0); /* print the header line(s) */
963 it = cur->call[IN].flow[IN].time +
964 cur->call[OUT].flow[IN].time;
967 ot = cur->call[IN].flow[OUT].time +
968 cur->call[OUT].flow[OUT].time;
971 rf = cur->call[IN].flow[IN].files +
972 cur->call[OUT].flow[IN].files;
974 sf = cur->call[IN].flow[OUT].files +
975 cur->call[OUT].flow[OUT].files;
977 ir = (it == 0.0) ? 0.0 : (ib / it);
978 or = (ot == 0.0) ? 0.0 : (ob / ot);
980 if (cur->next == NULL) /* totals line reached ? */
981 hdrprt('s',1); /* print the separator line */
983 printf("%-8s %4d %4d %9s %9s %9s %9s %9s %5.0f %5.0f\n",
984 cur->Hostname, rf, sf,
990 puts("\n(I) No data found to print Compact summary report");
995 puts("\n(I) No data available for Compact summary report");
1000 /* ------------------------------------------------------------------
1001 * Protocol statistics report
1002 * ------------------------------------------------------------------
1005 if (show_proto || show_all)
1009 /* --------------------- */
1010 /* protocol packet report */
1011 /* --------------------- */
1015 for (cur = hosts; cur != NULL; cur = cur->next)
1017 type = cur->Hostname;
1018 if (cur->next == NULL)
1021 puts("-------------------------------------------------------------------");
1022 cur->proto = t_prot;
1024 for (prot = cur->proto; prot != NULL; prot = prot->next)
1028 hdrprt('p',0); /* print the header line(s) */
1031 printf("%-8s %3s %4d %4d %5d %4d %10d %7d %10d\n",
1032 type == NULL ? " ":cur->Hostname,
1040 prot->pr_preceived);
1045 puts("\n(I) No data found to print Protocol packet report");
1047 /* --------------------- */
1048 /* protocol error report */
1049 /* --------------------- */
1055 for (cur = hosts; cur != NULL; cur = cur->next)
1057 type = cur->Hostname;
1058 if (cur->next == NULL)
1061 puts("--------------------------------------------------------------");
1062 cur->proto = t_prot;
1065 for (prot = cur->proto; prot != NULL; prot = prot->next)
1067 if ((prot->pr_eheader + prot->pr_echksum +
1068 prot->pr_eorder + prot->pr_ereject) != 0)
1072 hdrprt('p',1); /* print the header line(s) */
1075 printf("%-8s %3s %11d %11d %11d %11d\n",
1076 type == NULL ? " ":cur->Hostname,
1088 puts("\n(I) No data found to print Protocol error report");
1092 puts("\n(I) No data available for Protocol reports");
1097 /* ------------------------------------------------------------------
1098 * Call statistics report
1099 * ------------------------------------------------------------------
1102 if (show_calls || show_all)
1106 char t1[32], t2[32];
1109 for (cur = hosts; cur != NULL; cur = cur->next)
1111 if (cur->next == NULL)
1114 hdrprt('c',1); /* print the separator line */
1118 /* Don't print null-lines on deatail lines */
1119 if ( cur->call[OUT].calls + cur->call[IN].calls == 0 )
1122 /* Don't print the header twice. */
1125 hdrprt('c',0); /* print the header line(s) */
1129 if ( cur->call[OUT].calls > 0 || cur->next == NULL)
1131 fmtime(cur->call[OUT].connect_time, t1);
1132 printf( " %-8s %7d %7d %7d %9s",
1134 cur->call[OUT].succs,
1135 cur->call[OUT].calls - cur->call[OUT].succs,
1136 cur->call[OUT].calls,
1141 printf( " %-42s", cur->Hostname );
1143 if ( cur->call[IN].calls > 0 || cur->next == NULL )
1145 fmtime(cur->call[IN].connect_time, t2);
1146 printf( " %7d %7d %7d %9s",
1147 cur->call[IN].succs,
1148 cur->call[IN].calls - cur->call[IN].succs,
1149 cur->call[IN].calls,
1156 puts("\n(I) No data found to print Call statistics report");
1161 puts("\n(I) No data available for Call statistics report");
1166 /* ------------------------------------------------------------------
1167 * File statistics report
1168 * ------------------------------------------------------------------
1171 if (show_files || show_all)
1173 if (have_files[IN] || have_files[OUT])
1175 char t1[32], t2[32];
1176 double rate = 0, time = 0;
1181 for (cur = hosts; cur != NULL; cur = cur->next)
1184 for (sent= IN; sent <= OUT; ++sent)
1186 b = cur->call[IN].flow[sent].bytes +
1187 cur->call[OUT].flow[sent].bytes;
1188 time = cur->call[IN].flow[sent].time +
1189 cur->call[OUT].flow[sent].time;
1191 /* Don't print null-lines on detail lines. */
1192 if ( (b != 0 && time != 0.0) || cur->next == NULL)
1194 /* Don't print the header twice. */
1197 hdrprt('f',0); /* print the header line(s) */
1201 rate = (cur->call[IN].flow[sent].bytes +
1202 cur->call[OUT].flow[sent].bytes) / time;
1203 fmtime((cur->call[IN].flow[sent].time +
1204 cur->call[OUT].flow[sent].time), t2);
1206 if (lineOut == 0) /* first half not printed yet ? */
1208 if (cur->next == NULL) /* totals line ? */
1209 hdrprt('f',1); /* print the separator line */
1210 printf(" %-8s", cur->Hostname);
1211 if (sent == OUT) /* can't happen whith totals line */
1212 printf("%34s", " ");
1215 printf(" %5d %11s %9s %5.0f",
1216 cur->call[IN].flow[sent].files +
1217 cur->call[OUT].flow[sent].files,
1221 } /* end: for (sent ... ) */
1224 } /* end: for (cur= ... ) */
1227 puts("\n(I) No data found to print File statistics report");
1232 puts("\n(I) No data available for File statistics report");
1237 /* ------------------------------------------------------------------
1239 * ------------------------------------------------------------------
1242 if (show_efficiency || show_all)
1244 if (have_files[IN] || have_files[OUT])
1246 char t1[32], t2[32], t3[32];
1250 for (cur = hosts; cur != NULL; cur = cur->next)
1252 /* Don't print null-lines. */
1253 if ( 0 == cur->call[IN].flow[IN].files +
1254 cur->call[IN].flow[OUT].files +
1255 cur->call[OUT].flow[IN].files +
1256 cur->call[OUT].flow[OUT].files ||
1257 0.0 == (total= cur->call[IN].connect_time +
1258 cur->call[OUT].connect_time))
1265 hdrprt('e',0); /* print the header line(s) */
1269 flow = cur->call[IN].flow[IN].time +
1270 cur->call[IN].flow[OUT].time +
1271 cur->call[OUT].flow[IN].time +
1272 cur->call[OUT].flow[OUT].time;
1275 fmtime(total-flow, t3);
1277 if (cur->next == NULL)
1278 hdrprt('e',1); /* print the separator line */
1280 printf(" %-8s %10s %10s %10s %7.2f\n",
1281 cur->Hostname, t1, t2, t3,
1282 flow >= total ? 100.0: flow*100.0/total);
1283 } /* end: for (cur= .. */
1286 puts("\n(I) No data found to print Efficiency report");
1291 puts("\n(I) No data available for Efficiency report");
1296 /* ------------------------------------------------------------------
1297 * Command execution report
1298 * ------------------------------------------------------------------
1301 if (show_commands || show_all)
1305 int ncmds, i, match;
1308 * layout the header line. The column's header is the command name
1312 for (ncmds= 0, cmd= t_cmds;
1313 cmd != NULL && ncmds <= MAXCOLS-1;
1314 ncmds++, cmd= cmd->next)
1318 puts("\nCommand executions:");
1319 puts("-------------------");
1321 fputs(" site ", stdout);
1324 printf(" %7s", cmd->Commandname);
1328 puts("\n(I) No data found to print Command execution report");
1332 fputs("\n --------", stdout);
1333 for (i= 0; i<ncmds; i++)
1334 fputs(" ------", stdout);
1338 * print out the number of executions for each host/command
1341 for (cur= hosts; cur != NULL; cur= cur->next)
1343 if (cur->next == NULL)
1346 /* Don't print null-lines. */
1348 if (cur->cmds == NULL)
1351 printf(" %-8s", cur->Hostname);
1352 for (cmd= t_cmds; cmd != NULL; cmd= cmd->next)
1354 struct Execution_Command *ec;
1356 for(ec= cur->cmds; ec != NULL; ec= ec->next)
1358 if ( 0 == strcmp(cmd->Commandname, ec->Commandname) )
1360 printf(" %7d", ec->count);
1366 printf("%8s"," "); /* blank out column */
1372 * print the totals line
1375 fputs(" --------", stdout);
1376 for (i= 0; i<ncmds; i++)
1377 fputs("--------", stdout);
1378 printf("\n %-8s", cur->Hostname);
1379 for (cmd= t_cmds; cmd != NULL; cmd= cmd->next)
1381 printf(" %7d", cmd->count);
1388 puts("\n(I) No data available for Command execution report");
1392 if (report <= 0 ) /* any reports ? */
1394 puts("\n(I) Sorry! No data is available for any requested report\n");
1398 puts("\n(I) End of reports\n");
1402 /* ------------------------------------------------------------------
1404 * ------------------------------------------------------------------
1407 /* ------------------------------------------------------------------
1409 * ------------------------------------------------------------------
1414 fprintf(stderr,"Usage uurate [-acdefhiptvx] [-s hostname] [-I config file] [logfile(s) ... logfile(s)]\n");
1415 fprintf(stderr,"where:\t-a\tPrint reports c,e,f,x\n");
1416 fprintf(stderr,"\t-c\tReport call statistics\n");
1417 fprintf(stderr,"\t-d\tPrint the name of the default config file\n");
1418 fprintf(stderr,"\t-e\tReport efficiency statistics\n");
1419 fprintf(stderr,"\t-f\tReport file transfer statistics\n");
1420 fprintf(stderr,"\t-h\tPrint this help\n");
1421 fprintf(stderr,"\t-i\tRead log info from standard input\n");
1422 fprintf(stderr,"\t-p\tReport protocol statistics\n");
1423 fprintf(stderr,"\t-t\tAll available reports plus compact summary report\n");
1424 fprintf(stderr,"\t-v\tPrint version number\n");
1425 fprintf(stderr,"\t-x\tReport command execution statistics\n");
1426 fprintf(stderr,"\t-s host\tReport activities involving HOST only\n");
1427 fprintf(stderr,"\t-I config Use config instead of standard config file\n");
1428 fprintf(stderr,"If no report options given, a compact summary report is printed.\n");
1429 fprintf(stderr,"log files should be given as pairs that is Log/Stats ... .\n");
1430 fprintf(stderr,"If neither -i nor logfiles given, those names found in config will be used\n");
1435 /* ------------------------------------------------------------------
1436 * getmem - get some memory
1437 * ------------------------------------------------------------------
1440 static pointer *getmem(n)
1445 if( NULL== (p= calloc(1, n)) )
1447 fprintf(stderr,"\a%s (C) %s\n",Pgm_name, "out of memory\n");
1453 /* ------------------------------------------------------------------
1454 * inc_cmd - increment command count
1455 * ------------------------------------------------------------------
1458 static void inc_cmd(cmds, name)
1459 struct Execution_Command **cmds;
1463 struct Execution_Command *cmd, *ec;
1465 for (ec = cmd = *cmds; cmd != NULL; ec= cmd, cmd= cmd->next, cnt++)
1466 if ( (0 == strcmp(cmd->Commandname, name)) ||
1467 (0 == strcmp(cmd->Commandname, "Misc.")) )
1471 cmd= (struct Execution_Command *)getmem(sizeof(*cmd));
1472 if (cnt <= MAXCOLS-1) /* first col prints site name therefore < max-1 */
1474 strcpy(cmd->Commandname, name);
1482 strcpy(ec->Commandname, "Misc."); /* reached high-water-mark */
1483 cmd = ec; /* backtrack */
1490 /* ------------------------------------------------------------------
1491 * prot_sum - collect protocol data
1492 * ------------------------------------------------------------------
1495 struct Protocol_Summary *
1496 prot_sum(proto, ptype, ind)
1497 struct Protocol_Summary **proto;
1503 struct Protocol_Summary *cur, *first;
1505 for (first = cur = *proto; cur != NULL; first= cur, cur= cur->next, cnt++)
1507 if ( (0 == strncmp(cur->type, ptype,strlen(cur->type))))
1512 cur= (struct Protocol_Summary *)getmem(sizeof(*cur));
1513 sscanf(ptype,"%[^\' ]3",cur->type);
1515 first = *proto = cur;
1519 if (NULL == (ptype = strchr(ptype, ' ')))
1526 case 1: /* used protocol line */
1528 * uucp-1.04 format: .... packet size ssss window ww)
1529 * uucp-1.05 format: .... remote packet/window ssss/ww local ssss/ww)
1530 * (the remote packet/window will be used!)
1533 i1 = i2 = 0; /* reset */
1535 if (NULL == (strchr(ptype, '/')))
1536 sscanf(ptype,"%*s %*s %d %*s %d",&i1,&i2);
1538 sscanf(ptype,"%*s %*s %d/%d",&i1,&i2);
1540 if (i1 > cur->pr_psizemax)
1541 cur->pr_psizemax = i1;
1542 if (i1 < cur->pr_psizemin || cur->pr_psizemin == 0)
1543 cur->pr_psizemin = i1;
1545 if (i2 > cur->pr_pwinmax)
1546 cur->pr_pwinmax = i2;
1547 if (i2 < cur->pr_pwinmin || cur->pr_pwinmin == 0)
1548 cur->pr_pwinmin = i2;
1550 case 2: /* protocol statistics line */
1551 i1 = i2 = i3 = 0; /* reset */
1552 sscanf(ptype,"%*s %*s %d%*c %*s %d%*c %*s %d",&i1,&i2,&i3);
1553 cur->pr_psent += i1;
1554 cur->pr_present += i2;
1555 cur->pr_preceived += i3;
1562 /* ------------------------------------------------------------------
1563 * fmtime() - Format time in hours & minutes & seconds;
1564 * ------------------------------------------------------------------
1567 static void fmtime(dsec, buf)
1571 long hrs, min, lsec;
1578 lsec = fmod(dsec+0.5, 60L); /* round to the next full second */
1580 min = ((long)dsec / 60L) % 60L;
1583 sprintf(buf,"%6s%2ld"," ",lsec);
1585 sprintf(buf,"%3s%2ld:%02ld"," ",min,lsec);
1587 sprintf(buf,"%2ld:%02ld:%02ld",hrs,min,lsec);
1591 /* ------------------------------------------------------------------
1592 * fmbytes - Format size in bytes
1593 * ------------------------------------------------------------------
1596 static void fmbytes(n, buf)
1602 strcpy( buf, "0.0" );
1605 sprintf(buf, "%.1f", (double)n / 1000.0); /* Display in Kilobytes */
1609 /* ------------------------------------------------------------------
1610 * chk_config - Read the config file
1611 * check on keywords: logfile and statfile. When found override
1612 * the corresponding default
1613 * ------------------------------------------------------------------
1616 int chk_config(char *T_conf,int be_quiet, int type)
1620 char name[MAXPATHLEN+1];
1626 if ((Conf = fopen(T_conf, "r")) == NULL)
1630 puts(" Could not open config");
1633 puts(" The run will be aborted\n");
1639 fprintf(stderr,"%s (E) %s %s \n",Pgm_name,
1640 "could not open config:",
1643 fprintf(stderr,"%s (W) defaults used for all files\n",
1647 fprintf(stderr,"%s (C) ended due to errors\n",
1655 while (fgets(logline, sizeof(logline), Conf))
1657 if (logline[0] == '#')
1659 sscanf(logline,"%8s %s",keywrd,name);
1660 if (0 == strncmp(keywrd,"logfile",7))
1663 for (i=0;(i<=MAXPATHLEN && *pos1 != '\0');pos1++,pos2++,i++)
1665 if (*pos1 == '#') /* name immed followed by comment */
1667 if (*pos1 == '\\') /* quoted comment (filename has #) */
1669 ++pos1; /* skip escape char */
1670 if (*pos1 != '#') /* continuation ? */
1672 puts(" Config error:");
1673 puts(" Found filename continuation; bailing out\n");
1677 *pos2 = *pos1; /* move char */
1679 *pos2 = '\0'; /* terminate string */
1680 Tlog = (char *)getmem(strlen(name)+1);
1683 printf(" logfile used: %s\n",Tlog);
1685 if (statf) /* statsfile still to come ? */
1686 break; /* no finished */
1690 if (0 == strncmp(keywrd,"statfile",8))
1693 for (i=0;(i<=MAXPATHLEN && *pos1 != '\0');pos1++,pos2++,i++)
1695 if (*pos1 == '#') /* name immed followed by comment */
1697 if (*pos1 == '\\') /* quoted comment (filename has #) */
1699 ++pos1; /* skip escape char */
1700 if (*pos1 != '#') /* continuation ? */
1702 puts(" Config error:");
1703 puts(" Found filename continuation; bailing out\n");
1707 *pos2 = *pos1; /* move char */
1709 *pos2 = '\0'; /* terminate string */
1710 Tstat = (char *)getmem(strlen(name)+1);
1713 printf(" statfile used: %s\n",Tstat);
1715 if (logf) /* logfile still to come ? */
1716 break; /* no finished */
1726 puts(" logfile used: - default -");
1728 puts(" statfile used: - default -");
1735 /* ------------------------------------------------------------------
1736 * hdrprt - Print Header/Trailer lines (constant data)
1737 * ------------------------------------------------------------------
1740 static void hdrprt(char head, int bot)
1744 case('s'): /* standard summary report */
1747 puts("\nCompact summary:");
1748 puts("----------------");
1750 Name of + Files + +------- Bytes/1000 --------+ +------ Time -----+ + Avg CPS +\n\
1751 site in out inbound outbound total inbound outbound in out\n\
1752 -------- ---- ---- --------- --------- --------- --------- --------- ----- -----");
1756 --------------------------------------------------------------------------------");
1760 case('f'): /* file statistic report */
1763 puts("\nFile statistics:");
1764 puts("----------------");
1765 puts(" Name of +----------- Inbound -----------+ +---------- Outbound -----------+");
1766 puts(" site files Bytes/1000 xfr time B/sec files Bytes/1000 xfr time B/sec");
1767 puts(" -------- ----- ----------- --------- ----- ----- ----------- --------- -----");
1771 ----------------------------------------------------------------------------");
1775 case('c'): /* calls statistic report */
1778 puts("\nCall statistics:");
1779 puts("----------------");
1780 puts(" Name of +------- Outbound Calls -------+ +-------- Inbound Calls ------+");
1781 puts(" site succ. failed total time succ. failed total time");
1782 puts(" -------- ------ ------ ------ --------- ------ ------ ------ ---------");
1786 ----------------------------------------------------------------------------");
1790 case('e'): /* efficiency statistic report */
1793 puts("\nEfficiency:");
1794 puts("-----------");
1795 puts(" Name of +------ Times inbound/outbound -------+");
1796 puts(" site connected xfr time overhead eff. %");
1797 puts(" -------- --------- --------- --------- ------");
1800 puts(" -------------------------------------------------");
1803 case('i'): /* Environment information */
1806 puts("\nEnvironment Information:");
1807 puts("------------------------");
1808 printf(" Default config: %s\n",D_conf == NULL ?
1810 printf(" Default logfile: %s\n",Tlog);
1811 printf(" Default statfile: %s\n\n",Tstat);
1815 case('d'): /* Date/time coverage */
1818 puts("\n Date coverage of input files:");
1819 puts(" Name of +----- Start -----+ +------ End ------+");
1820 puts(" file date time date time");
1821 puts(" -------- ---------- -------- ---------- --------");
1825 case('p'): /* Protocol stats */
1828 puts("\nProtocol packet report:");
1829 puts("-----------------------");
1830 puts(" +------- protocol -----+ +--------- Packets ----------+");
1831 puts("Name of packet window ");
1832 puts("site typ min max min max sent resent received");
1833 puts("-------- --- ---- ---- ---- ---- ----------- ------- ----------");
1837 puts("\nProtocol error report:");
1838 puts("----------------------");
1839 puts("Name of +----------------- Error Types --------------------+");
1840 puts("site typ header checksum order rem-reject");
1841 puts("-------- --- ----------- ---------- ----------- ----------");
1848 puts("\nNo header for this report defined:");