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