1 /* $Id: ninpaths.c 6362 2003-05-31 18:35:04Z rra $
3 ** New inpaths reporting program.
5 ** Idea, data structures and part of code based on inpaths 2.5
6 ** by Brian Reid, Landon Curt Noll
8 ** This version written by Olaf Titz, Feb. 1997. Public domain.
16 #define VERSION "3.1.1"
18 #define MAXFNAME 1024 /* max length of file name */
19 #define MAXLINE 1024 /* max length of Path line */
20 #define HASH_TBL 65536 /* hash table size (power of two) */
21 #define MAXHOST 128 /* max length of host name */
22 #define HOSTF "%127s" /* scanf format for host name */
23 #define RECLINE 120 /* dump file line length softlimit */
25 /* structure used to tally the traffic between two hosts */
27 struct trec *rlink; /* next in chain */
28 struct nrec *linkid; /* pointer to... */
29 long tally; /* count */
32 /* structure to hold the information about a host */
34 struct nrec *link; /* next in chain */
35 struct trec *rlink; /* start of trec chain */
36 char *id; /* host name */
37 long no; /* identificator for dump file */
38 long sentto; /* tally of articles sent from here */
41 struct nrec *hosthash[HASH_TBL];
43 time_t starttime; /* Start time */
44 double atimes=0.0; /* Sum of articles times wrt. starttime */
45 long total=0, /* Total articles processed */
46 sites=0; /* Total sites known */
48 /* malloc and warn if out of mem */
54 fprintf(stderr, "warning: out of memory\n");
58 /* Hash function due to Glenn Fowler / Landon Curt Noll / Phong Vo */
65 for (val = 0; (c=(unsigned long)(*str)); ++str) {
66 val *= 16777619; /* magic */
67 val ^= c; /* more magic */
69 return (int)(val & (unsigned long)(HASH_TBL-1));
72 /* Look up a host in the hash table. Add if necessary. */
79 for (h=hosthash[i]; h; h=h->link)
80 if (!strcmp(n, h->id))
82 /* not there - allocate */
83 h=wmalloc(sizeof(struct nrec));
98 /* Look up a tally record between hosts. Add if necessary. */
100 tallyrec(struct nrec *r, struct nrec *h)
103 for (t=r->rlink; t; t=t->rlink)
106 t=wmalloc(sizeof(struct trec));
118 "!!NINP" <version> <starttime> <endtime> <sites> <total> <avgtime> "\n"
119 followed by <sites> S-records,
122 followed by max. <sites>^2 L-records
124 followed by max. <sites> L-records
125 "!!NEND" <nlrecs> "\n"
126 starttime, endtime, avgtime as UNIX date
127 the records are separated by space or \n
128 an S-record is "site count"
130 an L-record is "sitea!siteb!count"
132 an L-record is ":sitea" { "!siteb,count" }...
133 ",count" omitted if count==1
134 where sitea and siteb are numbers of the S-records starting at 0
148 fprintf(f, "!!NINP " VERSION " %lu %lu %ld %ld %ld\n",
149 (unsigned long) starttime, (unsigned long) time(NULL), sites,
150 total, (long)(atimes/total)+starttime);
152 /* write the S-records (hosts), numbering them in the process */
153 for (i=0; i<HASH_TBL; ++i)
154 for (h=hosthash[i]; h; h=h->link) {
156 j+=fprintf(f, "%s %ld", h->id, h->sentto);
165 fprintf(stderr, "internal error: sites=%ld, dumped=%ld\n", sites, n);
167 fprintf(f, "\n!!NLREC\n");
170 /* write the L-records (links) */
171 for (i=0; i<HASH_TBL; ++i)
172 for (h=hosthash[i]; h; h=h->link)
174 j+=fprintf(f, ":%ld", h->no);
175 for (; t; t=t->rlink) {
176 j+=fprintf(f, "!%ld", t->linkid->no);
178 j+=fprintf(f, ",%ld", t->tally);
186 fprintf(f, "\n!!NLEND %ld\n", n);
190 /* Write dump to a named file. Substitute %d in file name with system time. */
193 writedumpfile(const char *n)
198 if (n[0]=='-' && n[1]=='\0') {
202 snprintf(buf, sizeof(buf), n, time(0));
208 perror("writedumpfile: fopen");
212 /* Read a dump file. */
219 unsigned long st, et, at;
226 #define formerr(i) {\
227 fprintf(stderr, "dump file format error #%d\n", (i)); return -1; }
229 if (fscanf(f, "!!NINP %15s %lu %lu %ld %ld %lu\n",
230 v, &st, &et, &sit, &tot, &at)!=6)
233 n=calloc(sit, sizeof(struct nrec *));
235 fprintf(stderr, "error: out of memory\n");
238 for (i=0; i<sit; i++) {
239 if (fscanf(f, HOSTF " %ld ", c, &l)!=2) {
240 fprintf(stderr, "read %ld ", i);
248 if ((fscanf(f, HOSTF "\n", c)!=1) ||
249 strcmp(c, "!!NLREC"))
252 if (!strncmp(v, "3.0", 3)) {
253 /* Read 3.0-format L-records */
254 while (fscanf(f, "%d!%d!%ld ", &a, &b, &l)==3) {
255 t=tallyrec(n[a], n[b]);
261 } else if (!strncmp(v, "3.1", 3)) {
263 while (fscanf(f, " :%d", &a)==1) {
264 while ((i=fscanf(f, "!%d,%ld", &b, &l))>0) {
265 t=tallyrec(n[a], n[b]);
275 fprintf(stderr, "version %s ", v);
278 if ((fscanf(f, "!!NLEND %ld\n", &i)!=1)
282 fprintf(stderr, " dumped start %s total=%ld atimes=%ld (%ld)\n",
283 ctime(&st), tot, at, at-st);
285 /* Adjust the time average and total count */
286 if ((unsigned long) starttime > st) {
287 atimes+=(double)total*(starttime-st);
290 atimes+=(double)tot*(at-starttime);
293 fprintf(stderr, " current start %s total=%ld atimes=%.0f (%.0f)\n\n",
294 ctime(&starttime), total, atimes, atimes/total);
300 /* Read dump from a file. */
303 readdumpfile(const char *n)
308 if (n[0]=='-' && n[1]=='\0')
309 return readdump(stdin);
313 /* fprintf(stderr, "Reading dump file %s\n", n); */
318 perror("readdumpfile: fopen");
324 /* Process a Path line. */
335 for (c2=c; *c2 && *c2!='!'; c2++);
337 /* looks broken, dont bother with rest */
340 *c2++='\0'; /* skip "!!" too */
357 /* Take Path lines from file (stdin used here). */
364 int v=1; /* current line is valid */
366 while (fgets(buf, sizeof(buf), f)) {
368 if (!strncmp(c, "Path: ", 6))
370 /* find end of line. Some broken newsreaders preload Path with
371 a name containing spaces. Chop off those entries. */
372 for (ce=c; *ce && !CTYPE(isspace, *ce); ++ce);
378 for (; ce>c && *ce!='!'; --ce); /* ignore last element */
380 if (pathline(c)<0) /* process it */
381 /* If an out of memory condition occurs while reading
382 Path lines, stop reading and write the dump so far.
383 INN will restart a fresh ninpaths. */
385 /* update average age and grand total */
386 atimes+=(time(0)-starttime);
389 /* next line is valid */
395 /* Output a report suitable for mailing. From inpaths 2.5 */
398 report(const char *hostname, int verbose)
401 int i, columns, needHost;
402 long nhosts=0, nlinks=0;
403 struct nrec *list, *relay;
405 char hostString[MAXHOST];
409 fprintf(stderr, "report: no traffic\n");
412 /* mark own site to not report it */
413 list=hhost(hostname);
417 avgAge=((double)t0 - (atimes/total + (double)starttime)) /86400.0;
418 printf("ZCZC begin inhosts %s %s %d %ld %3.1f\n",
419 VERSION,hostname,verbose,total,avgAge);
420 for (i=0; i<HASH_TBL-1; i++) {
422 while (list != NULL) {
423 if (list->id[0] != 0 && list->rlink != NULL) {
424 if (verbose > 0 || (100*list->sentto > total))
425 printf("%ld\t%s\n",list->sentto, list->id);
430 printf("ZCZC end inhosts %s\n",hostname);
432 printf("ZCZC begin inpaths %s %s %d %ld %3.1f\n",
433 VERSION,hostname,verbose,total,avgAge);
434 for (i=0; i<HASH_TBL-1; i++) {
436 while (list != NULL) {
437 if (verbose > 1 || (100*list->sentto > total)) {
438 if (list->id[0] != 0 && list->rlink != NULL) {
439 columns = 3+strlen(list->id);
440 snprintf(hostString,sizeof(hostString),"%s H ",list->id);
443 while (rlist != NULL) {
445 (100*rlist->tally > total)
446 || ((verbose > 1)&&(5000*rlist->tally>total))
448 if (needHost) printf("%s",hostString);
450 relay = rlist->linkid;
451 if (relay->id[0] != 0) {
453 printf("\n%s",hostString);
454 columns = 3+strlen(list->id);
456 printf("%ld Z %s U ", rlist->tally, relay->id);
457 columns += 9+strlen(relay->id);
460 rlist = rlist->rlink;
463 if (!needHost) printf("\n");
470 printf("ZCZC end inpaths %s\n",hostname);
472 fprintf(stderr, "Processed %ld hosts, %ld links.\n", nhosts, nlinks);
479 main(int argc, char *argv[])
483 char *df=NULL, *rf=NULL;
485 for (i=0; i<HASH_TBL; i++)
489 while ((i=getopt(argc, argv, "pd:u:r:v:"))!=EOF)
492 /* read Path lines from stdin */
495 /* make a dump to the named file */
498 /* read dump from the named file */
499 if (readdumpfile(optarg)<0)
503 /* make a report for the named site */
506 /* control report verbosity */
507 vf=atoi(optarg); break;
509 fprintf(stderr, "unknown option %c\n", i);