015b9cc6946821d350d83d97b95a0022460b882e
[oweals/gnunet.git] / src / testbed / gnunet-daemon-latency-logger.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2008--2014 GNUnet e.V.
4
5       GNUnet is free software: you can redistribute it and/or modify it
6       under the terms of the GNU Affero General Public License as published
7       by the Free Software Foundation, either version 3 of the License,
8       or (at your option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       Affero General Public License for more details.
14      
15       You should have received a copy of the GNU Affero General Public License
16       along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file testbed/gnunet-daemon-latency-logger.c
21  * @brief log latency values from neighbour connections into an SQLite database
22  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
23  */
24
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_ats_service.h"
28 #include <sqlite3.h>
29
30
31 /**
32  * Logging shorthand
33  */
34 #define LOG(type,...)                           \
35   GNUNET_log (type, __VA_ARGS__)
36
37 /**
38  * Debug logging shorthand
39  */
40 #define DEBUG(...)                              \
41   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
42
43 /**
44  * Log an error message at log-level 'level' that indicates
45  * a failure of the command 'cmd' on file 'filename'
46  * with the message given by strerror(errno).
47  */
48 #define LOG_SQLITE(db, msg, level, cmd)                                 \
49   do {                                                                  \
50     GNUNET_log_from (level, "sqlite", _("`%s' failed at %s:%d with error: %s\n"), \
51                      cmd, __FILE__,__LINE__, sqlite3_errmsg(db));  \
52     if (msg != NULL)                                                    \
53       GNUNET_asprintf(msg, _("`%s' failed at %s:%u with error: %s"), cmd, \
54                       __FILE__, __LINE__, sqlite3_errmsg(db));     \
55   } while(0)
56
57
58 /**
59  * Entry type to be used in the map to store old latency values
60  */
61 struct Entry
62 {
63   /**
64    *  The peer's identity
65    */
66   struct GNUNET_PeerIdentity id;
67
68   /**
69    * The last known value for latency.
70    * FIXME: type!
71    */
72   unsigned int latency;
73
74 };
75
76
77 /**
78  * Handle to the map used to store old latency values for peers
79  */
80 static struct GNUNET_CONTAINER_MultiPeerMap *map;
81
82 /**
83  * The SQLite database handle
84  */
85 static struct sqlite3 *db;
86
87 /**
88  * Handle to the ATS performance subsystem
89  */
90 static struct GNUNET_ATS_PerformanceHandle *ats;
91
92 /**
93  * Prepared statement for inserting values into the database table
94  */
95 static struct sqlite3_stmt *stmt_insert;
96
97
98 /**
99  * @ingroup hashmap
100  * Iterator over hash map entries.
101  *
102  * @param cls closure
103  * @param key current public key
104  * @param value value in the hash map
105  * @return #GNUNET_YES if we should continue to
106  *         iterate,
107  *         #GNUNET_NO if not.
108  */
109 static int
110 free_iterator (void *cls,
111                const struct GNUNET_PeerIdentity *key,
112                void *value)
113 {
114   struct Entry *e = cls;
115
116   GNUNET_assert (GNUNET_YES ==
117                  GNUNET_CONTAINER_multipeermap_remove (map, key, e));
118   GNUNET_free (e);
119   return GNUNET_YES;
120 }
121
122
123 /**
124  * Shutdown
125  *
126  * @param cls NULL
127  * @return
128  */
129 static void
130 do_shutdown (void *cls)
131 {
132   GNUNET_ATS_performance_done (ats);
133   ats = NULL;
134   if (NULL != stmt_insert)
135   {
136     sqlite3_finalize (stmt_insert);
137     stmt_insert = NULL;
138   }
139   GNUNET_break (SQLITE_OK == sqlite3_close (db));
140   db = NULL;
141   if (NULL != map)
142   {
143     GNUNET_assert (GNUNET_SYSERR !=
144                    GNUNET_CONTAINER_multipeermap_iterate (map, free_iterator, NULL));
145     GNUNET_CONTAINER_multipeermap_destroy (map);
146     map = NULL;
147   }
148 }
149
150 /**
151  * Signature of a function that is called with QoS information about an address.
152  *
153  * @param cls closure
154  * @param address the address
155  * @param address_active #GNUNET_YES if this address is actively used
156  *        to maintain a connection to a peer;
157  *        #GNUNET_NO if the address is not actively used;
158  *        #GNUNET_SYSERR if this address is no longer available for ATS
159  * @param bandwidth_out assigned outbound bandwidth for the connection
160  * @param bandwidth_in assigned inbound bandwidth for the connection
161  * @param prop performance data for the address (as far as known)
162  */
163 static void
164 addr_info_cb (void *cls,
165               const struct GNUNET_HELLO_Address *address,
166               int address_active,
167               struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
168               struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
169               const struct GNUNET_ATS_Properties *prop)
170 {
171   static const char *query_insert =
172       "INSERT INTO ats_info("
173       " id,"
174       " val,"
175       " timestamp"
176       ") VALUES ("
177       " ?1,"
178       " ?2,"
179       " datetime('now')"
180       ");";
181   struct Entry *entry;
182   int latency; /* FIXME: type!? */
183
184   if (NULL == address)
185   {
186     /* ATS service temporarily disconnected */
187     return;
188   }
189
190   GNUNET_assert (NULL != db);
191   if (GNUNET_YES != address_active)
192     return;
193   latency = (int) prop->delay.rel_value_us;
194   entry = NULL;
195   if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (map,
196                                                             &address->peer))
197   {
198     entry = GNUNET_CONTAINER_multipeermap_get (map, &address->peer);
199     GNUNET_assert (NULL != entry);
200     if (latency == entry->latency)
201       return;
202   }
203   if (NULL == stmt_insert)
204   {
205     if (SQLITE_OK != sqlite3_prepare_v2 (db, query_insert, -1, &stmt_insert,
206                                          NULL))
207     {
208       LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_prepare_v2");
209       goto err_shutdown;
210     }
211   }
212   if ( (SQLITE_OK != sqlite3_bind_text (stmt_insert, 1,
213                                         GNUNET_i2s (&address->peer), -1,
214                                         SQLITE_STATIC)) ||
215         (SQLITE_OK != sqlite3_bind_int (stmt_insert, 2, latency)) )
216   {
217      LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_bind_text");
218      goto err_shutdown;
219   }
220   if (SQLITE_DONE != sqlite3_step (stmt_insert))
221   {
222     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_step");
223     goto err_shutdown;
224   }
225   if (SQLITE_OK != sqlite3_reset (stmt_insert))
226   {
227     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_insert");
228     goto err_shutdown;
229   }
230   if (NULL == entry)
231   {
232     entry = GNUNET_new (struct Entry);
233     entry->id = address->peer;
234     GNUNET_CONTAINER_multipeermap_put (map,
235                                        &entry->id, entry,
236                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
237   }
238   entry->latency = latency;
239   return;
240
241  err_shutdown:
242       GNUNET_SCHEDULER_shutdown ();
243 }
244
245
246 /**
247  * Main function that will be run.
248  *
249  * @param cls closure
250  * @param args remaining command-line arguments
251  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
252  * @param c configuration
253  */
254 static void
255 run (void *cls, char *const *args, const char *cfgfile,
256      const struct GNUNET_CONFIGURATION_Handle *c)
257 {
258   const char *query_create =
259       "CREATE TABLE ats_info ("
260       "id TEXT,"
261       "val INTEGER,"
262       "timestamp NUMERIC"
263       ");";
264   char *dbfile;
265
266   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c, "LATENCY-LOGGER",
267                                                             "DBFILE",
268                                                             &dbfile))
269   {
270     GNUNET_break (0);
271     return;
272   }
273   if (SQLITE_OK != sqlite3_open (dbfile, &db))
274   {
275     if (NULL != db)
276     {
277       LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite_open_v2");
278       GNUNET_break (SQLITE_OK == sqlite3_close (db));
279     }
280     else
281       LOG (GNUNET_ERROR_TYPE_ERROR, "Cannot open sqlite file %s\n", dbfile);
282     GNUNET_free (dbfile);
283     return;
284   }
285   if (0 != sqlite3_exec (db, query_create, NULL, NULL, NULL))
286     DEBUG ("SQLite Error: %d.  Perhaps the database `%s' already exits.\n",
287            sqlite3_errcode (db), dbfile);
288   DEBUG ("Opened database %s\n", dbfile);
289   GNUNET_free (dbfile);
290   dbfile = NULL;
291   ats = GNUNET_ATS_performance_init (c, &addr_info_cb, NULL);
292   map = GNUNET_CONTAINER_multipeermap_create (30, GNUNET_YES);
293   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
294 }
295
296
297 /**
298  * Execution entry point
299  */
300 int
301 main (int argc, char * const *argv)
302 {
303   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
304     GNUNET_GETOPT_OPTION_END
305   };
306   int ret;
307
308   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
309     return 2;
310   ret =
311       (GNUNET_OK ==
312        GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-latency-logger",
313                            _("Daemon to log latency values of connections to neighbours"),
314                            options, &run, NULL)) ? 0 : 1;
315   GNUNET_free ((void*) argv);
316   return ret;
317 }