/* This file is part of tagr.
Copyright (C) 2000, 2005, 2009 Max Bouglacoff, 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
unsigned lock_retry_count_option = 5;
unsigned lock_retry_timeout_option = 1;
static char *dbname;
static GDBM_FILE dbf;
static void
tagr_db_report (char *str)
{
logmsg (L_CRIT, "%s: %s", dbname, str);
}
int
open_db (int flag)
{
unsigned i;
dbname = mkfilename (basedir, TAGR_DBNAME, NULL);
for (i = 0; i < lock_retry_count_option; i++)
{
dbf = gdbm_open (dbname, 0,
flag == TAGR_DB_WR ? GDBM_WRCREAT : GDBM_READER,
TAGR_DBMODE, tagr_db_report);
if (dbf || errno != EAGAIN)
break;
sleep (lock_retry_timeout_option);
}
if (dbf == NULL)
{
logmsg (L_ERR, _("cannot open database %s: %s"),
dbname, gdbm_strerror (gdbm_errno));
return 1;
}
return 0;
}
void
close_db ()
{
gdbm_close (dbf);
free (dbname);
dbname = NULL;
}
/* FIXME: Spurious initialization of .size member. */
void
tr_init (struct traffic_record *tr)
{
tr->day_hist.queue = tr->history;
tr->day_hist.size = DAY_COUNT;
tr->week_hist.queue = tr->day_hist.queue + tr->day_hist.size;
tr->week_hist.size = WEEK_COUNT;
tr->month_hist.queue = tr->week_hist.queue + tr->week_hist.size;
tr->month_hist.size = MONTH_COUNT;
tr->year_hist.queue = tr->month_hist.queue + tr->month_hist.size;
tr->year_hist.size = YEAR_COUNT;
}
static void
_read_db (datum key, struct traffic_record **tr)
{
datum content;
content = gdbm_fetch (dbf, key);
if (content.dptr == NULL)
logmsg (L_NOTICE, _("record for %*.*s not found"),
key.dsize, key.dsize, key.dptr);
else if (content.dsize != sizeof **tr)
{
logmsg (L_ERR, _("wrong record size for %*.*s: %lu"),
key.dsize, key.dsize, key.dptr, content.dsize);
}
else
{
*tr = (struct traffic_record *) content.dptr;
tr_init (*tr);
return;
}
logmsg (L_NOTICE, _("creating record for %*.*s"),
key.dsize, key.dsize, key.dptr);
*tr = xmalloc (sizeof **tr);
memset (*tr, 0, sizeof **tr);
tr_init (*tr);
}
void
read_db (struct monitor *mon, struct traffic_record **tr)
{
datum key;
datum content;
key.dptr = mon->id;
key.dsize = strlen (mon->id);
_read_db (key, tr);
}
void
write_db (struct monitor *mon, struct traffic_record *tr)
{
datum key;
datum content;
key.dptr = mon->id;
key.dsize = strlen (mon->id);
content.dsize = sizeof *tr;
content.dptr = (char *) tr;
if (gdbm_store (dbf, key, content, GDBM_REPLACE))
{
logmsg (L_ERR, _("failed to write data for %s: %s"),
mon->id, gdbm_strerror (gdbm_errno));
}
}
static void
print_time (FILE *fp, time_t time)
{
fprintf (fp, "%lu [", (unsigned long) time);
fprintftime (fp, "%Y-%m-%d %H:%M:%S %z", localtime (&time), 0, 0);
fprintf (fp, "]");
}
static void
print_queue (const char *title, queue_t *q)
{
int i, count;
count = queue_count (q);
printf (ngettext ("%s (%d entry):\n", "%s (%d entries):\n",
count),
title, count);
for (i = count - 1; i >= 0; i--)
{
struct traffic_history *th = queue_get_ptr (q, i);
printf ("%d ", count - i);
print_time (stdout, th->time);
printf (" %g %g\n", th->inrate, th->outrate);
}
}
static void
print_avg (const char *title, struct avg_acc *avg)
{
char buf[512];
strftime (buf, sizeof buf, "%c", localtime (&avg->time));
printf ("%s: %lu (%s) %u %g %g\n",
title, avg->time, buf, avg->count, avg->inrate, avg->outrate);
}
static void
print_tr (datum key, struct traffic_record *tr)
{
int i, count;
printf ("ID: %*.*s\n", key.dsize, key.dsize, key.dptr);
printf (_("Last sample: "));
print_time (stdout, tr->last.time);
printf (" %lu %lu\n", tr->last.in, tr->last.out);
printf (_("Last rates: %g %g\n"),
tr->last_rates.inrate, tr->last_rates.outrate);
print_queue (_("Daily rates"), &tr->day_hist);
print_avg (_("Weekly average"), &tr->week_avg);
print_queue (_("Weekly rates"), &tr->week_hist);
print_avg (_("Monthly average"), &tr->month_avg);
print_queue (_("Monthly rates"), &tr->month_hist);
print_avg (_("Yearly average"), &tr->year_avg);
print_queue (_("Yearly rates"), &tr->year_hist);
}
void
list_db ()
{
datum key;
datum content;
struct monitor *mon;
if (open_db (TAGR_DB_RD))
exit (EX_UNAVAILABLE);
key = gdbm_firstkey (dbf);
while (key.dptr)
{
struct traffic_record *tr;
datum nextkey = gdbm_nextkey (dbf, key);
_read_db (key, &tr);
print_tr (key, tr);
free (tr);
free (key.dptr);
key = nextkey;
}
close_db ();
}
void
report (Stat *stat, time_t timestamp)
{
struct monitor *mon = find_monitor_id (stat->name);
if (mon)
{
struct traffic_record *tr;
struct traffic_sample s;
s.in = stat->in;
s.out = stat->out;
s.time = timestamp;
read_db (mon, &tr);
update_stats (mon, &s, tr);
write_db (mon, tr);
free (tr);
}
else
logmsg (L_WARNING, _("%s not found in config"), stat->name);
}
int
update_monitor (datum key, time_t timestamp, int flags)
{
char id[MAX_NAME_LENGTH+1];
struct traffic_record *tr = NULL;
struct monitor *mon;
if (key.dsize > MAX_NAME_LENGTH)
{
logmsg (L_ERR, _("tag name too long (%u)"), key.dsize);
return 1;
}
memcpy (id, key.dptr, key.dsize);
id[key.dsize] = 0;
mon = find_monitor_id (id);
if (!mon)
{
logmsg (L_ERR, _("%s: no such monitor"), id);
return 1;
}
_read_db (key, &tr);
if (tr)
{
update_output (mon, tr, timestamp, flags);
free (tr);
}
return 0;
}
void
rebuild (int flags)
{
datum key;
datum content;
time_t now = time (NULL);
verbose (1, _("rebuild initiated"));
if (open_db (TAGR_DB_RD) == 0)
{
key = gdbm_firstkey (dbf);
while (key.dptr)
{
datum nextkey = gdbm_nextkey (dbf, key);
update_monitor (key, now, flags);
free (key.dptr);
key = nextkey;
}
close_db ();
}
verbose (1, _("rebuild finished"));
}