1 /* $Id$
2  *****************************************************************************
3  * HPT --- FTN NetMail/EchoMail Tosser
4  *****************************************************************************
5  * Copyright (C) 2002-2003
6  *
7  * Husky development team
8  *
9  * http://husky.sf.net
10  *
11  * This file is part of HPT.
12  *
13  * HPT is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU General Public License as published by the
15  * Free Software Foundation; either version 2, or (at your option) any
16  * later version.
17  *
18  * HPT is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with HPT; see the file COPYING.  If not, write to the Free
25  * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
26  *****************************************************************************
27  */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 
34 #include <huskylib/xstr.h>
35 #include <huskylib/log.h>
36 #include "fidoconf.h"
37 #include "common.h"
38 
39 /* export functions from DLL */
40 #define DLLEXPORT
41 #include <huskylib/huskyext.h>
42 #include "stat.h"
43 
44 #define REV_MIN 1            /* min revision we can read */
45 #define REV_CUR 1            /* revision we write */
46 #define REV_MAX 1            /* max revision we can read */
47 
48 /*#define STAT_ALONE*/         /* complie stat.c w/o hpt */
49 /*#define STAT_DEBUG*/         /* output stat info each write */
50 
51 #ifdef STAT_ALONE
52 #  define msg(s) fprintf(stderr,      __FILE__ ":%u: %s %s", __LINE__, s)
53 #  define msg2(s,s2) fprintf(stderr,  __FILE__ ":%u: %s %s", __LINE__, s, s2)
54 #else
55 #  define msg(s) w_log(LL_ALERT,  __FILE__ ":%u: %s", __LINE__, s)
56 #  define msg2(s,s2) w_log(LL_ALERT,  __FILE__ ":%u: %s %s", __LINE__, s, s2)
57 #endif
58 
59 /* internal address record */
60 typedef struct _st_addr {
61     INT16 zone, net, node, point;
62 } st_addr;
63 
64 /* link stats data */
65 typedef struct _stat_link {
66     st_addr addr;
67     INT32 in, out, dupe, bad;
68     INT32 inb, outb;
69 } stat_link;
70 
71 /* links chain record */
72 typedef struct  _chain_link{
73     stat_link link;
74     struct _chain_link *next;
75 } chain_link;
76 
77 /* echo stats internal record */
78 typedef struct _stat_echo {
79     struct _stat_echo *next;
80     INT16              links;
81     chain_link        *chain;
82     INT16              tag_len;
83     char              *tag;
84 } stat_echo;
85 
86 /* prototypes */
87 int acmp(hs_addr *a1, st_addr *a2);
88 int acmp2(st_addr *a1, st_addr *a2);
89 int write_echo(FILE *F, stat_echo *e);
90 stat_echo *read_echo(FILE *F);
91 void free_echo(stat_echo *e);
92 void debug_out(stat_echo *e);
93 
94 static stat_echo *statecho = NULL;  /* first echo in echoes chain */
95 static int do_stat = 1;                /* drop to 0 if critical error */
96 
97 /* Compare addresses (fidoconfig hs_addr and local st_addr)
98  */
acmp(hs_addr * a1,st_addr * a2)99 int acmp(hs_addr *a1, st_addr *a2)
100 {
101     if (a1->zone  != a2->zone)  return (a1->zone  < a2->zone)  ? -1 : 1;
102     if (a1->net   != a2->net)   return (a1->net   < a2->net)   ? -1 : 1;
103     if (a1->node  != a2->node)  return (a1->node  < a2->node)  ? -1 : 1;
104     if (a1->point != a2->point) return (a1->point < a2->point) ? -1 : 1;
105     return 0;
106 }
107 
108 /* Compare addresses (two local st_addr)
109  */
acmp2(st_addr * a1,st_addr * a2)110 int acmp2(st_addr *a1, st_addr *a2)
111 {
112     if (a1->zone  != a2->zone)  return (a1->zone  < a2->zone)  ? -1 : 1;
113     if (a1->net   != a2->net)   return (a1->net   < a2->net)   ? -1 : 1;
114     if (a1->node  != a2->node)  return (a1->node  < a2->node)  ? -1 : 1;
115     if (a1->point != a2->point) return (a1->point < a2->point) ? -1 : 1;
116     return 0;
117 }
118 
put_stat(s_area * echo,hs_addr * link,st_type type,INT32 len)119 void put_stat(s_area *echo, hs_addr *link, st_type type, INT32 len)
120 {
121     stat_echo *cur = statecho, *prev = NULL, *me=NULL;
122     chain_link *curl, *prevl;
123     int res;
124 
125     if(!echo || !link){ msg("Parameter is NULL"); return; }
126     if (!do_stat) return;
127     /* find pos and insert echo */
128     while ( (res = (cur != NULL) ? sstricmp(echo->areaName, cur->tag) : -1) != 0 )
129         if (res < 0) {
130             me = calloc(1,sizeof(*me));
131             if (me == NULL) { msg("Out of memory"); do_stat = 0; return; }
132 
133             if( (me->tag_len = sstrlen(echo->areaName)) )
134                 me->tag = strdup(echo->areaName);
135             me->links = 0; me->chain = NULL;
136             if (prev != NULL) prev->next = me; else statecho = me;
137             me->next = cur; cur = me; break;
138         }
139         else { prev = cur; cur = cur->next; }
140         /* find pos and insert link into chain */
141         if (cur == NULL) return;
142         curl = cur->chain; prevl = NULL;
143         while ( (res = (curl != NULL) ? acmp(link, &(curl->link.addr)) : -1) != 0 ) {
144             if (res < 0) {
145                 chain_link *me;
146                 me = calloc(1,sizeof(*me));
147                 if (me == NULL) { msg("Out of memory"); do_stat = 0; return; }
148 
149                 cur->links++;
150                 me->link.addr.zone  = link->zone;
151                 me->link.addr.net   = link->net;
152                 me->link.addr.node  = link->node;
153                 me->link.addr.point = link->point;
154                 me->link.in = me->link.out = me->link.bad = me->link.dupe = 0;
155                 me->link.inb = me->link.outb = 0;
156                 if (prevl != NULL) prevl->next = me; else cur->chain = me;
157                 me->next = curl; curl = me; break;
158             }
159             else { prevl = curl; curl = curl->next; }
160         }
161         /* set values */
162         if (curl == NULL) return;
163         switch (type) {
164         case stNORM: curl->link.in++; curl->link.inb += len; break;
165         case stBAD:  curl->link.bad++; break;
166         case stDUPE: curl->link.dupe++; break;
167         case stOUT:  curl->link.out++; curl->link.outb += len; break;
168         default:     break;
169         }
170 }
171 
upd_stat(char * file)172 void upd_stat(char *file)
173 {
174     stat_echo *cur, *next;
175     FILE *OLD = NULL, *NEW = NULL;
176     char *oldf = NULL, *newf = NULL;
177     struct {
178         char vk[2];
179         short rev;
180         time_t t0;
181         char xxx[8];
182     } hdr = {{'v','k'}, REV_CUR, 0, {0,0,0,0,0,0,0,0}}, ohdr;
183 
184     if (!do_stat) { msg("stat was disabled"); return; }
185     if (statecho == NULL) {
186 #ifdef STAT_DEBUG
187         msg("Nothing new to stat");
188 #endif
189         return;
190     }
191 #ifdef STAT_DEBUG
192     msg("Current statistics below");
193     debug_out(NULL);
194     msg("Cumulative statistics below");
195 #endif
196     /* read old base: hpt.sta */
197     oldf = file;
198     OLD = fopen(oldf, "rb");
199     if (OLD != NULL) {
200         int rc = fread(&ohdr, sizeof(ohdr), 1, OLD);
201         if (rc < 1) {
202 #ifdef STAT_DEBUG
203             msg2("Ignoring empty or corrupt stat base", oldf);
204 #endif
205             fclose(OLD); OLD = NULL;
206         }
207         else if (ohdr.rev < REV_MIN || ohdr.rev > REV_MAX) {
208             msg2("Incompatible stat base", oldf); fclose(OLD);
209             do_stat = 0; return;
210         }
211     }
212     /* make new base: hpt.st$ */
213     if ( (newf = sstrdup(oldf)) != NULL ) newf[strlen(newf)-1] = '$';
214     else { msg("Out of memory"); if (OLD != NULL) fclose(OLD); return; }
215     NEW = fopen(newf, "wb");
216     if (NEW == NULL) {
217         msg2("Can't create tmp-file", newf);
218         msg2(" error: ", strerror(errno));
219         if (OLD != NULL) fclose(OLD);
220         nfree(newf);
221         do_stat = 0; return;
222     }
223     hdr.t0 = OLD ? ohdr.t0 : time(NULL);
224     if( fwrite(&hdr, sizeof(hdr), 1, NEW) != 1 ) {
225       msg2("Can't write to the tmp-file", newf);
226       msg2(" error:", strerror(errno));
227       if (OLD != NULL) fclose(OLD);
228       fclose(NEW);
229       nfree(newf);
230       do_stat = 0; return;
231     }
232     /* main loop */
233     cur = statecho;
234     while (do_stat && OLD && !feof(OLD)) {
235         stat_echo *old/* = read_echo(OLD)*/;
236         old = read_echo(OLD);
237         if (!do_stat || old == NULL) break;
238         /* write new echoes with lesser names */
239         while ( cur && sstricmp(cur->tag, old->tag) < 0 ) {
240             write_echo(NEW, cur);
241             cur = cur->next;
242         }
243         /* update current echo */
244         if ( cur && sstricmp(cur->tag, old->tag) == 0 ) {
245             chain_link *prevl = NULL;
246             chain_link *newl  = cur->chain;
247             chain_link *oldl  = old->chain;
248             while (oldl != NULL) {
249                 int res = newl ? acmp2(&(oldl->link.addr), &(newl->link.addr)) : -1;
250                 /* insert link before current */
251                 if (res < 0) {
252                     chain_link *me = malloc(sizeof(*me));
253                     if (me == NULL) { msg("Out of memory"); do_stat = 0; continue; }
254 
255                     memcpy(&(me->link), &(oldl->link), sizeof(me->link));
256                     if (prevl != NULL) prevl->next = me; else cur->chain = me;
257                     me->next = newl;
258                     cur->links++;
259                     oldl = oldl->next;
260                 }
261                 /* combine links data into current */
262                 else if (res == 0) {
263                     newl->link.in   += oldl->link.in;
264                     newl->link.out  += oldl->link.out;
265                     newl->link.dupe += oldl->link.dupe;
266                     newl->link.bad  += oldl->link.bad;
267                     newl->link.inb  += oldl->link.inb;
268                     newl->link.outb += oldl->link.outb;
269                     oldl = oldl->next;
270                     prevl = newl; newl = newl->next;
271                 }
272                 /* to append link after current just advance to the next link */
273                 else if (newl != NULL) { prevl = newl; newl = newl->next; }
274             }
275             write_echo(NEW, cur);
276             cur = cur->next;
277         }
278         /* keep old echo unchanged */
279         else write_echo(NEW, old);
280         free_echo(old);
281     }
282     /* write new echoes to the end of base */
283     while (do_stat && cur != NULL) { write_echo(NEW, cur); cur = cur->next; }
284     cur = statecho;
285     while (cur != NULL) { next = cur->next; free_echo(cur); cur = next; }
286     /* unlink old and rename new */
287     fclose(NEW); if (OLD) fclose(OLD);
288     if (do_stat) { remove(oldf); rename(newf, oldf); }
289     else { remove(newf); msg("New stat base is not written"); }
290     nfree(newf);
291 }
292 
write_echo(FILE * F,stat_echo * e)293 int write_echo(FILE *F, stat_echo *e)
294 {
295     chain_link *cl;
296     int tst;
297     short real_links = 0;
298 
299     if (!e || !e->links) return 0;
300 #ifdef STAT_DEBUG
301     debug_out(e);
302 #endif
303     cl = e->chain; while (cl) { real_links++; cl = cl->next; }
304 /*    tst = fwrite(&(e->links), sizeof(e->links), 1, F); */
305     tst = fwrite(&(real_links), sizeof(e->links), 1, F);
306     tst += fwrite(&(e->tag_len), sizeof(e->tag_len), 1, F);
307     tst += fwrite(e->tag, e->tag_len, 1, F);
308     if (tst < 3) { msg("Write error"); do_stat = 0; return 0; }
309 
310     cl = e->chain;
311     while (cl) {
312         tst = fwrite(&(cl->link), sizeof(cl->link), 1, F);
313         if (tst < 1) { msg("Write error"); do_stat = 0; return 0; }
314         cl = cl->next;
315     }
316     return 1;
317 }
318 
read_echo(FILE * F)319 stat_echo *read_echo(FILE *F)
320 {
321     stat_echo *old;
322     chain_link *l, *prev = NULL;
323     short ol, ot;
324     int i, tst;
325 
326     tst = fread(&ol, sizeof(ol), 1, F); if (tst < 1) return NULL;
327     tst = fread(&ot, sizeof(ot), 1, F); if (tst < 1) return NULL;
328 
329     old = calloc( 1, sizeof (stat_echo) );
330     if (old == NULL) { msg("Out of memory"); do_stat = 0; return NULL; }
331 
332     old->links = ol;
333     old->tag_len = ot;
334     old->chain = NULL;
335     old->tag = calloc( 1, ot+1 );
336     if (old->tag == NULL) { msg("Out of memory"); do_stat = 0; return NULL; }
337     tst = fread(old->tag, ot, 1, F);
338 
339     if (tst < 1) { msg("Read error, advstat database is broken"); free_echo(old); do_stat = 0; return NULL; }
340     /* read links */
341     for (i = 0; i < ol; i++) {
342         l = malloc(sizeof(*l));
343         if (l == NULL) { msg("Out of memory"); free_echo(old); do_stat = 0; return NULL; }
344 
345         if (prev != NULL) prev->next = l; else old->chain = l;
346         l->next = NULL;
347         tst = fread(&(l->link), sizeof(l->link), 1, F);
348         if (tst < 1) { msg("Read error, advstat database is broken"); free_echo(old); do_stat = 0; return NULL; }
349         prev = l;
350     }
351     return old;
352 }
353 
free_echo(stat_echo * e)354 void free_echo(stat_echo *e) {
355     chain_link *cur, *next;
356     cur = e->chain;
357     while (cur != NULL) { next = cur->next; nfree(cur); cur = next; }
358     nfree(e->tag);
359     nfree(e);
360 }
361 
debug_out(stat_echo * e)362 void debug_out(stat_echo *e) {
363 
364     unused(e);
365 
366 #ifdef STAT_DEBUG
367     stat_echo *cur = e ? e : statecho;
368     chain_link *curl;
369     while (cur != NULL) {
370         curl = cur->chain;
371         while (curl != NULL) {
372 #ifdef STAT_ALONE
373             fprintf(stderr, "%s, %d:%d/%d.%d: i=%d/o=%d/b=%d/d=%d ib=%d/ob=%d\n",
374 #else
375                 w_log(LL_DEBUGS, "%s, %d:%d/%d.%d: i=%d/o=%d/b=%d/d=%d ib=%d/ob=%d",
376 #endif
377                 cur->tag,
378                 curl->link.addr.zone, curl->link.addr.net, curl->link.addr.node, curl->link.addr.point,
379                 curl->link.in, curl->link.out, curl->link.bad, curl->link.dupe,
380                 curl->link.inb, curl->link.outb);
381             curl = curl->next;
382         }
383         if (e) break;
384         cur = cur->next;
385     }
386 #endif
387 }
388