/* $Id: stat.c,v 1.9.2.1 2003/04/08 19:59:34 d_sergienko Exp $
*****************************************************************************
* HPT --- FTN NetMail/EchoMail Tosser
*****************************************************************************
* Copyright (C) 2002-2003
*
* Husky development team
*
* http://husky.sf.net
*
* This file is part of HPT.
*
* HPT is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* HPT is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HPT; see the file COPYING. If not, write to the Free
* Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*****************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fidoconf/fidoconf.h>
#include <fidoconf/common.h>
#include <fidoconf/xstr.h>
#include <fidoconf/log.h>
#include "stat.h"
#define REV_MIN 1 /* min revision we can read */
#define REV_CUR 1 /* revision we write */
#define REV_MAX 1 /* max revision we can read */
/*#define STAT_ALONE*/ /* complie stat.c w/o hpt */
#define STAT_DEBUG
#ifdef STAT_ALONE
# define msg(s) fprintf(stderr, __FILE__ ":%u: %s %s", __LINE__, s)
# define msg2(s,s2) fprintf(stderr, __FILE__ ":%u: %s %s", __LINE__, s, s2)
#else
# define msg(s) w_log(LL_ALERT, __FILE__ ":%u: %s", __LINE__, s)
# define msg2(s,s2) w_log(LL_ALERT, __FILE__ ":%u: %s %s", __LINE__, s, s2)
#endif
/* internal address record */
typedef struct _st_addr {
short zone, net, node, point;
} st_addr;
/* link stats data */
typedef struct _stat_link {
st_addr addr;
long in, out, dupe, bad;
long inb, outb;
} stat_link;
/* links chain record */
typedef struct _chain_link{
stat_link link;
struct _chain_link *next;
} chain_link;
/* echo stats internal record */
typedef struct _stat_echo {
struct _stat_echo *next;
short links;
chain_link *chain;
short tag_len;
char *tag;
} stat_echo;
/* prototypes */
int acmp(hs_addr *a1, st_addr *a2);
int acmp2(st_addr *a1, st_addr *a2);
int write_echo(FILE *F, stat_echo *e);
stat_echo *read_echo(FILE *F);
void free_echo(stat_echo *e);
void debug_out(stat_echo *e);
static stat_echo *statecho = NULL; /* first echo in echoes chain */
static int do_stat = 1; /* drop to 0 if critical error */
/* Compare addresses (fidoconfig hs_addr and local st_addr)
*/
int acmp(hs_addr *a1, st_addr *a2)
{
if (a1->zone != a2->zone) return (a1->zone < a2->zone) ? -1 : 1;
if (a1->net != a2->net) return (a1->net < a2->net) ? -1 : 1;
if (a1->node != a2->node) return (a1->node < a2->node) ? -1 : 1;
if (a1->point != a2->point) return (a1->point < a2->point) ? -1 : 1;
return 0;
}
/* Compare addresses (two local st_addr)
*/
int acmp2(st_addr *a1, st_addr *a2)
{
if (a1->zone != a2->zone) return (a1->zone < a2->zone) ? -1 : 1;
if (a1->net != a2->net) return (a1->net < a2->net) ? -1 : 1;
if (a1->node != a2->node) return (a1->node < a2->node) ? -1 : 1;
if (a1->point != a2->point) return (a1->point < a2->point) ? -1 : 1;
return 0;
}
void put_stat(s_area *echo, hs_addr *link, st_type type, long len)
{
stat_echo *cur = statecho, *prev = NULL, *me=NULL;
chain_link *curl, *prevl;
int res;
if(!echo || !link){ msg("Parameter is NULL"); return; }
if (!do_stat) return;
/* find pos and insert echo */
while ( (res = (cur != NULL) ? sstricmp(echo->areaName, cur->tag) : -1) )
if (res < 0) {
me = calloc(1,sizeof(*me));
if (me == NULL) { msg("Out of memory"); do_stat = 0; return; }
if( (me->tag_len = sstrlen(echo->areaName)) )
me->tag = strdup(echo->areaName);
me->links = 0; me->chain = NULL;
if (prev != NULL) prev->next = me; else statecho = me;
me->next = cur; cur = me; break;
}
else { prev = cur; cur = cur->next; }
/* find pos and insert link into chain */
if (cur == NULL) return;
curl = cur->chain; prevl = NULL;
while ( (res = (curl != NULL) ? acmp(link, &(curl->link.addr)) : -1) ) {
if (res < 0) {
chain_link *me;
me = calloc(1,sizeof(*me));
if (me == NULL) { msg("Out of memory"); do_stat = 0; return; }
cur->links++;
me->link.addr.zone = link->zone;
me->link.addr.net = link->net;
me->link.addr.node = link->node;
me->link.addr.point = link->point;
me->link.in = me->link.out = me->link.bad = me->link.dupe = 0;
me->link.inb = me->link.outb = 0;
if (prevl != NULL) prevl->next = me; else cur->chain = me;
me->next = curl; curl = me; break;
}
else { prevl = curl; curl = curl->next; }
}
/* set values */
if (curl == NULL) return;
switch (type) {
case stNORM: curl->link.in++; curl->link.inb += len; break;
case stBAD: curl->link.bad++; break;
case stDUPE: curl->link.dupe++; break;
case stOUT: curl->link.out++; curl->link.outb += len; break;
}
}
void upd_stat(char *file)
{
stat_echo *cur, *next;
FILE *OLD = NULL, *NEW = NULL;
char *oldf = NULL, *newf = NULL;
struct {
char vk[2];
short rev;
time_t t0;
char xxx[8];
} hdr = {"vk", REV_CUR, 0, {0,0,0,0,0,0,0,0}}, ohdr;
if (!do_stat) { msg("stat was disabled"); return; }
if (statecho == NULL) {
#ifdef STAT_DEBUG
msg("Nothing new to stat");
#endif
return;
}
#ifdef STAT_DEBUG
msg("Current statistic below");
debug_out(NULL);
msg("Cumulative statistic below");
#endif
/* read old base: hpt.sta */
oldf = file;
OLD = fopen(oldf, "rb");
if (OLD != NULL) {
int rc = fread(&ohdr, sizeof(ohdr), 1, OLD);
if (rc < 1) {
#ifdef STAT_DEBUG
msg2("Ignoring empty or corrupt stat base", oldf);
#endif
fclose(OLD); OLD = NULL;
}
else if (ohdr.rev < REV_MIN || ohdr.rev > REV_MAX) {
msg2("Incompatible stat base", oldf); fclose(OLD);
do_stat = 0; return;
}
}
/* make new base: hpt.st$ */
if ( (newf = sstrdup(oldf)) ) newf[strlen(newf)-1] = '$';
else { msg("Out of memory"); if (OLD != NULL) fclose(OLD); return; }
NEW = fopen(newf, "wb");
if (NEW == NULL) {
msg2("Can't create tmp-file", newf);
if (OLD != NULL) fclose(OLD);
do_stat = 0; return;
}
hdr.t0 = OLD ? ohdr.t0 : time(NULL);
fwrite(&hdr, sizeof(hdr), 1, NEW);
/* main loop */
cur = statecho;
while (do_stat && OLD && !feof(OLD)) {
stat_echo *old/* = read_echo(OLD)*/;
old = read_echo(OLD);
if (!do_stat || old == NULL) break;
/* write new echoes with lesser names */
while ( cur && sstricmp(cur->tag, old->tag) < 0 ) {
write_echo(NEW, cur);
cur = cur->next;
}
/* update current echo */
if ( cur && sstricmp(cur->tag, old->tag) == 0 ) {
chain_link *prevl = NULL;
chain_link *newl = cur->chain;
chain_link *oldl = old->chain;
while (oldl != NULL) {
int res = newl ? acmp2(&(oldl->link.addr), &(newl->link.addr)) : -1;
/* insert link before current */
if (res < 0) {
chain_link *me = malloc(sizeof(*me));
if (me == NULL) { msg("Out of memory"); do_stat = 0; continue; }
memcpy(&(me->link), &(oldl->link), sizeof(me->link));
if (prevl != NULL) prevl->next = me; else cur->chain = me;
me->next = newl;
cur->links++;
oldl = oldl->next;
}
/* combine links data into current */
else if (res == 0) {
newl->link.in += oldl->link.in;
newl->link.out += oldl->link.out;
newl->link.dupe += oldl->link.dupe;
newl->link.bad += oldl->link.bad;
newl->link.inb += oldl->link.inb;
newl->link.outb += oldl->link.outb;
oldl = oldl->next;
prevl = newl; newl = newl->next;
}
/* to append link after current just advance to the next link */
else if (newl != NULL) { prevl = newl; newl = newl->next; }
}
write_echo(NEW, cur);
cur = cur->next;
}
/* keep old echo unchanged */
else write_echo(NEW, old);
free_echo(old);
}
/* write new echoes to the end of base */
while (do_stat && cur != NULL) { write_echo(NEW, cur); cur = cur->next; }
cur = statecho;
while (cur != NULL) { next = cur->next; free_echo(cur); cur = next; }
/* unlink old and rename new */
fclose(NEW); if (OLD) fclose(OLD);
if (do_stat) { remove(oldf); rename(newf, oldf); }
else { remove(newf); msg("New stat base is not written"); }
}
int write_echo(FILE *F, stat_echo *e)
{
chain_link *cl;
int tst;
short real_links = 0;
if (!e || !e->links) return 0;
#ifdef STAT_DEBUG
debug_out(e);
#endif
cl = e->chain; while (cl) { real_links++; cl = cl->next; }
/* tst = fwrite(&(e->links), sizeof(e->links), 1, F); */
tst = fwrite(&(real_links), sizeof(e->links), 1, F);
tst += fwrite(&(e->tag_len), sizeof(e->tag_len), 1, F);
tst += fwrite(e->tag, e->tag_len, 1, F);
if (tst < 3) { msg("Write error"); do_stat = 0; return 0; }
cl = e->chain;
while (cl) {
tst = fwrite(&(cl->link), sizeof(cl->link), 1, F);
if (tst < 1) { msg("Write error"); do_stat = 0; return 0; }
cl = cl->next;
}
return 1;
}
stat_echo *read_echo(FILE *F)
{
stat_echo *old;
chain_link *l, *prev = NULL;
short ol, ot;
int i, tst;
tst = fread(&ol, sizeof(ol), 1, F); if (tst < 1) return NULL;
tst = fread(&ot, sizeof(ot), 1, F); if (tst < 1) return NULL;
old = calloc( 1, sizeof (stat_echo) );
if (old == NULL) { msg("Out of memory"); do_stat = 0; return NULL; }
old->links = ol;
old->tag_len = ot;
old->chain = NULL;
old->tag = calloc( 1, ot+1 );
tst = fread(old->tag, ot, 1, F);
if (tst < 1) { msg("Read error"); free_echo(old); do_stat = 0; return NULL; }
/* read links */
for (i = 0; i < ol; i++) {
l = malloc(sizeof(*l));
if (l == NULL) { msg("Out of memory"); do_stat = 0; return NULL; }
if (prev != NULL) prev->next = l; else old->chain = l;
l->next = NULL;
tst = fread(&(l->link), sizeof(l->link), 1, F);
if (tst < 1) { msg("Read error"); free_echo(old); do_stat = 0; return NULL; }
prev = l;
}
return old;
}
void free_echo(stat_echo *e) {
chain_link *cur, *next;
cur = e->chain;
while (cur != NULL) { next = cur->next; nfree(cur); cur = next; }
nfree(e->tag);
nfree(e);
}
void debug_out(stat_echo *e) {
#ifdef STAT_DEBUG
stat_echo *cur = e ? e : statecho;
chain_link *curl;
while (cur != NULL) {
curl = cur->chain;
while (curl != NULL) {
#ifdef STAT_ALONE
fprintf(stderr, "%s, %d:%d/%d.%d: i=%d/o=%d/b=%d/d=%d ib=%d/ob=%d\n",
#else
w_log(LL_DEBUGS, "%s, %d:%d/%d.%d: i=%d/o=%d/b=%d/d=%d ib=%d/ob=%d",
#endif
cur->tag,
curl->link.addr.zone, curl->link.addr.net, curl->link.addr.node, curl->link.addr.point,
curl->link.in, curl->link.out, curl->link.bad, curl->link.dupe,
curl->link.inb, curl->link.outb);
curl = curl->next;
}
if (e) break;
cur = cur->next;
}
#endif
}
syntax highlighted by Code2HTML, v. 0.9.1