aboutsummaryrefslogtreecommitdiff
path: root/src/stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stat.c')
-rw-r--r--src/stat.c419
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);
+}
+

Return to:

Send suggestions and report system problems to the System administrator.