/* This file is part of tagr. Copyright (C) 2005, 2009 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 3, 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, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #define obstack_chunk_alloc malloc #define obstack_chunk_free free #include #include 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; struct traffic_history th; if (now - last_time <= step) { th.inrate = inrate; th.outrate = outrate; verbose (3, "Insert %lu %g %g", next, th.inrate, th.outrate); queue_put (q, &th); if (ovf) ovf (&th, tr, now); return; } for (next = last_time + step; next <= now; next += step) { 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); queue_put (q, &th); if (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) { verbose (2, "begin overflow_monthly %lu %g %g", now, th->inrate, th->outrate); overflow (th, tr, now, NULL, &tr->year_avg, &tr->year_hist, YEAR_COUNT, YEAR_SAMPLE); verbose (2, "end overflow_monthly"); } int ovf_weekly (struct traffic_history *th, struct traffic_record *tr, time_t now) { verbose (2, "begin overflow_weekly %lu %g %g", now, th->inrate, th->outrate); overflow (th, tr, now, ovf_monthly, &tr->month_avg, &tr->month_hist, MONTH_COUNT, MONTH_SAMPLE); verbose (2, "end overflow_daily"); } int ovf_daily (struct traffic_history *th, struct traffic_record *tr, time_t now) { verbose (2, "begin overflow_daily %lu %g %g", now, th->inrate, th->outrate); overflow (th, tr, now, ovf_weekly, &tr->week_avg, &tr->week_hist, WEEK_COUNT, WEEK_SAMPLE); verbose (2, "end overflow_daily"); } void update_stats (struct monitor *mon, struct traffic_sample *sample, struct traffic_record *tr) { time_t interval; double inrate, outrate; struct traffic_history *lastp = queue_get_tail (&tr->day_hist); interval = sample->time - tr->last.time; if (interval == 0) { logmsg (L_ERR, "Ignoring zero interval"); return; } inrate = (double) sample->in / interval; outrate = (double) sample->out / interval; if (lastp) { interpolate (&tr->day_hist, DAY_SAMPLE, sample->time, tr->last.time, &tr->last_rates, interval, inrate, outrate, ovf_daily, tr); } else { struct traffic_history th; interval = sample->time - tr->last.time; th.inrate = inrate; th.outrate = outrate; queue_put (&tr->day_hist, &th); } tr->last.time = sample->time; tr->last.in = sample->in; tr->last.out = sample->out; tr->last_rates.inrate = inrate; tr->last_rates.outrate = outrate; } static void compute_avg (struct avg_acc *avg, queue_t *q, struct traffic_sample *last) { int i, n = queue_count (q); avg->inrate = avg->outrate = 0; avg->count = n; for (i = 0; i < n; i++) { struct traffic_history *th = queue_get_ptr (q, i); avg->inrate += th->inrate; avg->outrate += th->outrate; } avg->inrate /= n; avg->outrate /= n; avg->time = last->time; } static void _convert (queue_t *q, ovf_t ovf, struct traffic_record *tr, struct traffic_sample *hist, size_t count, time_t sample_interval) { size_t i; struct traffic_sample *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, ovf, 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 traffic_sample *hp, size_t count) { verbose (2, "begin convert_yearly"); _convert (&tr->year_hist, NULL, tr, hp, count, YEAR_SAMPLE); compute_avg (&tr->year_avg, &tr->year_hist, &tr->last); verbose (2, "end convert_yearly"); } static void convert_monthly (struct traffic_record *tr, struct traffic_sample *hp, size_t count) { verbose (2, "begin convert_monthly"); if (count > MONTH_COUNT+1) convert_yearly (tr, hp + MONTH_COUNT + 1, count - (MONTH_COUNT + 1)); _convert (&tr->month_hist, ovf_monthly, tr, hp, MONTH_COUNT + 1, MONTH_SAMPLE); compute_avg (&tr->month_avg, &tr->month_hist, &tr->last); verbose (2, "end convert_monthly"); } static void convert_weekly (struct traffic_record *tr, struct traffic_sample *hp, size_t count) { verbose (2, "begin convert_weekly"); if (count > WEEK_COUNT+1) convert_monthly (tr, hp + WEEK_COUNT + 1, count - (WEEK_COUNT + 1)); _convert (&tr->week_hist, ovf_weekly, tr, hp, WEEK_COUNT + 1, WEEK_SAMPLE); compute_avg (&tr->week_avg, &tr->week_hist, &tr->last); verbose (2, "end convert_weekly"); } static void convert_daily (struct traffic_record *tr, struct traffic_sample *hp, size_t count) { verbose (2, "begin convert_daily"); if (count > DAY_COUNT+1) convert_weekly (tr, hp + DAY_COUNT + 1, count - (DAY_COUNT + 1)); _convert (&tr->day_hist, ovf_daily, tr, hp, DAY_COUNT + 1, DAY_SAMPLE); verbose (2, "end convert_daily"); } static void convert_stats (struct monitor *mon, struct traffic_sample *last, struct traffic_sample *hp, size_t count) { struct traffic_record *trp, tr; memset (&tr, 0, sizeof tr); tr_init (&tr); convert_daily (&tr, hp, count); read_db (mon, &trp); *trp = tr; trp->last = *last; write_db (mon, trp); free (trp); } static int import_log (const char *name) { FILE *fp; int rc = 0; struct obstack stk; struct traffic_sample 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) { struct monitor *mon; char *p = strrchr (name, '/'); char *base = xstrdup (p ? p + 1 : name); p = strrchr (base, '.'); if (p) *p = 0; mon = find_monitor (base); if (!mon) { logmsg (L_ERR, "cannot find monitor `%s'", base); rc = 1; } else convert_stats (mon, &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 (EX_OSERR, "cannot stat file `%s': %s", dirname, strerror (errno)); else if (S_ISREG (st.st_mode)) { open_db (TAGR_DB_WR); 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 (TAGR_DB_WR); 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 (EX_UNAVAILABLE, "cannot scan directory: %s", strerror (ENOMEM)); case GLOB_ABORTED: die (EX_UNAVAILABLE, "scanning aborted"); case GLOB_NOMATCH: break; default: die (EX_UNAVAILABLE, "cannot scan directory `%s'", dirname); } } verbose (1, "Number of imported log files: %d", count); }