|
|
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.