|
|
1.1 root 1: /*
2: * @(#)uurate.c 1.2 - Thu Sep 3 18:32:46 1992
3: *
4: * This program digests log and stats files in the "Taylor" format
5: * and outputs various statistical data to standard out.
6: *
7: * Author:
8: * Bob Denny ([email protected])
9: * Fri Feb 7 13:38:36 1992
10: *
11: * Original author:
12: * Mark Pizzolato [email protected]
13: *
14: * Edits:
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.
21: *
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.
26: */
27:
28: char version[] = "@(#) Taylor UUCP Log File Summary Filter, Version 1.2";
29:
30: #include <ctype.h> /* Character Classification */
31: #include <string.h>
32: #include <math.h>
33:
34: #include "uucp.h"
35:
36:
37: #define _DEBUG_ 0
38:
39: /*
40: * Direction of Calling and Data Transmission
41: */
42: #define IN 0 /* Inbound */
43: #define OUT 1 /* Outbound */
44:
45: /*
46: * Data structures used to collect information
47: */
48: struct File_Stats
49: {
50: int files; /* Files Transferred */
51: unsigned long bytes; /* Data Size Transferred*/
52: double time; /* Transmission Time */
53: };
54:
55: struct Phone_Call
56: {
57: int calls; /* Call Count */
58: int succs; /* Successful calls */
59: double connect_time; /* Connect Time Spent */
60: struct File_Stats flow[2]; /* Rcvd & Sent Data */
61: };
62:
63: struct Execution_Command
64: {
65: struct Execution_Command *next;
66: char Commandname[64];
67: int count;
68: };
69:
70: struct Host_entry
71: {
72: struct Host_entry *next;
73: char Hostname[32];
74: struct Execution_Command *cmds; /* Local Activities */
75: struct Phone_Call call[2]; /* In & Out Activities */
76: };
77:
78: /*
79: * Stuff for getopt()
80: */
81: extern int optind; /* GETOPT : Option Index */
82: extern char *optarg; /* GETOPT : Option Value */
83: extern void *calloc();
84:
85: static void fmtime();
86: static void fmbytes();
87:
88: /*
89: * Default files to read. Taken from Taylor compile-time configuration.
90: * Must look like an argvec, hence the dummy argv[0].
91: */
92: static char *(def_logs[3]) = { "", LOGFILE, STATFILE };
93:
94: /*
95: * Misc. strings for reports
96: */
97: static char *(file_hdr[2]) = { "\nReceived file statistics:\n",
98: "\nSent file statistics\n" };
99:
100: /*
101: * BEGIN EXECUTION
102: */
103: main(argc, argv)
104: int argc;
105: char *argv[];
106: {
107: char c;
108: char *p, *s;
109: struct Host_entry *hosts = NULL;
110: struct Host_entry *cur = NULL;
111: struct Host_entry *e;
112: struct Execution_Command *cmd;
113: struct Execution_Command *ec;
114: char Hostname[64];
115: FILE *Log = NULL;
116: char logline[1024];
117: char *logmsg;
118: int sent;
119: int called;
120: int show_files = 0; /* I prefer boolean, but... */
121: int show_calls = 0;
122: int show_commands = 0;
123: int show_efficiency = 0;
124: int show_summary = 0;
125: int have_files = 0;
126: int have_calls = 0;
127: int have_commands = 0;
128: int use_stdin = 0;
129: Hostname[0] = '\0';
130:
131: /*
132: * I wish the compiler had the #error directive!
133: */
134: #if !HAVE_TAYLOR_LOGGING
135: fprintf(stderr, "uurate cannot be used with your configuration of\n");
136: fprintf(stderr, "Taylor UUCP. To use uurate you must be using the\n");
137: fprintf(stderr, "TAYLOR_LOGGING configuration.\n");
138: exit(1);
139: #endif
140:
141: /*
142: * Process the command line arguments
143: */
144: while((c = getopt(argc, argv, "h:cfexai")) != EOF)
145: {
146: switch(c)
147: {
148: case 'h':
149: strcpy(Hostname, optarg);
150: break;
151: case 'c':
152: show_calls = 1;
153: break;
154: case 'f':
155: show_files = 1;
156: break;
157: case 'x':
158: show_commands = 1;
159: break;
160: case 'e':
161: show_efficiency = 1;
162: break;
163: case 'a':
164: show_calls = show_files = show_commands = show_efficiency = 1;
165: break;
166: case 'i':
167: use_stdin = 1;
168: break;
169: default :
170: goto usage;
171: }
172: }
173:
174: /*
175: * If no report switches given, show summary report.
176: */
177: if (show_calls == 0 && show_files == 0
178: && show_efficiency == 0 && show_commands == 0)
179: show_summary = 1;
180:
181: /*
182: * Adjust argv and argc to account for the args processed above.
183: */
184: argc -= (optind - 1);
185: argv += (optind - 1);
186:
187: /*
188: * If further args present, Assume rest are logfiles for us to process,
189: * otherwise, take input from Log and Stat files provided in the
190: * compilation environment of Taylor UUCP. If -i was given, Log already
191: * points to stdin and no file args are accepted.
192: */
193: if(argc == 1) /* No file arguments */
194: {
195: if (use_stdin) /* If -i, read from stdin */
196: {
197: argc = 2;
198: Log = stdin;
199: }
200: else /* Read from current logs */
201: {
202: argc = 3; /* Bash argvec to default log/stat files */
203: argv = &def_logs[0];
204: }
205: }
206: else if (use_stdin) /* File args with -i is an error */
207: {
208: fprintf(stderr, "uurate (error): file args given with '-i'\n");
209: goto usage;
210: }
211:
212: #if _DEBUG_
213: printf("\n");
214: #endif
215:
216: /*
217: * MAIN LOGFILE PROCESSING LOOP
218: */
219: while (argc > 1)
220: {
221:
222: if (!use_stdin && (Log = fopen(argv[1], "r")) == NULL)
223: {
224: perror(argv[1]);
225: return;
226: }
227:
228: #if _DEBUG_
229: printf("Reading %s...\n", (use_stdin ? "stdin" : argv[1]));
230: #endif
231:
232: /*
233: * Read each line of the logfile and collect information
234: */
235: while (fgets(logline, sizeof(logline), Log))
236: {
237: /*
238: * The host name of the other end of the connection is
239: * always the second field of the log line, whether we
240: * are reading a Log file or a Stats file. Set 'p' to
241: * point to the second field, null-terminated. Skip
242: * the line if something is funny.
243: */
244: if (NULL == (p = strchr(logline, ' ')))
245: continue;
246: ++p;
247: if (NULL != (s = strchr(p, ' ')))
248: *s = '\0';
249: for (s = p; *s; ++s)
250: if (isupper(*s))
251: *s = tolower(*s);
252: /*
253: * Skip this line if we got -h <host> and
254: * this line does not contain that host name.
255: */
256: if (Hostname[0] != '\0')
257: if (0 != strcmp(p, Hostname))
258: continue;
259: /*
260: * We are within a call block now. If this line is a file
261: * transfer record, determine the direction. If not then
262: * skip the line if it is not interesting.
263: */
264: if ((s = strchr(++s, ')')) == NULL)
265: continue;
266: logmsg = s + 2; /* Message is 2 characters after ')' */
267: if (0 == strncmp(logmsg, "sent", 4))
268: sent = OUT;
269: else
270: if (0 == strncmp(logmsg, "received", 8))
271: sent = IN;
272: else
273: if ((0 != strncmp(logmsg, "Call complete", 13)) &&
274: (0 != strncmp(logmsg, "Calling system", 14)) &&
275: (0 != strncmp(logmsg, "Incoming call", 13)) &&
276: (0 != strncmp(logmsg, "Executing", 9)))
277: continue;
278: /*
279: * Find the Host_entry for this host, or create a new
280: * one and link it on to the list.
281: */
282: if ((cur == NULL) || (0 != strcmp(p, cur->Hostname)))
283: {
284: for (cur = hosts; cur != NULL ; cur = cur->next)
285: if (0 == strcmp(cur->Hostname, p))
286: break;
287: if (cur == NULL)
288: {
289: cur = (struct Host_entry *)calloc(1, sizeof(*hosts));
290: strcpy(cur->Hostname, p);
291: if (hosts == NULL)
292: hosts = cur;
293: else
294: {
295: for (e = hosts; e->next != NULL; e = e->next);
296: e->next = cur;
297: }
298: }
299: }
300: /*
301: * OK, if this is a uuxqt record, find the Execution_Command
302: * structure for the command being executed, or create a new
303: * one. Then count an execution of this command.
304: */
305: if (0 == strncmp(logmsg, "Executing", 9))
306: {
307: if (NULL == (p = strchr(logmsg, '(')))
308: continue;
309: if ((s = strpbrk(++p, " )")) == NULL)
310: continue;
311: *s = '\0';
312: for (cmd = cur->cmds; cmd != NULL; cmd = cmd->next)
313: if (0 == strcmp(cmd->Commandname, p))
314: break;
315: if (cmd == NULL)
316: {
317: cmd = (struct Execution_Command *)calloc(1, sizeof(*cmd));
318: strcpy(cmd->Commandname, p);
319: if (cur->cmds == NULL)
320: cur->cmds = cmd;
321: else
322: {
323: for (ec = cur->cmds; ec->next != NULL; ec = ec->next);
324: ec->next = cmd;
325: }
326: }
327: ++cmd->count;
328: have_commands = 1;
329: continue;
330: }
331: /*
332: * Count start of outgoing call.
333: */
334: if (0 == strncmp(logmsg, "Calling system", 14))
335: {
336: called = OUT;
337: cur->call[called].calls += 1;
338: have_calls = 1;
339: continue;
340: }
341: /*
342: * Count start of incoming call.
343: */
344: if (0 == strncmp(logmsg, "Incoming call", 13))
345: {
346: called = IN;
347: cur->call[called].calls += 1;
348: have_calls = 1;
349: continue;
350: }
351: /*
352: * Handle end of call. Pick up the connect time.
353: */
354: if (0 == strncmp(logmsg, "Call complete", 13))
355: {
356: cur->call[called].succs += 1;
357: if (NULL == (s = strchr(logmsg, '(')))
358: continue;
359: cur->call[called].connect_time += atof(s+1);
360: continue;
361: }
362: /*
363: * If we reached here, this must have been a file transfer
364: * record. Count it in the field corresponding to the
365: * direction of the transfer. Count bytes transferred and
366: * the time to transfer as well.
367: */
368: have_files = 1;
369: cur->call[called].flow[sent].files += 1;
370: if (NULL == (s = strchr(logmsg, ' ')))
371: continue;
372: cur->call[called].flow[sent].bytes += atol(++s);
373: if (NULL == (s = strchr(s, ' ')))
374: continue;
375: if (NULL == (s = strpbrk(s, "0123456789")))
376: continue;
377: cur->call[called].flow[sent].time += atof(s);
378: }
379: argc -= 1;
380: argv += 1;
381: if(Log != stdin)
382: fclose(Log);
383: }
384:
385: /*
386: * ***********
387: * * REPORTS *
388: * ***********
389: */
390:
391: /*
392: * Truncate the Hostnames to 8 characters at most.
393: */
394: for (cur = hosts; cur != NULL; cur = cur->next)
395: cur->Hostname[8] = '\0';
396:
397: #if _DEBUG_
398: printf("\n");
399: #endif
400:
401: /*
402: * Summary report
403: *
404: * I know, this code could be tightened (rbd)...
405: */
406: if(show_summary)
407: {
408: char t1[32], t2[32], t3[32], t4[32], t5[32];
409: long ib, ob, b, rf, sf;
410: long t_ib=0, t_ob=0, t_b=0, t_rf=0, t_sf=0;
411: double it, ot, ir, or;
412: double t_it=0.0, t_ot=0.0;
413: int nhosts = 0;
414:
415: printf("\n\
416: Remote ------- Bytes -------- --- Time ---- -- Avg CPS -- -- Files --\n");
417: printf("\
418: Host Rcvd Sent Total Rcvd Sent Rcvd Sent Rcvd Sent\n");
419: printf("\
420: -------- ------- ------- ------- ------ ------ ------ ------ ----- -----\n");
421: for (cur = hosts; cur != NULL; cur = cur->next)
422: {
423: ib = (cur->call[IN].flow[IN].bytes +
424: cur->call[OUT].flow[IN].bytes);
425: fmbytes(ib, t1);
426: t_ib += ib;
427:
428: ob = (cur->call[IN].flow[OUT].bytes +
429: cur->call[OUT].flow[OUT].bytes);
430: fmbytes(ob, t2);
431: t_ob += ob;
432:
433: b = ib + ob;
434: fmbytes(b, t3);
435: t_b += b;
436:
437: it = cur->call[IN].flow[IN].time +
438: cur->call[OUT].flow[IN].time;
439: fmtime(it, t4);
440: t_it += it;
441:
442: ot = cur->call[IN].flow[OUT].time +
443: cur->call[OUT].flow[OUT].time;
444: fmtime(ot, t5);
445: t_ot += ot;
446:
447: rf = cur->call[IN].flow[IN].files +
448: cur->call[OUT].flow[IN].files;
449: t_rf += rf;
450:
451: sf = cur->call[IN].flow[OUT].files +
452: cur->call[OUT].flow[OUT].files;
453: t_sf += sf;
454:
455: ir = (it == 0.0) ? 0.0 : (ib / it);
456: or = (ot == 0.0) ? 0.0 : (ob / ot);
457:
458: printf("%-8s %7s %7s %7s %6s %6s %6.1f %6.1f %5d %5d\n",
459: cur->Hostname,
460: t1, t2, t3, t4, t5,
461: ir, or, rf, sf);
462: }
463:
464: if(nhosts > 1)
465: {
466: fmbytes(t_ib, t1);
467: fmbytes(t_ob, t2);
468: fmbytes(t_b, t3);
469: fmtime(t_it, t4);
470: fmtime(t_ot, t5);
471: ir = (t_it == 0.0) ? 0.0 : (t_ib / t_it);
472: or = (t_ot == 0.0) ? 0.0 : (t_ob / t_ot);
473:
474: printf("\
475: -------- ------- ------- ------- ------ ------ ------ ------ ----- -----\n");
476: printf("\
477: Totals %7s %7s %7s %6s %6s %6.1f %6.1f %5d %5d\n",
478: t1, t2, t3, t4, t5,
479: ir, or, t_rf, t_sf);
480: }
481: }
482:
483:
484: /*
485: * Call statistics report
486: */
487: if(show_calls && have_calls)
488: {
489: char t1[32], t2[32];
490:
491: printf("\nCall statistics:\n");
492: printf("\
493: sysname callto failto totime callfm failfm fmtime\n");
494: printf("\
495: -------- ------ ------ -------- ------ ------ --------\n");
496: for (cur = hosts; cur != NULL; cur = cur->next)
497: {
498: fmtime(cur->call[OUT].connect_time, t1);
499: fmtime(cur->call[IN].connect_time, t2),
500: printf(" %-8s %6d %6d %8s %6d %6d %8s\n",
501: cur->Hostname,
502: cur->call[OUT].calls,
503: cur->call[OUT].calls - cur->call[OUT].succs,
504: t1,
505: cur->call[IN].calls,
506: cur->call[IN].calls - cur->call[IN].succs,
507: t2);
508: }
509: }
510:
511: /*
512: * File statistics report
513: */
514: if(show_files && have_files)
515: {
516: char t1[32], t2[32];
517:
518: for (sent = IN; sent <= OUT; ++sent)
519: {
520: printf(file_hdr[sent]);
521: printf(" sysname files bytes xfr time byte/s\n");
522: printf(" -------- ------ -------- -------- ------\n");
523: for (cur = hosts; cur != NULL; cur = cur->next)
524: {
525: double rate;
526: double time;
527:
528: time = cur->call[IN].flow[sent].time +
529: cur->call[OUT].flow[sent].time;
530: if (time == 0.0)
531: continue;
532: rate = (cur->call[IN].flow[sent].bytes +
533: cur->call[OUT].flow[sent].bytes) / time;
534: fmbytes((cur->call[IN].flow[sent].bytes +
535: cur->call[OUT].flow[sent].bytes), t1);
536: fmtime((cur->call[IN].flow[sent].time +
537: cur->call[OUT].flow[sent].time), t2);
538: printf(" %-8s %6d %8s %8s %6.1f\n",
539: cur->Hostname,
540: cur->call[IN].flow[sent].files +
541: cur->call[OUT].flow[sent].files,
542: t1, t2, rate);
543: }
544: }
545: }
546:
547: /*
548: * Efficiency report
549: */
550: if (show_efficiency && have_files)
551: {
552: char t1[32], t2[32], t3[32];
553: double total, flow;
554:
555: printf("\nEfficiency:\n");
556: printf(" sysname conntime flowtime ovhdtime eff. %%\n");
557: printf(" -------- -------- -------- -------- ------\n");
558: for (cur = hosts; cur != NULL; cur = cur->next)
559: {
560: total = cur->call[IN].connect_time + cur->call[OUT].connect_time;
561: flow = cur->call[IN].flow[IN].time + cur->call[IN].flow[OUT].time +
562: cur->call[OUT].flow[IN].time + cur->call[OUT].flow[OUT].time;
563: fmtime(total, t1);
564: fmtime(flow, t2);
565: fmtime((total-flow), t3);
566: printf(" %-8s %8s %8s %8s %5.1f%%\n",
567: cur->Hostname, t1, t2, t3, ((flow / total) * 100.0));
568: }
569: }
570:
571: /*
572: * Command execution report
573: */
574: if (show_commands & have_commands)
575: {
576: printf("\nCommand executions:\n");
577: printf(" sysname rmail rnews other\n");
578: printf(" -------- ------ ------ ------\n");
579: for (cur = hosts; cur != NULL; cur = cur->next)
580: {
581: int rmail, rnews, other;
582:
583: if (cur->cmds == NULL)
584: continue;
585: rmail = rnews = other = 0;
586: for (cmd = cur->cmds; cmd != NULL; cmd = cmd->next)
587: {
588: if (strcmp(cmd->Commandname, "rmail") == 0)
589: rmail += cmd->count;
590: else if (strcmp(cmd->Commandname, "rnews") == 0)
591: rnews += cmd->count;
592: else
593: other += cmd->count;
594: }
595: printf(" %-8s %6d %6d %6d\n", cur->Hostname,
596: rmail, rnews, other);
597: }
598: }
599: return;
600:
601: usage:
602: fprintf(stderr,
603: "Usage uurate [-cfexai] [-h hostname] [logfile ... logfile]\n");
604: fprintf(stderr,"where:\t-c\tReport call statistics\n");
605: fprintf(stderr, "\t-f\tReport file transfer statistics\n");
606: fprintf(stderr, "\t-e\tReport efficiency statistics\n");
607: fprintf(stderr, "\t-x\tReport command execution statistics\n");
608: fprintf(stderr, "\t-a\tAll of the above reports\n");
609: fprintf(stderr, "\t-h host\tReport activities involving ONLY host\n");
610: fprintf(stderr, "\t-i\tRead log info from standard input\n");
611: fprintf(stderr,
612: "If no report options given, a compact summary report is given.\n");
613: fprintf(stderr,
614: "If neither -i nor logfiles given, defaults to reading from\n");
615: fprintf(stderr, "%s and %s\n\n", LOGFILE, STATFILE);
616: }
617:
618: /*
619: * fmtime() - Format time in hours & minutes;
620: */
621: static void fmtime(dsec, buf)
622: double dsec;
623: char *buf;
624: {
625: long hrs, min, lsec;
626:
627: lsec = dsec;
628: hrs = lsec / 3600L;
629: min = (lsec - (hrs * 3600L)) / 60L;
630:
631: sprintf(buf, "%02ld:%02ld", hrs, min);
632: }
633:
634: /*
635: * fmbytes - Format size in bytes
636: */
637: static void fmbytes(n, buf)
638: unsigned long n;
639: char *buf;
640: {
641: char t;
642: double s = n;
643:
644: if(s >= 10239897.6) /* More than 9999.9K ? */
645: {
646: s = (double)n / 1048576.0; /* Yes, display in Megabytes */
647: t = 'M';
648: }
649: else
650: {
651: s = (double)n / 1024.0; /* Display in Kilobytes */
652: t = 'K';
653: }
654:
655: sprintf(buf, "%.1f%c", s, t);
656: }
657:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.