/*
This file is part of GNUnet
Copyright (C) 2008--2014 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
GNUnet 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
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
/**
* @file testbed/gnunet-daemon-latency-logger.c
* @brief log latency values from neighbour connections into an SQLite database
* @author Sree Harsha Totakura
*/
#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_ats_service.h"
#include
/**
* Logging shorthand
*/
#define LOG(type,...) \
GNUNET_log (type, __VA_ARGS__)
/**
* Debug logging shorthand
*/
#define DEBUG(...) \
LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
/**
* Log an error message at log-level 'level' that indicates
* a failure of the command 'cmd' on file 'filename'
* with the message given by strerror(errno).
*/
#define LOG_SQLITE(db, msg, level, cmd) \
do { \
GNUNET_log_from (level, "sqlite", _("`%s' failed at %s:%d with error: %s\n"), \
cmd, __FILE__,__LINE__, sqlite3_errmsg(db)); \
if (msg != NULL) \
GNUNET_asprintf(msg, _("`%s' failed at %s:%u with error: %s"), cmd, \
__FILE__, __LINE__, sqlite3_errmsg(db)); \
} while(0)
/**
* Entry type to be used in the map to store old latency values
*/
struct Entry
{
/**
* The peer's identity
*/
struct GNUNET_PeerIdentity id;
/**
* The last known value for latency.
* FIXME: type!
*/
unsigned int latency;
};
/**
* Handle to the map used to store old latency values for peers
*/
static struct GNUNET_CONTAINER_MultiPeerMap *map;
/**
* The SQLite database handle
*/
static struct sqlite3 *db;
/**
* Handle to the ATS performance subsystem
*/
static struct GNUNET_ATS_PerformanceHandle *ats;
/**
* Prepared statement for inserting values into the database table
*/
static struct sqlite3_stmt *stmt_insert;
/**
* @ingroup hashmap
* Iterator over hash map entries.
*
* @param cls closure
* @param key current public key
* @param value value in the hash map
* @return #GNUNET_YES if we should continue to
* iterate,
* #GNUNET_NO if not.
*/
static int
free_iterator (void *cls,
const struct GNUNET_PeerIdentity *key,
void *value)
{
struct Entry *e = cls;
GNUNET_assert (GNUNET_YES ==
GNUNET_CONTAINER_multipeermap_remove (map, key, e));
GNUNET_free (e);
return GNUNET_YES;
}
/**
* Shutdown
*
* @param cls NULL
* @return
*/
static void
do_shutdown (void *cls)
{
GNUNET_ATS_performance_done (ats);
ats = NULL;
if (NULL != stmt_insert)
{
sqlite3_finalize (stmt_insert);
stmt_insert = NULL;
}
GNUNET_break (SQLITE_OK == sqlite3_close (db));
db = NULL;
if (NULL != map)
{
GNUNET_assert (GNUNET_SYSERR !=
GNUNET_CONTAINER_multipeermap_iterate (map, free_iterator, NULL));
GNUNET_CONTAINER_multipeermap_destroy (map);
map = NULL;
}
}
/**
* Signature of a function that is called with QoS information about an address.
*
* @param cls closure
* @param address the address
* @param address_active #GNUNET_YES if this address is actively used
* to maintain a connection to a peer;
* #GNUNET_NO if the address is not actively used;
* #GNUNET_SYSERR if this address is no longer available for ATS
* @param bandwidth_out assigned outbound bandwidth for the connection
* @param bandwidth_in assigned inbound bandwidth for the connection
* @param prop performance data for the address (as far as known)
*/
static void
addr_info_cb (void *cls,
const struct GNUNET_HELLO_Address *address,
int address_active,
struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
const struct GNUNET_ATS_Properties *prop)
{
static const char *query_insert =
"INSERT INTO ats_info("
" id,"
" val,"
" timestamp"
") VALUES ("
" ?1,"
" ?2,"
" datetime('now')"
");";
struct Entry *entry;
int latency; /* FIXME: type!? */
if (NULL == address)
{
/* ATS service temporarily disconnected */
return;
}
GNUNET_assert (NULL != db);
if (GNUNET_YES != address_active)
return;
latency = (int) prop->delay.rel_value_us;
entry = NULL;
if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (map,
&address->peer))
{
entry = GNUNET_CONTAINER_multipeermap_get (map, &address->peer);
GNUNET_assert (NULL != entry);
if (latency == entry->latency)
return;
}
if (NULL == stmt_insert)
{
if (SQLITE_OK != sqlite3_prepare_v2 (db, query_insert, -1, &stmt_insert,
NULL))
{
LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_prepare_v2");
goto err_shutdown;
}
}
if ( (SQLITE_OK != sqlite3_bind_text (stmt_insert, 1,
GNUNET_i2s (&address->peer), -1,
SQLITE_STATIC)) ||
(SQLITE_OK != sqlite3_bind_int (stmt_insert, 2, latency)) )
{
LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_bind_text");
goto err_shutdown;
}
if (SQLITE_DONE != sqlite3_step (stmt_insert))
{
LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_step");
goto err_shutdown;
}
if (SQLITE_OK != sqlite3_reset (stmt_insert))
{
LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_insert");
goto err_shutdown;
}
if (NULL == entry)
{
entry = GNUNET_new (struct Entry);
entry->id = address->peer;
GNUNET_CONTAINER_multipeermap_put (map,
&entry->id, entry,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
}
entry->latency = latency;
return;
err_shutdown:
GNUNET_SCHEDULER_shutdown ();
}
/**
* Main function that will be run.
*
* @param cls closure
* @param args remaining command-line arguments
* @param cfgfile name of the configuration file used (for saving, can be NULL!)
* @param c configuration
*/
static void
run (void *cls, char *const *args, const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *c)
{
const char *query_create =
"CREATE TABLE ats_info ("
"id TEXT,"
"val INTEGER,"
"timestamp NUMERIC"
");";
char *dbfile;
if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c, "LATENCY-LOGGER",
"DBFILE",
&dbfile))
{
GNUNET_break (0);
return;
}
if (SQLITE_OK != sqlite3_open (dbfile, &db))
{
if (NULL != db)
{
LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite_open_v2");
GNUNET_break (SQLITE_OK == sqlite3_close (db));
}
else
LOG (GNUNET_ERROR_TYPE_ERROR, "Cannot open sqlite file %s\n", dbfile);
GNUNET_free (dbfile);
return;
}
if (0 != sqlite3_exec (db, query_create, NULL, NULL, NULL))
DEBUG ("SQLite Error: %d. Perhaps the database `%s' already exits.\n",
sqlite3_errcode (db), dbfile);
DEBUG ("Opened database %s\n", dbfile);
GNUNET_free (dbfile);
dbfile = NULL;
ats = GNUNET_ATS_performance_init (c, &addr_info_cb, NULL);
map = GNUNET_CONTAINER_multipeermap_create (30, GNUNET_YES);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
}
/**
* Execution entry point
*/
int
main (int argc, char * const *argv)
{
static const struct GNUNET_GETOPT_CommandLineOption options[] = {
GNUNET_GETOPT_OPTION_END
};
int ret;
if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
return 2;
ret =
(GNUNET_OK ==
GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-latency-logger",
_("Daemon to log latency values of connections to neighbours"),
options, &run, NULL)) ? 0 : 1;
GNUNET_free ((void*) argv);
return ret;
}