/******************************************************** * File: crashstats.c * Created at Sun Jan 28 22:10:33 MSK 2001 by raorn // raorn@binec.ru * CrashStats - statistics generation utility * $Id: crashstats.c,v 1.17 2002/01/10 02:04:09 raorn Exp $ *******************************************************/ #include #include #include #include #if TIME_WITH_SYS_TIME # include # include #elif HAVE_SYS_TIME # include #else # include #endif #include #include #include #ifdef HAVE_GETOPT_H # include #else # include #endif #include #include uchar verstr[] = "CrashStats/" PLATFORM_NAME " " VERSION; #define STATS_IDENTIFIER "CST3" struct DiskAreaStats { uchar Tagname[80]; struct Node4D Aka; uchar Group; uchar fill_to_make_even; /* Just ignore this one */ ulong TotalTexts; ushort Last8Days[8]; ulong Dupes; time_t FirstTime; time_t LastTime; }; struct DiskNodeStats { struct Node4D Node; ulong GotNetmails; ulong GotNetmailBytes; ulong SentNetmails; ulong SentNetmailBytes; ulong GotEchomails; ulong GotEchomailBytes; ulong SentEchomails; ulong SentEchomailBytes; ulong Dupes; time_t FirstTime; }; struct StatsNode { struct StatsNode *Next; uchar Tagname[80]; ulong Average; ulong Total; ulong Dupes; time_t FirstTime; time_t LastTime; ushort Last8Days[8]; }; struct NodeStatsNode { struct NodeStatsNode *Next; struct Node4D Node; ulong GotNetmails; ulong GotNetmailBytes; ulong SentNetmails; ulong SentNetmailBytes; ulong GotEchomails; ulong GotEchomailBytes; ulong SentEchomails; ulong SentEchomailBytes; ulong Dupes; ulong Days; time_t FirstTime; }; #define SHOWVER 128 #define NOAREAS 129 #define NONODES 130 bool diskfull; int CompareAlpha(const void *a1, const void *a2) { struct StatsNode **s1, **s2; s1 = (struct StatsNode **) a1; s2 = (struct StatsNode **) a2; return (stricmp((*s1)->Tagname, (*s2)->Tagname)); } int CompareTotal(const void *a1, const void *a2) { struct StatsNode **s1, **s2; s1 = (struct StatsNode **) a1; s2 = (struct StatsNode **) a2; if ((*s1)->Total < (*s2)->Total) return (1); if ((*s1)->Total > (*s2)->Total) return (-1); return (0); } int CompareDupes(const void *a1, const void *a2) { struct StatsNode **s1, **s2; s1 = (struct StatsNode **) a1; s2 = (struct StatsNode **) a2; if ((*s1)->Dupes < (*s2)->Dupes) return (1); if ((*s1)->Dupes > (*s2)->Dupes) return (-1); return (0); } int CompareMsgsDay(const void *a1, const void *a2) { struct StatsNode **s1, **s2; s1 = (struct StatsNode **) a1; s2 = (struct StatsNode **) a2; if ((*s1)->Average < (*s2)->Average) return (1); if ((*s1)->Average > (*s2)->Average) return (-1); return (0); } int CompareFirstTime(const void *a1, const void *a2) { struct StatsNode **s1, **s2; s1 = (struct StatsNode **) a1; s2 = (struct StatsNode **) a2; if ((*s1)->FirstTime < (*s2)->FirstTime) return (1); if ((*s1)->FirstTime > (*s2)->FirstTime) return (-1); return (0); } int CompareLastTime(const void *a1, const void *a2) { struct StatsNode **s1, **s2; s1 = (struct StatsNode **) a1; s2 = (struct StatsNode **) a2; if ((*s1)->LastTime < (*s2)->LastTime) return (1); if ((*s1)->LastTime > (*s2)->LastTime) return (-1); return (0); } bool Sort(struct jbList * list, uchar sortmode) { ulong nc; struct StatsNode *sn, **buf, **work; nc = 0; for (sn = (struct StatsNode *) list->First; sn; sn = sn->Next) nc++; if (nc == 0) return (TRUE); /* Nothing to sort */ if (!(buf = (struct StatsNode **) malloc(nc * sizeof(struct StatsNode *)))) return (FALSE); work = buf; for (sn = (struct StatsNode *) list->First; sn; sn = sn->Next) *work++ = sn; switch (sortmode) { case 'a': qsort(buf, nc, 4, CompareAlpha); break; case 't': qsort(buf, nc, 4, CompareTotal); break; case 'm': qsort(buf, nc, 4, CompareMsgsDay); break; case 'd': qsort(buf, nc, 4, CompareFirstTime); break; case 'l': qsort(buf, nc, 4, CompareLastTime); break; case 'u': qsort(buf, nc, 4, CompareDupes); break; } jbNewList(list); for (work = buf; nc--;) jbAddNode(list, (struct jbNode *) *work++); free(buf); return (TRUE); } int CompareNodes(const void *a1, const void *a2) { struct NodeStatsNode **s1, **s2; s1 = (struct NodeStatsNode **) a1; s2 = (struct NodeStatsNode **) a2; return (Compare4D(&(*s1)->Node, &(*s2)->Node)); } bool SortNodes(struct jbList * list) { struct NodeStatsNode *sn, **buf, **work; ulong nc; nc = 0; for (sn = (struct NodeStatsNode *) list->First; sn; sn = sn->Next) nc++; if (nc == 0) return (TRUE); /* Nothing to sort */ if (!(buf = (struct NodeStatsNode **) malloc(nc * sizeof(struct NodeStatsNode *)))) return (FALSE); work = buf; for (sn = (struct NodeStatsNode *) list->First; sn; sn = sn->Next) *work++ = sn; qsort(buf, nc, 4, CompareNodes); jbNewList(list); for (work = buf; nc--;) jbAddNode(list, (struct jbNode *) *work++); free(buf); return (TRUE); } char *unit(long i) { static char buf[40]; if ((i > 10000000) || (i < -10000000)) snprintf(buf, 40, "%ld MB", i / (1024 * 1024)); else if ((i > 10000) || (i < -10000)) snprintf(buf, 40, "%ld KB", i / 1024); else snprintf(buf, 40, "%ld bytes", i); return buf; } bool CheckFlags(uchar group, uchar * node) { int c; for (c = 0; c < strlen(node); c++) { if (toupper(group) == toupper(node[c])) return (TRUE); } return (FALSE); } ulong CalculateAverage(ushort * last8array, ulong total, ulong daystatswritten, time_t firstday) { ushort days, c; ulong sum; if (daystatswritten == 0 || firstday == 0) return (0); days = daystatswritten - firstday; if (days > 7) days = 7; sum = 0; for (c = 1; c < days + 1; c++) sum += last8array[c]; if (days == 0) days = 1; if (sum == 0 && total != 0) { days = daystatswritten - firstday; if (days == 0) days = 1; return (total / days); } return (sum / days); } bool ctrlc; RETSIGTYPE breakfunc(int x) { ctrlc = TRUE; } int main(int argc, char **argv) { FILE *fh; ulong total, areas, totaldupes; time_t firsttime, t; ulong DayStatsWritten; uchar buf[200], date[30], date2[30]; struct DiskAreaStats dastat; struct DiskNodeStats dnstat; struct StatsNode *sn; struct NodeStatsNode *nsn; struct jbList StatsList; struct jbList NodesList; ulong c, num, tot; ushort total8days[8]; uchar sortmode = 'a', *groups = NULL; int lastseven = 0, noareas = 0, nonodes = 0; struct tm *tp; uchar *monthnames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" }; static struct option lopts[] = { {"help", 0, NULL, 'h'}, {"sort", required_argument, NULL, 's'}, {"groups", required_argument, NULL, 'g'}, {"last7", 0, NULL, '7'}, {"no-areas", 0, NULL, NOAREAS}, {"no-nodes", 0, NULL, NONODES}, {"version", 0, NULL, SHOWVER}, {0, 0, 0, 0} }; static uchar optstr[] = "+hs:g:7an"; static uchar helpstr[] = "Usage: crashstats [OPTION]... FILE\n" "Display CrashEcho statistics.\n" "\n" " -h, --help display this help and exit\n" " -s, --sort=MODE specifies the sort mode:\n" " a - sort alphabetically\n" " t - sort by total number of messages\n" " m - sort by msgs/day\n" " d - sort by first time messages were imported\n" " l - sort by last time messages were imported\n" " u - sort by number of dupes\n" " -g, --groups=GROUPS areas in the specified GROUPS are included\n" " -7, --last7 displays detailed information about the flow of\n" " messages in areas for the last seven days\n" " --no-areas hide area statistics\n" " --no-nodes hide node statistics\n" " --version print version number and exit\n\n"; int ch, lopt_index; signal(SIGINT, breakfunc); while((ch=getopt_long(argc, argv, optstr, lopts, &lopt_index)) != -1){ switch(ch){ case 'h': case '?': default: fprintf(stderr, helpstr); exit(EXIT_OK); /* Not reached */ case SHOWVER: fprintf(stderr, "%s\n", verstr); exit(EXIT_OK); /* Not reached */ case 's': sortmode = tolower(optarg[0]); break; case 'g': groups = optarg; break; case '7': lastseven = 1; break; case NOAREAS: noareas = 1; break; case NONODES: nonodes = 1; break; } } argc -= optind; argv += optind; if(argc != 1){ fprintf(stderr, helpstr); exit(EXIT_ERROR); } if (!strchr("amtdlu", sortmode)) { fprintf(stderr, "Unknown sort mode %c\n", sortmode); exit(EXIT_ERROR); } if (noareas && nonodes) { fprintf(stderr, "Nothing to do\n"); exit(EXIT_ERROR); } if (!(fh = fopen(argv[0], "rb"))) { fprintf(stderr, "Error opening %s\n", (char *) argv[0]); fprintf(stderr, "Error: %s\n", strerror(errno)); exit(EXIT_ERROR); } fread(buf, 4, 1, fh); buf[4] = 0; if (strcmp(buf, STATS_IDENTIFIER) != 0) { fprintf(stderr, "Unknown format of stats file\n"); fclose(fh); exit(EXIT_ERROR); } fread(&DayStatsWritten, sizeof(ulong), 1, fh); total = 0; totaldupes = 0; firsttime = 0; areas = 0; for (c = 0; c < 8; c++) total8days[c] = 0; jbNewList(&StatsList); jbNewList(&NodesList); fread(&num, sizeof(ulong), 1, fh); c = 0; if (!noareas) { while (c < num && fread(&dastat, 1, sizeof(struct DiskAreaStats), fh) == sizeof(struct DiskAreaStats)) { if (!groups || CheckFlags(dastat.Group, groups)) { if (!(sn = malloc(sizeof(struct StatsNode)))) { fprintf(stderr, "Out of memory\n"); jbFreeList(&StatsList); fclose(fh); exit(EXIT_ERROR); } jbAddNode(&StatsList, (struct jbNode *) sn); strcpy(sn->Tagname, dastat.Tagname); sn->Dupes = dastat.Dupes; sn->Total = dastat.TotalTexts; sn->FirstTime = dastat.FirstTime; sn->LastTime = dastat.LastTime; memcpy(&sn->Last8Days[0], &dastat.Last8Days[0], 8 * sizeof(ushort)); sn->Average = CalculateAverage(&dastat.Last8Days[0], dastat.TotalTexts, DayStatsWritten, sn->FirstTime / (24 * 60 * 60)); } if (dastat.FirstTime != 0) if (firsttime == 0 || firsttime > dastat.FirstTime) firsttime = dastat.FirstTime; c++; } } else { while (c < num && fread(&dastat, 1, sizeof(struct DiskAreaStats), fh) == sizeof(struct DiskAreaStats)) c++; } fread(&num, sizeof(ulong), 1, fh); c = 0; if (!nonodes) { while (c < num && fread(&dnstat, 1, sizeof(struct DiskNodeStats), fh) == sizeof(struct DiskNodeStats)) { if (!(nsn = malloc(sizeof(struct NodeStatsNode)))) { fprintf(stderr, "Out of memory\n"); jbFreeList(&NodesList); jbFreeList(&StatsList); fclose(fh); exit(EXIT_ERROR); } jbAddNode(&NodesList, (struct jbNode *) nsn); Copy4D(&nsn->Node, &dnstat.Node); nsn->GotNetmails = dnstat.GotNetmails; nsn->GotNetmailBytes = dnstat.GotNetmailBytes; nsn->SentNetmails = dnstat.SentNetmails; nsn->SentNetmailBytes = dnstat.SentNetmailBytes; nsn->GotEchomails = dnstat.GotEchomails; nsn->GotEchomailBytes = dnstat.GotEchomailBytes; nsn->SentEchomails = dnstat.SentEchomails; nsn->SentEchomailBytes = dnstat.SentEchomailBytes; nsn->Dupes = dnstat.Dupes; nsn->Days = DayStatsWritten - dnstat.FirstTime % (24 * 60 * 60); if (nsn->Days == 0) nsn->Days = 1; nsn->FirstTime = dnstat.FirstTime; if (dnstat.FirstTime != 0) if (firsttime == 0 || firsttime > dnstat.FirstTime) firsttime = dnstat.FirstTime; c++; } } else { while (c < num && fread(&dnstat, 1, sizeof(struct DiskNodeStats), fh) == sizeof(struct DiskNodeStats)) c++; } fclose(fh); t = (time_t) DayStatsWritten *24 * 60 * 60; tp = localtime(&firsttime); snprintf(date, 30, "%02d-%s-%02d", tp->tm_mday, monthnames[tp->tm_mon], tp->tm_year % 100); tp = localtime(&t); snprintf(date2, 30, "%02d-%s-%02d", tp->tm_mday, monthnames[tp->tm_mon], tp->tm_year % 100); printf("\nStatistics from %s to %s\n", date, date2); if (!ctrlc && !noareas) { Sort(&StatsList, 'a'); Sort(&StatsList, sortmode); printf("\n"); if (lastseven) { printf("Area "); for (c = 7; c > 0; c--) { t = (DayStatsWritten - c) * 24 * 60 * 60; tp = localtime(&t); printf(" %02d", tp->tm_mday); } printf(" Total\n============================================================================\n"); if (!ctrlc) { for (sn = (struct StatsNode *) StatsList.First; sn && !ctrlc; sn = sn->Next) { tot = 0; for (c = 7; c > 0; c--) tot += sn->Last8Days[c]; printf("%-33.33s %4d %4d %4d %4d %4d %4d %4d : %5ld\n", sn->Tagname, sn->Last8Days[7], sn->Last8Days[6], sn->Last8Days[5], sn->Last8Days[4], sn->Last8Days[3], sn->Last8Days[2], sn->Last8Days[1], tot); for (c = 7; c > 0; c--) total8days[c] += sn->Last8Days[c]; areas++; } if (!ctrlc) { tot = 0; for (c = 7; c > 0; c--) tot += total8days[c]; printf("=============================================================================\n"); snprintf(buf, 200, "Totally in all %lu areas", areas); printf("%-33.33s %4d %4d %4d %4d %4d %4d %4d : %5ld\n", buf, total8days[7], total8days[6], total8days[5], total8days[4], total8days[3], total8days[2], total8days[1], tot); } } } else { printf("Area First Last Msgs Msgs/day Dupes\n"); printf("============================================================================\n"); if (!ctrlc) { for (sn = (struct StatsNode *) StatsList.First; sn && !ctrlc; sn = sn->Next) { if (sn->LastTime == 0) { strcpy(date2, " "); } else { tp = localtime(&sn->LastTime); snprintf(date2, 30, "%02d-%s-%02d", tp->tm_mday, monthnames[tp->tm_mon], tp->tm_year % 100); } if (sn->FirstTime == 0) { strcpy(date, " "); } else { tp = localtime(&sn->FirstTime); snprintf(date, 30, "%02d-%s-%02d", tp->tm_mday, monthnames[tp->tm_mon], tp->tm_year % 100); } for (c = 0; c < 8; c++) total8days[c] += sn->Last8Days[c]; total += sn->Total; totaldupes += sn->Dupes; areas++; printf("%-30.30s %-9.9s %-9.9s %7ld %7ld %7ld\n", sn->Tagname, date, date2, sn->Total, sn->Average, sn->Dupes); } } if (!ctrlc) { printf("============================================================================\n"); snprintf(buf, 200, "Totally in all %lu areas", areas); printf("%-42s %7ld %7ld %7ld\n", buf, total, CalculateAverage(&total8days[0], total, DayStatsWritten, firsttime / (24 * 60 * 60)), totaldupes); } } } if (!ctrlc && !nonodes) { SortNodes(&NodesList); printf("\n"); printf("Nodes statistics\n"); printf("================\n"); for (nsn = (struct NodeStatsNode *) NodesList.First; nsn && !ctrlc; nsn = nsn->Next) { if (nsn->FirstTime == 0) { strcpy(date, ""); } else { tp = localtime(&nsn->FirstTime); snprintf(date, 30, "%02d-%s-%02d", tp->tm_mday, monthnames[tp->tm_mon], tp->tm_year % 100); } snprintf(buf, 200, "%u:%u/%u.%u", nsn->Node.Zone, nsn->Node.Net, nsn->Node.Node, nsn->Node.Point); printf("%-30.40s Statistics since: %s\n\n", buf, date); printf(" Sent netmails: %lu/%s\n", nsn->SentNetmails, unit(nsn->SentNetmailBytes)); printf(" Received netmails: %lu/%s\n", nsn->GotNetmails, unit(nsn->GotNetmailBytes)); printf(" Sent echomails: %lu/%s\n", nsn->SentEchomails, unit(nsn->SentEchomailBytes)); printf(" Received echomails: %lu/%s\n", nsn->GotEchomails, unit(nsn->GotEchomailBytes)); printf(" Dupes: %lu\n", nsn->Dupes); printf("\n"); } } if (ctrlc) { printf("*** Break\n"); } else { printf("\n"); } jbFreeList(&StatsList); jbFreeList(&NodesList); exit(EXIT_OK); }