uncrustify as demanded.
[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      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file testbed/gnunet-daemon-latency-logger.c
23  * @brief log latency values from neighbour connections into an SQLite database
24  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_ats_service.h"
30 #include <sqlite3.h>
31
32
33 /**
34  * Logging shorthand
35  */
36 #define LOG(type, ...)                           \
37   GNUNET_log(type, __VA_ARGS__)
38
39 /**
40  * Debug logging shorthand
41  */
42 #define DEBUG(...)                              \
43   LOG(GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
44
45 /**
46  * Log an error message at log-level 'level' that indicates
47  * a failure of the command 'cmd' on file 'filename'
48  * with the message given by strerror(errno).
49  */
50 #define LOG_SQLITE(db, msg, level, cmd)                                 \
51   do {                                                                  \
52       GNUNET_log_from(level, "sqlite", _("`%s' failed at %s:%d with error: %s\n"), \
53                       cmd, __FILE__, __LINE__, sqlite3_errmsg(db));  \
54       if (msg != NULL)                                                    \
55       GNUNET_asprintf (msg, _("`%s' failed at %s:%u with error: %s"), cmd, \
56                        __FILE__, __LINE__, sqlite3_errmsg(db));     \
57     } while (0)
58
59
60 /**
61  * Entry type to be used in the map to store old latency values
62  */
63 struct Entry {
64   /**
65    *  The peer's identity
66    */
67   struct GNUNET_PeerIdentity id;
68
69   /**
70    * The last known value for latency.
71    * FIXME: type!
72    */
73   unsigned int latency;
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 }