diff options
Diffstat (limited to 'src/stat.c')
-rw-r--r-- | src/stat.c | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/src/stat.c b/src/stat.c new file mode 100644 index 0000000..2751873 --- /dev/null +++ b/src/stat.c @@ -0,0 +1,419 @@ +/* This file is part of tagr. + Copyright (C) 2005, Sergey Poznyakoff + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <glob.h> +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free +#include <obstack.h> +#include <tagr.h> + +typedef int (*ovf_t) (struct traffic_history *th, struct traffic_record *tr, + time_t now); + +void +interpolate (queue_t *q, + time_t step, time_t now, + time_t last_time, struct traffic_history *last_rates, + time_t interval, double inrate, double outrate, + ovf_t ovf, + struct traffic_record *tr) +{ + time_t next; + + for (next = last_time + step; next <= now; next += step) + { + struct traffic_history th; + + th.inrate = (inrate - last_rates->inrate) * (next - last_time) + / interval + last_rates->inrate; + th.outrate = (outrate - last_rates->outrate) * (next - last_time) + / interval + last_rates->outrate; + verbose (3, "Insert %lu %g %g", next, th.inrate, th.outrate); + if (queue_xchg (q, &th) && ovf) + ovf (&th, tr, now); + } +} + +int +overflow (struct traffic_history *th, + struct traffic_record *tr, + time_t now, + ovf_t ovf, + struct avg_acc *avg, + queue_t *q, + int maxcount, + int step) +{ + if (now - avg->time > step) + { + struct traffic_history *lastp = queue_get_tail (q); + if (lastp) + interpolate (q, + step, + now, + avg->time, + lastp, + now - avg->time, + avg->inrate, avg->outrate, + ovf, tr); + else + { + struct traffic_history tmp; + tmp.inrate = avg->inrate; + tmp.outrate = avg->outrate; + verbose (3, "Insert %lu %g %g", now, tmp.inrate, tmp.outrate); + queue_put (q, &tmp); + } + + avg->inrate = avg->outrate = 0; + avg->count = 0; + avg->time = now; + } + avg->inrate = (avg->count * avg->inrate + th->inrate) / (avg->count + 1); + avg->outrate = (avg->count * avg->outrate + th->outrate) / (avg->count + 1); + avg->count++; +} + +int +ovf_monthly (struct traffic_history *th, struct traffic_record *tr, time_t now) +{ + overflow (th, tr, now, NULL, &tr->year_avg, &tr->year_hist, + YEAR_COUNT, YEAR_SAMPLE); +} + +int +ovf_weekly (struct traffic_history *th, struct traffic_record *tr, time_t now) +{ + overflow (th, tr, now, ovf_monthly, &tr->month_avg, &tr->month_hist, + MONTH_COUNT, MONTH_SAMPLE); +} + +int +ovf_daily (struct traffic_history *th, struct traffic_record *tr, time_t now) +{ + overflow (th, tr, now, ovf_weekly, &tr->week_avg, &tr->week_hist, + WEEK_COUNT, WEEK_SAMPLE); +} + +void +update_stats (SD *sd, struct traffic_record *tr) +{ + time_t interval; + double inrate, outrate; + + struct traffic_history *lastp = queue_get_tail (&tr->day_hist); + + interval = sd->t - tr->last.time; + if (interval == 0) + { + logmsg (L_ERR, "Ignoring zero interval"); + return; + } + inrate = (double) sd->in / interval; + outrate = (double) sd->out / interval; + if (lastp) + { + interpolate (&tr->day_hist, + DAY_SAMPLE, + sd->t, + tr->last.time, + &tr->last_rates, + interval, + inrate, outrate, + ovf_daily, tr); + } + else + { + struct traffic_history th; + interval = sd->t - tr->last.time; + th.inrate = inrate; + th.outrate = outrate; + queue_put (&tr->day_hist, &th); + } + tr->last.time = sd->t; + tr->last.in = sd->in; + tr->last.out = sd->out; + tr->last_rates.inrate = inrate; + tr->last_rates.outrate = outrate; +} + + + +#define hist_rec last_sample + +static void +_convert (queue_t *q, + struct traffic_record *tr, struct hist_rec *hist, size_t count, + time_t sample_interval) +{ + size_t i; + struct hist_rec *hp; + + tr->last.time = hist[count-1].time - sample_interval; + for (hp = hist + count - 1; hp >= hist; hp--) + { + time_t interval; + double inrate, outrate; + struct traffic_history *lastp = queue_get_tail (q); + + interval = hp->time - tr->last.time; + inrate = (double) hp->in; + outrate = (double) hp->out; + + if (interval == 0) + { + logmsg (L_ERR, "Ignoring zero interval"); + break; + } + if (lastp) + { + interpolate (q, + sample_interval, + hp->time, + tr->last.time, + &tr->last_rates, + interval, + inrate, outrate, + NULL, tr); + } + else + { + struct traffic_history th; + interval = hp->time - tr->last.time; + th.inrate = inrate; + th.outrate = outrate; + queue_put (q, &th); + } + + tr->last.time = hp->time; + tr->last.in = hp->in; + tr->last.out = hp->out; + tr->last_rates.inrate = inrate; + tr->last_rates.outrate = outrate; + } +} + +static void +convert_yearly (struct traffic_record *tr, struct hist_rec *hp, size_t count) +{ + _convert (&tr->year_hist, tr, hp, YEAR_COUNT + 1, YEAR_SAMPLE); +} + +static void +convert_monthly (struct traffic_record *tr, struct hist_rec *hp, size_t count) +{ + if (count > MONTH_COUNT+1) + convert_yearly (tr, hp + MONTH_COUNT + 1, count - (MONTH_COUNT + 1)); + _convert (&tr->month_hist, tr, hp, MONTH_COUNT + 1, MONTH_SAMPLE); +} + +static void +convert_weekly (struct traffic_record *tr, struct hist_rec *hp, size_t count) +{ + if (count > WEEK_COUNT+1) + convert_monthly (tr, hp + WEEK_COUNT + 1, count - (WEEK_COUNT + 1)); + _convert (&tr->week_hist, tr, hp, WEEK_COUNT + 1, WEEK_SAMPLE); +} + +static void +convert_daily (struct traffic_record *tr, struct hist_rec *hp, size_t count) +{ + if (count > DAY_COUNT+1) + convert_weekly (tr, hp + DAY_COUNT + 1, count - (DAY_COUNT + 1)); + _convert (&tr->day_hist, tr, hp, DAY_COUNT + 1, DAY_SAMPLE); +} + +static void +convert_stats (SD *sd, struct hist_rec *last, + struct hist_rec *hp, size_t count) +{ + struct traffic_record *trp, tr; + + memset (&tr, 0, sizeof tr); + tr_init (&tr); + + convert_daily (&tr, hp, count); + + read_db (sd, &trp); + *trp = tr; + trp->last = *last; + write_db (sd, trp); + free (trp); +} + +static int +import_log (const char *name) +{ + FILE *fp; + int rc = 0; + struct obstack stk; + struct hist_rec last, hist, *hp; + unsigned long inmax, outmax; + size_t count = 0; + char *buf = NULL; + size_t bufsize = 0; + size_t line = 1; + time_t cur; + + fp = fopen (name, "r"); + if (!fp) + { + logmsg (L_ERR, "cannot open `%s': %s", name, strerror (errno)); + return 1; + } + verbose (2, "Importing %s", name); + + + if (fscanf (fp, "%ld %lu %lu\n", &last.time, &last.in, &last.out) != 3) + { + logmsg (L_ERR, "%s:1: Unexpected number of fields", name); + fclose (fp); + return 1; + } + cur = last.time; + + obstack_init (&stk); + while (getline (&buf, &bufsize, fp) > 0) + { + int i; + unsigned long rd[5]; + + line++; + + if (sscanf (buf, "%lu %lu %lu %lu %lu", + &rd[0], &rd[1], &rd[2], &rd[3], &rd[4]) < 5) + { + rd[3] = rd[1]; + rd[4] = rd[2]; + } + + for (i = 0; i <= 4; i++) + rd[i] = rd[i] < 0 ? 0 : rd[i]; + + hist.time = rd[0]; + if (hist.time > cur) + { + logmsg (L_WARNING, "%s:%lu: is corrupted", name, line); + break; + } + cur = hist.time; + hist.in = rd[1]; + hist.out = rd[2]; + if (inmax < rd[3]) + inmax = rd[3]; + if (inmax < hist.in) + inmax = hist.in; + if (outmax < rd[4]) + outmax = rd[4]; + if (outmax < hist.out) + outmax = hist.out; + + obstack_grow (&stk, &hist, sizeof (hist)); + count++; + } + fclose (fp); + free (buf); + + hp = obstack_finish (&stk); + if (count) + { + SD *sd; + + char *p = strrchr (name, '/'); + char *base = xstrdup (p ? p + 1 : name); + p = strrchr (base, '.'); + if (p) + *p = 0; + + sd = find_router (base); + if (!sd) + { + logmsg (L_ERR, "cannot find router `%s'", base); + rc = 1; + } + else + convert_stats (sd, &last, hp, count); + } + + obstack_free (&stk, NULL); + return rc; +} + +void +import (const char *dirname) +{ + size_t count = 0; + struct stat st; + + verbose (2, "Examining `%s'", dirname); + + if (stat (dirname, &st)) + die (3, "cannot stat file `%s': %s", dirname, strerror (errno)); + else if (S_ISREG (st.st_mode)) + { + open_db (); + if (import_log (dirname) == 0) + count++; + close_db (); + } + else if (S_ISDIR (st.st_mode)) + { + char *pattern; + glob_t gl; + size_t i; + int rc; + + pattern = mkfilename (dirname, "*/", "*.log"); + rc = glob (pattern, 0, NULL, &gl); + free (pattern); + + switch (rc) + { + case 0: + open_db (); + for (i = 0; i < gl.gl_pathc; i++) + if (import_log (gl.gl_pathv[i]) == 0) + count++; + globfree (&gl); + close_db (); + break; + + case GLOB_NOSPACE: + die (3, "cannot scan directory: %s", strerror (ENOMEM)); + + case GLOB_ABORTED: + die (3, "scanning aborted"); + + case GLOB_NOMATCH: + break; + + default: + die (3, "cannot scan directory `%s'", dirname); + } + } + + verbose (1, "Number of imported log files: %d", count); +} + |