WiP
[oweals/gnunet.git] / src / dht / plugin_dhtlog_mysql.c
1 /*
2      This file is part of GNUnet.
3      (C) 2006 - 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file src/dht/plugin_dhtlog_mysql.c
23  * @brief MySQL logging plugin to record DHT operations to MySQL server
24  * @author Nathan Evans
25  *
26  * Database: MySQL
27  */
28
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31 #include "dhtlog.h"
32 #include <mysql/mysql.h>
33
34
35 #define DEBUG_DHTLOG GNUNET_YES
36
37 /**
38  * Maximum number of supported parameters for a prepared
39  * statement.  Increase if needed.
40  */
41 #define MAX_PARAM 32
42
43 /**
44  * A generic statement handle to use
45  * for prepared statements.  This way,
46  * once the statement is initialized
47  * we don't redo work.
48  */
49 struct StatementHandle
50 {
51   /**
52    * Internal statement
53    */
54   MYSQL_STMT *statement;
55
56   /**
57    * Textual query
58    */
59   char *query;
60
61   /**
62    * Whether or not the handle is valid
63    */
64   int valid;
65 };
66
67 /**
68  * Type of a callback that will be called for each
69  * data set returned from MySQL.
70  *
71  * @param cls user-defined argument
72  * @param num_values number of elements in values
73  * @param values values returned by MySQL
74  * @return GNUNET_OK to continue iterating, GNUNET_SYSERR to abort
75  */
76 typedef int (*GNUNET_MysqlDataProcessor) (void *cls,
77                                           unsigned int num_values,
78                                           MYSQL_BIND * values);
79
80 static unsigned long max_varchar_len;
81
82 /**
83  * The configuration the DHT service is running with
84  */
85 static const struct GNUNET_CONFIGURATION_Handle *cfg;
86
87 static unsigned long long current_trial = 0;    /* I like to assign 0, just to remember */
88
89 /**
90  * Connection to the MySQL Server.
91  */
92 static MYSQL *conn;
93
94 #define INSERT_QUERIES_STMT "INSERT INTO queries (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid, time) "\
95                           "VALUES (?, ?, ?, ?, ?, ?, ?, NOW())"
96 static struct StatementHandle *insert_query;
97
98 #define INSERT_ROUTES_STMT "INSERT INTO routes (trialuid, querytype, hops, dhtkeyuid, dhtqueryid, succeeded, nodeuid, from_node, to_node) "\
99                           "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
100 static struct StatementHandle *insert_route;
101
102 #define INSERT_NODES_STMT "INSERT INTO nodes (trialuid, nodeid, nodebits) "\
103                           "VALUES (?, ?, ?)"
104 static struct StatementHandle *insert_node;
105
106 #define INSERT_ROUNDS_STMT "INSERT INTO rounds (trialuid, round_type, round_count, starttime) "\
107                           "VALUES (?, ?, ?, NOW())"
108
109 static struct StatementHandle *insert_round;
110
111 #define INSERT_ROUND_DETAILS_STMT "INSERT INTO rounds (trialuid, round_type, round_count, starttime, endtime, num_messages, num_messages_succeeded) "\
112                           "VALUES (?, ?, ?, NOW(), NOW(), ?, ?)"
113
114 static struct StatementHandle *insert_round_details;
115
116 #define INSERT_TRIALS_STMT "INSERT INTO trials"\
117                             "(starttime, other_trial_identifier, numnodes, topology,"\
118                             "topology_percentage, topology_probability,"\
119                             "blacklist_topology, connect_topology, connect_topology_option,"\
120                             "connect_topology_option_modifier, puts, gets, "\
121                             "concurrent, settle_time, num_rounds, malicious_getters,"\
122                             "malicious_putters, malicious_droppers, malicious_get_frequency,"\
123                             "malicious_put_frequency, stop_closest, stop_found, strict_kademlia, "\
124                             "gets_succeeded, message) "\
125                             "VALUES (NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
126
127 static struct StatementHandle *insert_trial;
128
129 #define INSERT_STAT_STMT "INSERT INTO node_statistics"\
130                             "(trialuid, nodeuid, route_requests,"\
131                             "route_forwards, result_requests,"\
132                             "client_results, result_forwards, gets,"\
133                             "puts, data_inserts, find_peer_requests, "\
134                             "find_peers_started, gets_started, puts_started, find_peer_responses_received,"\
135                             "get_responses_received, find_peer_responses_sent, get_responses_sent) "\
136                             "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
137
138 static struct StatementHandle *insert_stat;
139
140 #define INSERT_GENERIC_STAT_STMT "INSERT INTO generic_stats" \
141                                  "(trialuid, nodeuid, section, name, value)"\
142                                  "VALUES (?, ?, ?, ?, ?)"
143 static struct StatementHandle *insert_generic_stat;
144
145 #define INSERT_DHTKEY_STMT "INSERT INTO dhtkeys (dhtkey, trialuid, keybits) "\
146                           "VALUES (?, ?, ?)"
147 static struct StatementHandle *insert_dhtkey;
148
149 #define UPDATE_TRIALS_STMT "UPDATE trials set endtime=NOW(), gets_succeeded = ? where trialuid = ?"
150 static struct StatementHandle *update_trial;
151
152 #define UPDATE_CONNECTIONS_STMT "UPDATE trials set totalConnections = ? where trialuid = ?"
153 static struct StatementHandle *update_connection;
154
155 #define GET_TRIAL_STMT "SELECT MAX( trialuid ) FROM trials"
156 static struct StatementHandle *get_trial;
157
158 #define GET_TOPOLOGY_STMT "SELECT MAX( topology_uid ) FROM topology"
159 static struct StatementHandle *get_topology;
160
161 #define GET_DHTKEYUID_STMT "SELECT dhtkeyuid FROM dhtkeys where dhtkey = ? and trialuid = ?"
162 static struct StatementHandle *get_dhtkeyuid;
163
164 #define GET_NODEUID_STMT "SELECT nodeuid FROM nodes where trialuid = ? and nodeid = ?"
165 static struct StatementHandle *get_nodeuid;
166
167 #define INSERT_TOPOLOGY_STMT "INSERT INTO topology (trialuid, date, connections) "\
168                              "VALUES (?, NOW(), ?)"
169 static struct StatementHandle *insert_topology;
170
171 #define EXTEND_TOPOLOGY_STMT "INSERT INTO extended_topology (topology_uid, uid_first, uid_second) "\
172                              "VALUES (?, ?, ?)"
173 static struct StatementHandle *extend_topology;
174
175 #define SET_MALICIOUS_STMT "update nodes set malicious_dropper = 1  where trialuid = ? and nodeid = ?"
176 static struct StatementHandle *update_node_malicious;
177
178 #define UPDATE_TOPOLOGY_STMT "update topology set connections = ?  where topology_uid = ?"
179 static struct StatementHandle *update_topology;
180
181 /**
182  * Run a query (not a select statement)
183  *
184  * @return GNUNET_OK if executed, GNUNET_SYSERR if an error occurred
185  */
186 int
187 run_statement (const char *statement)
188 {
189   mysql_query (conn, statement);
190   if (mysql_error (conn)[0])
191     {
192       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
193                  "mysql_query");
194       return GNUNET_SYSERR;
195     }
196   return GNUNET_OK;
197 }
198
199 /*
200  * Creates tables if they don't already exist for dht logging
201  */
202 static int
203 itable ()
204 {
205 #define MRUNS(a) (GNUNET_OK != run_statement (a) )
206
207   if (MRUNS ("CREATE TABLE IF NOT EXISTS `dhtkeys` ("
208              "dhtkeyuid int(10) unsigned NOT NULL auto_increment COMMENT 'Unique Key given to each query',"
209              "`dhtkey` varchar(255) NOT NULL COMMENT 'The ASCII value of the key being searched for',"
210              "trialuid int(10) unsigned NOT NULL,"
211              "keybits blob NOT NULL,"
212              "UNIQUE KEY `dhtkeyuid` (`dhtkeyuid`)"
213              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
214     return GNUNET_SYSERR;
215
216   if (MRUNS ("CREATE TABLE IF NOT EXISTS `nodes` ("
217              "`nodeuid` int(10) unsigned NOT NULL auto_increment,"
218              "`trialuid` int(10) unsigned NOT NULL,"
219              "`nodeid` varchar(255) NOT NULL,"
220              "`nodebits` blob NOT NULL,"
221              "PRIMARY KEY  (`nodeuid`)"
222              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
223     return GNUNET_SYSERR;
224
225   if (MRUNS ("CREATE TABLE IF NOT EXISTS `queries` ("
226              "`trialuid` int(10) unsigned NOT NULL,"
227              "`queryuid` int(10) unsigned NOT NULL auto_increment,"
228              "`dhtqueryid` bigint(20) NOT NULL,"
229              "`querytype` enum('1','2','3','4','5') NOT NULL,"
230              "`hops` int(10) unsigned NOT NULL,"
231              "`succeeded` tinyint NOT NULL,"
232              "`nodeuid` int(10) unsigned NOT NULL,"
233              "`time` timestamp NOT NULL default CURRENT_TIMESTAMP,"
234              "`dhtkeyuid` int(10) unsigned NOT NULL,"
235              "PRIMARY KEY  (`queryuid`)"
236              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
237     return GNUNET_SYSERR;
238
239   if (MRUNS ("CREATE TABLE IF NOT EXISTS `routes` ("
240              "`trialuid` int(10) unsigned NOT NULL,"
241              "`queryuid` int(10) unsigned NOT NULL auto_increment,"
242              "`dhtqueryid` bigint(20) NOT NULL,"
243              "`querytype` enum('1','2','3','4','5') NOT NULL,"
244              "`hops` int(10) unsigned NOT NULL,"
245              "`succeeded` tinyint NOT NULL,"
246              "`nodeuid` int(10) unsigned NOT NULL,"
247              "`time` timestamp NOT NULL default CURRENT_TIMESTAMP,"
248              "`dhtkeyuid` int(10) unsigned NOT NULL,"
249              "`from_node` int(10) unsigned NOT NULL,"
250              "`to_node` int(10) unsigned NOT NULL,"
251              "PRIMARY KEY  (`queryuid`)"
252              ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
253     return GNUNET_SYSERR;
254
255   if (MRUNS ("CREATE TABLE IF NOT EXISTS `trials` ("
256               "`trialuid` int(10) unsigned NOT NULL auto_increment,"
257               "`other_trial_identifier` int(10) unsigned NOT NULL default '0',"
258               "`numnodes` int(10) unsigned NOT NULL,"
259               "`topology` int(10) NOT NULL,"
260               "`blacklist_topology` int(11) NOT NULL,"
261               "`connect_topology` int(11) NOT NULL,"
262               "`connect_topology_option` int(11) NOT NULL,"
263               "`topology_percentage` float NOT NULL,"
264               "`topology_probability` float NOT NULL,"
265               "`connect_topology_option_modifier` float NOT NULL,"
266               "`starttime` datetime NOT NULL,"
267               "`endtime` datetime NOT NULL,"
268               "`puts` int(10) unsigned NOT NULL,"
269               "`gets` int(10) unsigned NOT NULL,"
270               "`concurrent` int(10) unsigned NOT NULL,"
271               "`settle_time` int(10) unsigned NOT NULL,"
272               "`totalConnections` int(10) unsigned NOT NULL,"
273               "`message` text NOT NULL,"
274               "`num_rounds` int(10) unsigned NOT NULL,"
275               "`malicious_getters` int(10) unsigned NOT NULL,"
276               "`malicious_putters` int(10) unsigned NOT NULL,"
277               "`malicious_droppers` int(10) unsigned NOT NULL,"
278               "`topology_modifier` double NOT NULL,"
279               "`malicious_get_frequency` int(10) unsigned NOT NULL,"
280               "`malicious_put_frequency` int(10) unsigned NOT NULL,"
281               "`stop_closest` int(10) unsigned NOT NULL,"
282               "`stop_found` int(10) unsigned NOT NULL,"
283               "`strict_kademlia` int(10) unsigned NOT NULL,"
284               "`gets_succeeded` int(10) unsigned NOT NULL,"
285               "PRIMARY KEY  (`trialuid`),"
286               "UNIQUE KEY `trialuid` (`trialuid`)"
287               ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
288     return GNUNET_SYSERR;
289
290   if (MRUNS ("CREATE TABLE IF NOT EXISTS `topology` ("
291               "`topology_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
292               "`trialuid` int(10) unsigned NOT NULL,"
293               "`date` datetime NOT NULL,"
294               "`connections` int(10) unsigned NOT NULL,"
295               "PRIMARY KEY (`topology_uid`)) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
296     return GNUNET_SYSERR;
297
298   if (MRUNS ("CREATE TABLE IF NOT EXISTS `extended_topology` ("
299              "`extended_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
300              "`topology_uid` int(10) unsigned NOT NULL,"
301              "`uid_first` int(10) unsigned NOT NULL,"
302              "`uid_second` int(10) unsigned NOT NULL,"
303              "PRIMARY KEY (`extended_uid`)"
304              ") ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"))
305     return GNUNET_SYSERR;
306
307   if (MRUNS ("CREATE TABLE IF NOT EXISTS `node_statistics` ("
308               "`stat_uid` int(10) unsigned NOT NULL AUTO_INCREMENT,"
309               "`trialuid` int(10) unsigned NOT NULL,"
310               "`nodeuid` int(10) unsigned NOT NULL,"
311               "`route_requests` int(10) unsigned NOT NULL,"
312               "`route_forwards` int(10) unsigned NOT NULL,"
313               "`result_requests` int(10) unsigned NOT NULL,"
314               "`client_results` int(10) unsigned NOT NULL,"
315               "`result_forwards` int(10) unsigned NOT NULL,"
316               "`gets` int(10) unsigned NOT NULL,"
317               "`puts` int(10) unsigned NOT NULL,"
318               "`data_inserts` int(10) unsigned NOT NULL,"
319               "`find_peer_requests` int(10) unsigned NOT NULL,"
320               "`find_peers_started` int(10) unsigned NOT NULL,"
321               "`gets_started` int(10) unsigned NOT NULL,"
322               "`puts_started` int(10) unsigned NOT NULL,"
323               "`find_peer_responses_received` int(10) unsigned NOT NULL,"
324               "`get_responses_received` int(10) unsigned NOT NULL,"
325               "`find_peer_responses_sent` int(10) unsigned NOT NULL,"
326               "`get_responses_sent` int(10) unsigned NOT NULL,"
327              "PRIMARY KEY (`stat_uid`)"
328             ") ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;"))
329     return GNUNET_SYSERR;
330
331   if (MRUNS ("SET AUTOCOMMIT = 1"))
332     return GNUNET_SYSERR;
333
334   return GNUNET_OK;
335 #undef MRUNS
336 }
337
338 /**
339  * Create a prepared statement.
340  *
341  * @return NULL on error
342  */
343 struct StatementHandle *
344 prepared_statement_create (const char *statement)
345 {
346   struct StatementHandle *ret;
347
348   ret = GNUNET_malloc (sizeof (struct StatementHandle));
349   ret->query = GNUNET_strdup (statement);
350   return ret;
351 }
352
353 /**
354  * Close a prepared statement.
355  *
356  * @return NULL on error
357  */
358 void
359 prepared_statement_close (struct StatementHandle *s)
360 {
361   if (s == NULL)
362     {
363       return;
364     }
365
366   GNUNET_free_non_null(s->query);
367
368   if (s->valid == GNUNET_YES)
369     mysql_stmt_close(s->statement);
370   GNUNET_free(s);
371 }
372
373 /*
374  * Initialize the prepared statements for use with dht test logging
375  */
376 static int
377 iopen (struct GNUNET_DHTLOG_Plugin *plugin)
378 {
379   int ret;
380   my_bool reconnect;
381   unsigned int timeout;
382   char *user;
383   char *password;
384   char *server;
385   char *database;
386   unsigned long long port;
387
388   conn = mysql_init (NULL);
389   if (conn == NULL)
390     return GNUNET_SYSERR;
391
392   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
393                                                          "MYSQL", "DATABASE",
394                                                          &database))
395     {
396       database = GNUNET_strdup("gnunet");
397     }
398
399   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
400                                                           "MYSQL", "USER", &user))
401     {
402       user = GNUNET_strdup("dht");
403     }
404
405   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
406                                                           "MYSQL", "PASSWORD", &password))
407     {
408       password = GNUNET_strdup("dhttest**");
409     }
410
411   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (plugin->cfg,
412                                                           "MYSQL", "SERVER", &server))
413     {
414       server = GNUNET_strdup("localhost");
415     }
416
417   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->cfg,
418                                                           "MYSQL", "MYSQL_PORT", &port))
419     {
420       port = 0;
421     }
422
423   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to mysql with: user %s, pass %s, server %s, database %s, port %d\n",
424               user, password, server, database, port);
425
426   reconnect = 0;
427   timeout = 60; /* in seconds */
428   mysql_options (conn, MYSQL_OPT_RECONNECT, &reconnect);
429   mysql_options (conn,
430                  MYSQL_OPT_CONNECT_TIMEOUT, (const void *) &timeout);
431   mysql_options(conn, MYSQL_SET_CHARSET_NAME, "UTF8");
432   mysql_options (conn, MYSQL_OPT_READ_TIMEOUT, (const void *) &timeout);
433   mysql_options (conn, MYSQL_OPT_WRITE_TIMEOUT, (const void *) &timeout);
434   mysql_real_connect (conn, server, user, password,
435                       database, (unsigned int) port, NULL, CLIENT_IGNORE_SIGPIPE);
436
437   GNUNET_free_non_null(server);
438   GNUNET_free_non_null(password);
439   GNUNET_free_non_null(user);
440   GNUNET_free_non_null(database);
441
442   if (mysql_error (conn)[0])
443     {
444       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
445                  "mysql_real_connect");
446       return GNUNET_SYSERR;
447     }
448
449 #if OLD
450   db = GNUNET_MYSQL_database_open (coreAPI->ectx, coreAPI->cfg);
451   if (db == NULL)
452     return GNUNET_SYSERR;
453 #endif
454
455   ret = itable ();
456
457 #define PINIT(a,b) (NULL == (a = prepared_statement_create(b)))
458   if (PINIT (insert_query, INSERT_QUERIES_STMT) ||
459       PINIT (insert_route, INSERT_ROUTES_STMT) ||
460       PINIT (insert_trial, INSERT_TRIALS_STMT) ||
461       PINIT (insert_round, INSERT_ROUNDS_STMT) ||
462       PINIT (insert_round, INSERT_ROUND_DETAILS_STMT) ||
463       PINIT (insert_stat, INSERT_STAT_STMT) ||
464       PINIT (insert_generic_stat, INSERT_GENERIC_STAT_STMT) ||
465       PINIT (insert_node, INSERT_NODES_STMT) ||
466       PINIT (insert_dhtkey, INSERT_DHTKEY_STMT) ||
467       PINIT (update_trial, UPDATE_TRIALS_STMT) ||
468       PINIT (get_dhtkeyuid, GET_DHTKEYUID_STMT) ||
469       PINIT (get_nodeuid, GET_NODEUID_STMT) ||
470       PINIT (update_connection, UPDATE_CONNECTIONS_STMT) ||
471       PINIT (get_trial, GET_TRIAL_STMT) ||
472       PINIT (get_topology, GET_TOPOLOGY_STMT) ||
473       PINIT (insert_topology, INSERT_TOPOLOGY_STMT) ||
474       PINIT (update_topology, UPDATE_TOPOLOGY_STMT) ||
475       PINIT (extend_topology, EXTEND_TOPOLOGY_STMT) ||
476       PINIT (update_node_malicious, SET_MALICIOUS_STMT) )
477     {
478       return GNUNET_SYSERR;
479     }
480 #undef PINIT
481
482   return ret;
483 }
484
485 static int
486 return_ok (void *cls, unsigned int num_values, MYSQL_BIND * values)
487 {
488   return GNUNET_OK;
489 }
490
491 /**
492  * Prepare a statement for running.
493  *
494  * @return GNUNET_OK on success
495  */
496 static int
497 prepare_statement (struct StatementHandle *ret)
498 {
499   if (GNUNET_YES == ret->valid)
500     return GNUNET_OK;
501
502   ret->statement = mysql_stmt_init (conn);
503   if (ret->statement == NULL)
504     return GNUNET_SYSERR;
505
506   if (mysql_stmt_prepare (ret->statement, ret->query, strlen (ret->query)))
507     {
508       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
509                  "mysql_stmt_prepare `%s', %s", ret->query, mysql_error(conn));
510       mysql_stmt_close (ret->statement);
511       ret->statement = NULL;
512       return GNUNET_SYSERR;
513     }
514   ret->valid = GNUNET_YES;
515   return GNUNET_OK;
516 }
517
518 /**
519  * Bind the parameters for the given MySQL statement
520  * and run it.
521  *
522  * @param s statement to bind and run
523  * @param ap arguments for the binding
524  * @return GNUNET_SYSERR on error, GNUNET_OK on success
525  */
526 static int
527 init_params (struct StatementHandle *s, va_list ap)
528 {
529   MYSQL_BIND qbind[MAX_PARAM];
530   unsigned int pc;
531   unsigned int off;
532   enum enum_field_types ft;
533
534   pc = mysql_stmt_param_count (s->statement);
535   if (pc > MAX_PARAM)
536     {
537       /* increase internal constant! */
538       GNUNET_break (0);
539       return GNUNET_SYSERR;
540     }
541   memset (qbind, 0, sizeof (qbind));
542   off = 0;
543   ft = 0;
544   while ((pc > 0) && (-1 != (ft = va_arg (ap, enum enum_field_types))))
545     {
546       qbind[off].buffer_type = ft;
547       switch (ft)
548         {
549         case MYSQL_TYPE_FLOAT:
550           qbind[off].buffer = va_arg (ap, float *);
551           break;
552         case MYSQL_TYPE_LONGLONG:
553           qbind[off].buffer = va_arg (ap, unsigned long long *);
554           qbind[off].is_unsigned = va_arg (ap, int);
555           break;
556         case MYSQL_TYPE_LONG:
557           qbind[off].buffer = va_arg (ap, unsigned int *);
558           qbind[off].is_unsigned = va_arg (ap, int);
559           break;
560         case MYSQL_TYPE_VAR_STRING:
561         case MYSQL_TYPE_STRING:
562         case MYSQL_TYPE_BLOB:
563           qbind[off].buffer = va_arg (ap, void *);
564           qbind[off].buffer_length = va_arg (ap, unsigned long);
565           qbind[off].length = va_arg (ap, unsigned long *);
566           break;
567         default:
568           /* unsupported type */
569           GNUNET_break (0);
570           return GNUNET_SYSERR;
571         }
572       pc--;
573       off++;
574     }
575   if (!((pc == 0) && (ft != -1) && (va_arg (ap, int) == -1)))
576     {
577       GNUNET_break (0);
578       return GNUNET_SYSERR;
579     }
580   if (mysql_stmt_bind_param (s->statement, qbind))
581     {
582       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
583                   _("`%s' failed at %s:%d with error: %s\n"),
584                   "mysql_stmt_bind_param",
585                    __FILE__, __LINE__, mysql_stmt_error (s->statement));
586       return GNUNET_SYSERR;
587     }
588
589   if (mysql_stmt_execute (s->statement))
590     {
591     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
592                _("`%s' failed at %s:%d with error: %s\n"),
593                "mysql_stmt_execute",
594                __FILE__, __LINE__, mysql_stmt_error (s->statement));
595       return GNUNET_SYSERR;
596     }
597
598   return GNUNET_OK;
599 }
600
601 /**
602  * Run a prepared SELECT statement.
603  *
604  * @param s handle to the statement we should execute
605  * @param result_size number of results in set
606  * @param results pointer to already initialized MYSQL_BIND
607  *        array (of sufficient size) for passing results
608  * @param processor function to call on each result
609  * @param processor_cls extra argument to processor
610  * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
611  *        values (size + buffer-reference for pointers); terminated
612  *        with "-1"
613  *
614  * @return GNUNET_SYSERR on error, otherwise
615  *         the number of successfully affected (or queried) rows
616  */
617 int
618 prepared_statement_run_select (struct StatementHandle *s,
619                                unsigned int result_size,
620                                MYSQL_BIND * results,
621                                GNUNET_MysqlDataProcessor
622                                processor, void *processor_cls,
623                                ...)
624 {
625   va_list ap;
626   int ret;
627   unsigned int rsize;
628   int total;
629
630   if (GNUNET_OK != prepare_statement (s))
631     {
632       GNUNET_break (0);
633       return GNUNET_SYSERR;
634     }
635   va_start (ap, processor_cls);
636   if (GNUNET_OK != init_params (s, ap))
637     {
638       GNUNET_break (0);
639       va_end (ap);
640       return GNUNET_SYSERR;
641     }
642   va_end (ap);
643   rsize = mysql_stmt_field_count (s->statement);
644   if (rsize > result_size)
645     {
646       GNUNET_break (0);
647       return GNUNET_SYSERR;
648     }
649   if (mysql_stmt_bind_result (s->statement, results))
650     {
651       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
652                   _("`%s' failed at %s:%d with error: %s\n"),
653                   "mysql_stmt_bind_result",
654                   __FILE__, __LINE__, mysql_stmt_error (s->statement));
655       return GNUNET_SYSERR;
656     }
657
658   total = 0;
659   while (1)
660     {
661       ret = mysql_stmt_fetch (s->statement);
662       if (ret == MYSQL_NO_DATA)
663         break;
664       if (ret != 0)
665         {
666           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
667                        _("`%s' failed at %s:%d with error: %s\n"),
668                        "mysql_stmt_fetch",
669                        __FILE__, __LINE__, mysql_stmt_error (s->statement));
670           return GNUNET_SYSERR;
671         }
672       if (processor != NULL)
673         if (GNUNET_OK != processor (processor_cls, rsize, results))
674           break;
675       total++;
676     }
677   mysql_stmt_reset (s->statement);
678   return total;
679 }
680
681
682 static int
683 get_node_uid (unsigned long long *nodeuid, const GNUNET_HashCode * peerHash)
684 {
685   MYSQL_BIND rbind[1];
686   struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
687   unsigned long long p_len;
688
689   int ret;
690   memset (rbind, 0, sizeof (rbind));
691   rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
692   rbind[0].buffer = nodeuid;
693   rbind[0].is_unsigned = GNUNET_YES;
694
695   GNUNET_CRYPTO_hash_to_enc (peerHash, &encPeer);
696   p_len = strlen ((char *) &encPeer);
697
698   if (1 != (ret = prepared_statement_run_select (get_nodeuid,
699                                                               1,
700                                                               rbind,
701                                                               return_ok,
702                                                               NULL,
703                                                               MYSQL_TYPE_LONGLONG,
704                                                               &current_trial,
705                                                               GNUNET_YES,
706                                                               MYSQL_TYPE_VAR_STRING,
707                                                               &encPeer,
708                                                               max_varchar_len,
709                                                               &p_len, -1)))
710     {
711 #if DEBUG_DHTLOG
712       fprintf (stderr, "FAILED\n");
713 #endif
714       return GNUNET_SYSERR;
715     }
716   return GNUNET_OK;
717 }
718
719 static int
720 get_current_trial (unsigned long long *trialuid)
721 {
722   MYSQL_BIND rbind[1];
723
724   memset (rbind, 0, sizeof (rbind));
725   rbind[0].buffer_type = MYSQL_TYPE_LONG;
726   rbind[0].is_unsigned = 1;
727   rbind[0].buffer = trialuid;
728
729   if ((GNUNET_OK !=
730        prepared_statement_run_select (get_trial,
731                                       1,
732                                       rbind,
733                                       return_ok, NULL, -1)))
734     {
735       return GNUNET_SYSERR;
736     }
737
738   return GNUNET_OK;
739 }
740
741 static int
742 get_current_topology (unsigned long long *topologyuid)
743 {
744   MYSQL_BIND rbind[1];
745
746   memset (rbind, 0, sizeof (rbind));
747   rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
748   rbind[0].is_unsigned = 1;
749   rbind[0].buffer = topologyuid;
750
751   if ((GNUNET_OK !=
752        prepared_statement_run_select (get_topology,
753                                       1,
754                                       rbind,
755                                       return_ok, NULL, -1)))
756     {
757       return GNUNET_SYSERR;
758     }
759
760   return GNUNET_OK;
761 }
762
763 static int
764 get_dhtkey_uid (unsigned long long *dhtkeyuid, const GNUNET_HashCode * key)
765 {
766   MYSQL_BIND rbind[1];
767   struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
768   unsigned long long k_len;
769   memset (rbind, 0, sizeof (rbind));
770   rbind[0].buffer_type = MYSQL_TYPE_LONG;
771   rbind[0].is_unsigned = 1;
772   rbind[0].buffer = dhtkeyuid;
773   GNUNET_CRYPTO_hash_to_enc (key, &encKey);
774   k_len = strlen ((char *) &encKey);
775 #if DEBUG_DHTLOG
776   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Searching for dhtkey `%s' in trial %llu\n", GNUNET_h2s(key), current_trial);
777 #endif
778   if ((GNUNET_OK !=
779        prepared_statement_run_select (get_dhtkeyuid,
780                                       1,
781                                       rbind,
782                                       return_ok, NULL,
783                                       MYSQL_TYPE_VAR_STRING,
784                                       &encKey,
785                                       max_varchar_len,
786                                       &k_len,
787                                       MYSQL_TYPE_LONGLONG,
788                                       &current_trial,
789                                       GNUNET_YES, -1)))
790     {
791       return GNUNET_SYSERR;
792     }
793
794   return GNUNET_OK;
795 }
796
797 /**
798  * Run a prepared statement that does NOT produce results.
799  *
800  * @param s handle to the statement we should execute
801  * @param insert_id NULL or address where to store the row ID of whatever
802  *        was inserted (only for INSERT statements!)
803  * @param ... pairs and triplets of "MYSQL_TYPE_XXX" keys and their respective
804  *        values (size + buffer-reference for pointers); terminated
805  *        with "-1"
806  *
807  * @return GNUNET_SYSERR on error, otherwise
808  *         the number of successfully affected rows
809  */
810 int
811 prepared_statement_run (struct StatementHandle *s,
812                         unsigned long long *insert_id, ...)
813 {
814   va_list ap;
815   int affected;
816
817   if (GNUNET_OK != prepare_statement(s))
818     {
819       GNUNET_break(0);
820       return GNUNET_SYSERR;
821     }
822   GNUNET_assert(s->valid == GNUNET_YES);
823   if (s->statement == NULL)
824     return GNUNET_SYSERR;
825
826   va_start (ap, insert_id);
827
828   if (GNUNET_OK != init_params (s, ap))
829     {
830       va_end (ap);
831       return GNUNET_SYSERR;
832     }
833
834   va_end (ap);
835   affected = mysql_stmt_affected_rows (s->statement);
836   if (NULL != insert_id)
837     *insert_id = (unsigned long long) mysql_stmt_insert_id (s->statement);
838   mysql_stmt_reset (s->statement);
839
840   return affected;
841 }
842
843 /*
844  * Inserts the specified trial into the dhttests.trials table
845  *
846  * @param trial_info struct containing the data to insert about this trial
847  *
848  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
849  */
850 int add_trial (struct GNUNET_DHTLOG_TrialInfo *trial_info)
851 {
852   MYSQL_STMT *stmt;
853   int ret;
854   unsigned long long m_len;
855   m_len = strlen (trial_info->message);
856
857   stmt = mysql_stmt_init(conn);
858   if (GNUNET_OK !=
859       (ret = prepared_statement_run (insert_trial, &current_trial,
860                                      MYSQL_TYPE_LONG, &trial_info->other_identifier, GNUNET_YES,
861                                      MYSQL_TYPE_LONG, &trial_info->num_nodes, GNUNET_YES,
862                                      MYSQL_TYPE_LONG, &trial_info->topology, GNUNET_YES,
863                                      MYSQL_TYPE_FLOAT, &trial_info->topology_percentage,
864                                      MYSQL_TYPE_FLOAT, &trial_info->topology_probability,
865                                      MYSQL_TYPE_LONG, &trial_info->blacklist_topology, GNUNET_YES,
866                                      MYSQL_TYPE_LONG, &trial_info->connect_topology, GNUNET_YES,
867                                      MYSQL_TYPE_LONG, &trial_info->connect_topology_option, GNUNET_YES,
868                                      MYSQL_TYPE_FLOAT, &trial_info->connect_topology_option_modifier,
869                                      MYSQL_TYPE_LONG, &trial_info->puts, GNUNET_YES,
870                                      MYSQL_TYPE_LONG, &trial_info->gets, GNUNET_YES,
871                                      MYSQL_TYPE_LONG, &trial_info->concurrent, GNUNET_YES,
872                                      MYSQL_TYPE_LONG, &trial_info->settle_time, GNUNET_YES,
873                                      MYSQL_TYPE_LONG, &trial_info->num_rounds, GNUNET_YES,
874                                      MYSQL_TYPE_LONG, &trial_info->malicious_getters, GNUNET_YES,
875                                      MYSQL_TYPE_LONG, &trial_info->malicious_putters, GNUNET_YES,
876                                      MYSQL_TYPE_LONG, &trial_info->malicious_droppers, GNUNET_YES,
877                                      MYSQL_TYPE_LONG, &trial_info->malicious_get_frequency, GNUNET_YES,
878                                      MYSQL_TYPE_LONG, &trial_info->malicious_put_frequency, GNUNET_YES,
879                                      MYSQL_TYPE_LONG, &trial_info->stop_closest, GNUNET_YES,
880                                      MYSQL_TYPE_LONG, &trial_info->stop_found, GNUNET_YES,
881                                      MYSQL_TYPE_LONG, &trial_info->strict_kademlia, GNUNET_YES,
882                                      MYSQL_TYPE_LONG, &trial_info->gets_succeeded, GNUNET_YES,
883                                      MYSQL_TYPE_BLOB, trial_info->message, max_varchar_len +
884                                      max_varchar_len, &m_len,
885                                       -1)))
886     {
887       if (ret == GNUNET_SYSERR)
888         {
889           mysql_stmt_close(stmt);
890           return GNUNET_SYSERR;
891         }
892     }
893
894   get_current_trial (&current_trial);
895
896   mysql_stmt_close(stmt);
897   return GNUNET_OK;
898 }
899
900 /*
901  * Inserts the specified round into the dhttests.rounds table
902  *
903  * @param round_type the type of round that is being started
904  * @param round_count counter for the round (if applicable)
905  *
906  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
907  */
908 int add_round (unsigned int round_type, unsigned int round_count)
909 {
910
911   MYSQL_STMT *stmt;
912   int ret;
913
914   stmt = mysql_stmt_init(conn);
915   ret = prepared_statement_run (insert_round,
916                                 NULL,
917                                 MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
918                                 MYSQL_TYPE_LONG, &round_type, GNUNET_YES,
919                                 MYSQL_TYPE_LONG, &round_count, GNUNET_YES, -1);
920   mysql_stmt_close(stmt);
921   if (ret != GNUNET_OK)
922     return GNUNET_SYSERR;
923   return ret;
924 }
925
926 /*
927  * Inserts the specified round results into the
928  * dhttests.processed_round_details table
929  *
930  * @param round_type the type of round that is being started
931  * @param round_count counter for the round (if applicable)
932  * @param num_messages the total number of messages initiated
933  * @param num_messages_succeeded the number of messages that succeeded
934  *
935  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
936  */
937 int add_round_details (unsigned int round_type, unsigned int round_count,
938                        unsigned int num_messages, unsigned int num_messages_succeeded)
939 {
940   MYSQL_STMT *stmt;
941   int ret;
942
943   stmt = mysql_stmt_init(conn);
944   ret = prepared_statement_run (insert_round_details,
945                                 NULL,
946                                 MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
947                                 MYSQL_TYPE_LONG, &round_type, GNUNET_YES,
948                                 MYSQL_TYPE_LONG, &round_count, GNUNET_YES,
949                                 MYSQL_TYPE_LONG, &num_messages, GNUNET_YES,
950                                 MYSQL_TYPE_LONG, &num_messages_succeeded, GNUNET_YES,
951                                 -1);
952   mysql_stmt_close(stmt);
953   if (ret != GNUNET_OK)
954     return GNUNET_SYSERR;
955   return ret;
956 }
957
958 /*
959  * Inserts the specified stats into the dhttests.node_statistics table
960  *
961  * @param peer the peer inserting the statistic
962  * @param route_requests route requests seen
963  * @param route_forwards route requests forwarded
964  * @param result_requests route result requests seen
965  * @param client_requests client requests initiated
966  * @param result_forwards route results forwarded
967  * @param gets get requests handled
968  * @param puts put requests handle
969  * @param data_inserts data inserted at this node
970  * @param find_peer_requests find peer requests seen
971  * @param find_peers_started find peer requests initiated at this node
972  * @param gets_started get requests initiated at this node
973  * @param puts_started put requests initiated at this node
974  * @param find_peer_responses_received find peer responses received locally
975  * @param get_responses_received get responses received locally
976  * @param find_peer_responses_sent find peer responses sent from this node
977  * @param get_responses_sent get responses sent from this node
978  *
979  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
980  */
981 int
982 add_stat (const struct GNUNET_PeerIdentity *peer, unsigned int route_requests,
983           unsigned int route_forwards, unsigned int result_requests,
984           unsigned int client_requests, unsigned int result_forwards,
985           unsigned int gets, unsigned int puts,
986           unsigned int data_inserts, unsigned int find_peer_requests,
987           unsigned int find_peers_started, unsigned int gets_started,
988           unsigned int puts_started, unsigned int find_peer_responses_received,
989           unsigned int get_responses_received, unsigned int find_peer_responses_sent,
990           unsigned int get_responses_sent)
991 {
992   MYSQL_STMT *stmt;
993   int ret;
994   unsigned long long peer_uid;
995   unsigned long long return_uid;
996   if (peer == NULL)
997     return GNUNET_SYSERR;
998
999   if (GNUNET_OK != get_node_uid (&peer_uid, &peer->hashPubKey))
1000     {
1001       return GNUNET_SYSERR;
1002     }
1003
1004   stmt = mysql_stmt_init(conn);
1005   if (GNUNET_OK !=
1006       (ret = prepared_statement_run (insert_stat,
1007                                      &return_uid,
1008                                      MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1009                                      MYSQL_TYPE_LONGLONG, &peer_uid, GNUNET_YES,
1010                                      MYSQL_TYPE_LONG, &route_requests, GNUNET_YES,
1011                                      MYSQL_TYPE_LONG, &route_forwards, GNUNET_YES,
1012                                      MYSQL_TYPE_LONG, &result_requests, GNUNET_YES,
1013                                      MYSQL_TYPE_LONG, &client_requests, GNUNET_YES,
1014                                      MYSQL_TYPE_LONG, &result_forwards, GNUNET_YES,
1015                                      MYSQL_TYPE_LONG, &gets, GNUNET_YES,
1016                                      MYSQL_TYPE_LONG, &puts, GNUNET_YES,
1017                                      MYSQL_TYPE_LONG, &data_inserts, GNUNET_YES,
1018                                      MYSQL_TYPE_LONG, &find_peer_requests, GNUNET_YES,
1019                                      MYSQL_TYPE_LONG, &find_peers_started, GNUNET_YES,
1020                                      MYSQL_TYPE_LONG, &gets_started, GNUNET_YES,
1021                                      MYSQL_TYPE_LONG, &puts_started, GNUNET_YES,
1022                                      MYSQL_TYPE_LONG, &find_peer_responses_received, GNUNET_YES,
1023                                      MYSQL_TYPE_LONG, &get_responses_received, GNUNET_YES,
1024                                      MYSQL_TYPE_LONG, &find_peer_responses_sent, GNUNET_YES,
1025                                      MYSQL_TYPE_LONG, &get_responses_sent, GNUNET_YES,
1026                                      -1)))
1027     {
1028       if (ret == GNUNET_SYSERR)
1029         {
1030           mysql_stmt_close(stmt);
1031           return GNUNET_SYSERR;
1032         }
1033     }
1034
1035   mysql_stmt_close(stmt);
1036   return GNUNET_OK;
1037 }
1038
1039 /*
1040  * Inserts the specified stats into the dhttests.generic_stats table
1041  *
1042  * @param peer the peer inserting the statistic
1043  * @param name the name of the statistic
1044  * @param section the section of the statistic
1045  * @param value the value of the statistic
1046  *
1047  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1048  */
1049 int
1050 add_generic_stat (const struct GNUNET_PeerIdentity *peer,
1051                   const char *name,
1052                   const char *section, uint64_t value)
1053 {
1054   unsigned long long peer_uid;
1055   unsigned long long section_len;
1056   unsigned long long name_len;
1057   int ret;
1058   if (peer == NULL)
1059     return GNUNET_SYSERR;
1060
1061   if (GNUNET_OK != get_node_uid (&peer_uid, &peer->hashPubKey))
1062     {
1063       return GNUNET_SYSERR;
1064     }
1065
1066   section_len = strlen(section);
1067   name_len = strlen(name);
1068
1069   if (GNUNET_OK !=
1070       (ret = prepared_statement_run (insert_generic_stat,
1071                                      NULL,
1072                                      MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1073                                      MYSQL_TYPE_LONGLONG, &peer_uid, GNUNET_YES,
1074                                      MYSQL_TYPE_VAR_STRING, &section, max_varchar_len, &section_len,
1075                                      MYSQL_TYPE_VAR_STRING, &name, max_varchar_len, &name_len,
1076                                      MYSQL_TYPE_LONGLONG, &value, GNUNET_YES,
1077                                      -1)))
1078     {
1079       if (ret == GNUNET_SYSERR)
1080         {
1081           return GNUNET_SYSERR;
1082         }
1083     }
1084   return GNUNET_OK;
1085 }
1086
1087 /*
1088  * Inserts the specified dhtkey into the dhttests.dhtkeys table,
1089  * stores return value of dhttests.dhtkeys.dhtkeyuid into dhtkeyuid
1090  *
1091  * @param dhtkeyuid return value
1092  * @param dhtkey hashcode of key to insert
1093  *
1094  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1095  */
1096 int
1097 add_dhtkey (unsigned long long *dhtkeyuid, const GNUNET_HashCode * dhtkey)
1098 {
1099
1100   int ret;
1101   struct GNUNET_CRYPTO_HashAsciiEncoded encKey;
1102   unsigned long long k_len;
1103   unsigned long long h_len;
1104   unsigned long long curr_dhtkeyuid;
1105   GNUNET_CRYPTO_hash_to_enc (dhtkey, &encKey);
1106   k_len = strlen ((char *) &encKey);
1107   h_len = sizeof (GNUNET_HashCode);
1108   curr_dhtkeyuid = 0;
1109   ret = get_dhtkey_uid(&curr_dhtkeyuid, dhtkey);
1110   if (curr_dhtkeyuid != 0) /* dhtkey already exists */
1111     {
1112       if (dhtkeyuid != NULL)
1113         *dhtkeyuid = curr_dhtkeyuid;
1114       return GNUNET_OK;
1115     }
1116   else if (ret == GNUNET_SYSERR)
1117     {
1118 #if DEBUG_DHTLOG
1119       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Failed to get dhtkeyuid!\n");
1120 #endif
1121     }
1122
1123   if (GNUNET_OK !=
1124       (ret = prepared_statement_run (insert_dhtkey,
1125                                      dhtkeyuid,
1126                                      MYSQL_TYPE_VAR_STRING,
1127                                      &encKey,
1128                                      max_varchar_len,
1129                                      &k_len,
1130                                      MYSQL_TYPE_LONG,
1131                                      &current_trial,
1132                                      GNUNET_YES,
1133                                      MYSQL_TYPE_BLOB,
1134                                      dhtkey,
1135                                      sizeof (GNUNET_HashCode),
1136                                      &h_len, -1)))
1137     {
1138       if (ret == GNUNET_SYSERR)
1139         {
1140           return GNUNET_SYSERR;
1141         }
1142     }
1143
1144   return GNUNET_OK;
1145 }
1146
1147
1148
1149 /*
1150  * Inserts the specified node into the dhttests.nodes table
1151  *
1152  * @param nodeuid the inserted node uid
1153  * @param node the node to insert
1154  *
1155  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1156  */
1157 int
1158 add_node (unsigned long long *nodeuid, struct GNUNET_PeerIdentity * node)
1159 {
1160   struct GNUNET_CRYPTO_HashAsciiEncoded encPeer;
1161   unsigned long p_len;
1162   unsigned long h_len;
1163   int ret;
1164
1165   if (node == NULL)
1166     return GNUNET_SYSERR;
1167
1168   GNUNET_CRYPTO_hash_to_enc (&node->hashPubKey, &encPeer);
1169   p_len = (unsigned long) strlen ((char *) &encPeer);
1170   h_len = sizeof (GNUNET_HashCode);
1171   if (GNUNET_OK !=
1172       (ret = prepared_statement_run (insert_node,
1173                                      nodeuid,
1174                                      MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1175                                      MYSQL_TYPE_VAR_STRING, &encPeer, max_varchar_len, &p_len,
1176                                      MYSQL_TYPE_BLOB, &node->hashPubKey, sizeof (GNUNET_HashCode),
1177                                      &h_len, -1)))
1178     {
1179       if (ret == GNUNET_SYSERR)
1180         {
1181           return GNUNET_SYSERR;
1182         }
1183     }
1184   return GNUNET_OK;
1185 }
1186
1187 /*
1188  * Update dhttests.trials table with current server time as end time
1189  *
1190  * @param gets_succeeded how many gets did the testcase report as successful
1191  *
1192  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1193  */
1194 int
1195 update_trials (unsigned int gets_succeeded)
1196 {
1197   int ret;
1198
1199   if (GNUNET_OK !=
1200       (ret = prepared_statement_run (update_trial,
1201                                     NULL,
1202                                     MYSQL_TYPE_LONG, &gets_succeeded, GNUNET_YES,
1203                                     MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1204                                     -1)))
1205     {
1206       if (ret == GNUNET_SYSERR)
1207         {
1208           return GNUNET_SYSERR;
1209         }
1210     }
1211   if (ret > 0)
1212     return GNUNET_OK;
1213   else
1214     return GNUNET_SYSERR;
1215 }
1216
1217
1218 /*
1219  * Update dhttests.nodes table setting the identified
1220  * node as a malicious dropper.
1221  *
1222  * @param peer the peer that was set to be malicious
1223  *
1224  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1225  */
1226 int set_malicious (struct GNUNET_PeerIdentity *peer)
1227 {
1228   unsigned long long p_len;
1229   int ret;
1230   char *temp_str;
1231
1232   temp_str = GNUNET_strdup(GNUNET_h2s_full(&peer->hashPubKey));
1233   p_len = strlen(temp_str);
1234
1235   if (GNUNET_OK !=
1236       (ret = prepared_statement_run (update_node_malicious,
1237                                     NULL,
1238                                     MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1239                                     MYSQL_TYPE_VAR_STRING, temp_str, max_varchar_len, &p_len,
1240                                     -1)))
1241     {
1242       if (ret == GNUNET_SYSERR)
1243         {
1244           return GNUNET_SYSERR;
1245         }
1246     }
1247   return GNUNET_OK;
1248 }
1249
1250
1251 /*
1252  * Update dhttests.trials table with total connections information
1253  *
1254  * @param totalConnections the number of connections
1255  *
1256  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1257  */
1258 int
1259 add_connections (unsigned int totalConnections)
1260 {
1261   int ret;
1262
1263   if (GNUNET_OK !=
1264       (ret = prepared_statement_run (update_connection,
1265                                                   NULL,
1266                                                   MYSQL_TYPE_LONG,
1267                                                   &totalConnections,
1268                                                   GNUNET_YES,
1269                                                   MYSQL_TYPE_LONGLONG,
1270                                                   &current_trial, GNUNET_YES, -1)))
1271     {
1272       if (ret == GNUNET_SYSERR)
1273         {
1274           return GNUNET_SYSERR;
1275         }
1276     }
1277   if (ret > 0)
1278     return GNUNET_OK;
1279   else
1280     return GNUNET_SYSERR;
1281 }
1282
1283 /*
1284  * Inserts the specified query into the dhttests.queries table
1285  *
1286  * @param sqlqueruid inserted query uid
1287  * @param queryid dht query id
1288  * @param type type of the query
1289  * @param hops number of hops query traveled
1290  * @param succeeded whether or not query was successful
1291  * @param node the node the query hit
1292  * @param key the key of the query
1293  *
1294  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1295  */
1296 int
1297 add_query (unsigned long long *sqlqueryuid, unsigned long long queryid,
1298            unsigned int type, unsigned int hops, int succeeded,
1299            const struct GNUNET_PeerIdentity * node, const GNUNET_HashCode * key)
1300 {
1301   int ret;
1302   unsigned long long peer_uid, key_uid;
1303   peer_uid = 0;
1304   key_uid = 0;
1305
1306   if ((node != NULL)
1307       && (GNUNET_OK == get_node_uid (&peer_uid, &node->hashPubKey)))
1308     {
1309
1310     }
1311   else
1312     {
1313       return GNUNET_SYSERR;
1314     }
1315
1316   if ((key != NULL) && (GNUNET_OK == get_dhtkey_uid (&key_uid, key)))
1317     {
1318
1319     }
1320   else if ((key != NULL) && (key->bits[(512 / 8 / sizeof (unsigned int)) - 1] == 42))   /* Malicious marker */
1321     {
1322       key_uid = 0;
1323     }
1324   else
1325     {
1326       return GNUNET_SYSERR;
1327     }
1328
1329   if (GNUNET_OK !=
1330       (ret = prepared_statement_run (insert_query,
1331                                                   sqlqueryuid,
1332                                                   MYSQL_TYPE_LONGLONG,
1333                                                   &current_trial,
1334                                                   GNUNET_YES,
1335                                                   MYSQL_TYPE_LONG,
1336                                                   &type,
1337                                                   GNUNET_NO,
1338                                                   MYSQL_TYPE_LONG,
1339                                                   &hops,
1340                                                   GNUNET_YES,
1341                                                   MYSQL_TYPE_LONGLONG,
1342                                                   &key_uid,
1343                                                   GNUNET_YES,
1344                                                   MYSQL_TYPE_LONGLONG,
1345                                                   &queryid,
1346                                                   GNUNET_YES,
1347                                                   MYSQL_TYPE_LONG,
1348                                                   &succeeded,
1349                                                   GNUNET_NO,
1350                                                   MYSQL_TYPE_LONGLONG,
1351                                                   &peer_uid, GNUNET_YES, -1)))
1352     {
1353       if (ret == GNUNET_SYSERR)
1354         {
1355           return GNUNET_SYSERR;
1356         }
1357     }
1358   if (ret > 0)
1359     return GNUNET_OK;
1360   else
1361     return GNUNET_SYSERR;
1362 }
1363
1364 /*
1365  * Inserts the specified route information into the dhttests.routes table
1366  *
1367  * @param sqlqueruid inserted query uid
1368  * @param queryid dht query id
1369  * @param type type of the query
1370  * @param hops number of hops query traveled
1371  * @param succeeded whether or not query was successful
1372  * @param node the node the query hit
1373  * @param key the key of the query
1374  * @param from_node the node that sent the message to node
1375  * @param to_node next node to forward message to
1376  *
1377  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1378  */
1379 int
1380 add_route (unsigned long long *sqlqueryuid, unsigned long long queryid,
1381            unsigned int type, unsigned int hops,
1382            int succeeded, const struct GNUNET_PeerIdentity * node,
1383            const GNUNET_HashCode * key, const struct GNUNET_PeerIdentity * from_node,
1384            const struct GNUNET_PeerIdentity * to_node)
1385 {
1386   unsigned long long peer_uid = 0;
1387   unsigned long long key_uid = 0;
1388   unsigned long long from_uid = 0;
1389   unsigned long long to_uid = 0;
1390   int ret;
1391
1392   if (from_node != NULL)
1393     get_node_uid (&from_uid, &from_node->hashPubKey);
1394
1395   if (to_node != NULL)
1396     get_node_uid (&to_uid, &to_node->hashPubKey);
1397   else
1398     to_uid = 0;
1399
1400   if ((node != NULL))
1401     {
1402       if (1 != get_node_uid (&peer_uid, &node->hashPubKey))
1403         {
1404           return GNUNET_SYSERR;
1405         }
1406     }
1407   else
1408     return GNUNET_SYSERR;
1409
1410   if ((key != NULL))
1411     {
1412       if (1 != get_dhtkey_uid (&key_uid, key))
1413         {
1414           return GNUNET_SYSERR;
1415         }
1416     }
1417   else
1418     return GNUNET_SYSERR;
1419
1420   if (GNUNET_OK !=
1421       (ret = prepared_statement_run (insert_route,
1422                                     sqlqueryuid,
1423                                     MYSQL_TYPE_LONGLONG,
1424                                     &current_trial,
1425                                     GNUNET_YES,
1426                                     MYSQL_TYPE_LONG,
1427                                     &type,
1428                                     GNUNET_NO,
1429                                     MYSQL_TYPE_LONG,
1430                                     &hops,
1431                                     GNUNET_YES,
1432                                     MYSQL_TYPE_LONGLONG,
1433                                     &key_uid,
1434                                     GNUNET_YES,
1435                                     MYSQL_TYPE_LONGLONG,
1436                                     &queryid,
1437                                     GNUNET_YES,
1438                                     MYSQL_TYPE_LONG,
1439                                     &succeeded,
1440                                     GNUNET_NO,
1441                                     MYSQL_TYPE_LONGLONG,
1442                                     &peer_uid,
1443                                     GNUNET_YES,
1444                                     MYSQL_TYPE_LONGLONG,
1445                                     &from_uid,
1446                                     GNUNET_YES,
1447                                     MYSQL_TYPE_LONGLONG,
1448                                     &to_uid, GNUNET_YES, -1)))
1449     {
1450       if (ret == GNUNET_SYSERR)
1451         {
1452           return GNUNET_SYSERR;
1453         }
1454     }
1455   if (ret > 0)
1456     return GNUNET_OK;
1457   else
1458     return GNUNET_SYSERR;
1459 }
1460
1461 /*
1462  * Update dhttests.topology table with total connections information
1463  *
1464  * @param totalConnections the number of connections
1465  *
1466  * @return GNUNET_OK on success, GNUNET_SYSERR on failure.
1467  */
1468 int
1469 update_current_topology (unsigned int connections)
1470 {
1471   int ret;
1472   unsigned long long topologyuid;
1473
1474   get_current_topology(&topologyuid);
1475
1476   if (GNUNET_OK !=
1477       (ret = prepared_statement_run (update_topology,
1478                                      NULL,
1479                                      MYSQL_TYPE_LONG,
1480                                      &connections,
1481                                      GNUNET_YES,
1482                                      MYSQL_TYPE_LONGLONG,
1483                                      &topologyuid, GNUNET_YES, -1)))
1484     {
1485       if (ret == GNUNET_SYSERR)
1486         {
1487           return GNUNET_SYSERR;
1488         }
1489     }
1490   if (ret > 0)
1491     return GNUNET_OK;
1492   return GNUNET_SYSERR;
1493
1494 }
1495
1496 /*
1497  * Records the current topology (number of connections, time, trial)
1498  *
1499  * @param num_connections how many connections are in the topology
1500  *
1501  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1502  */
1503 int
1504 add_topology (int num_connections)
1505 {
1506   int ret;
1507
1508   if (GNUNET_OK !=
1509       (ret = prepared_statement_run (insert_topology,
1510                                      NULL,
1511                                      MYSQL_TYPE_LONGLONG, &current_trial, GNUNET_YES,
1512                                      MYSQL_TYPE_LONG, &num_connections, GNUNET_YES,
1513                                      -1)))
1514     {
1515       if (ret == GNUNET_SYSERR)
1516         {
1517           return GNUNET_SYSERR;
1518         }
1519     }
1520   if (ret > 0)
1521     return GNUNET_OK;
1522   return GNUNET_SYSERR;
1523 }
1524
1525 /*
1526  * Records a connection between two peers in the current topology
1527  *
1528  * @param first one side of the connection
1529  * @param second other side of the connection
1530  *
1531  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1532  */
1533 int
1534 add_extended_topology (const struct GNUNET_PeerIdentity *first, const struct GNUNET_PeerIdentity *second)
1535 {
1536   int ret;
1537   unsigned long long first_uid;
1538   unsigned long long second_uid;
1539   unsigned long long topologyuid;
1540
1541   if (GNUNET_OK != get_current_topology(&topologyuid))
1542     return GNUNET_SYSERR;
1543   if (GNUNET_OK != get_node_uid(&first_uid, &first->hashPubKey))
1544     return GNUNET_SYSERR;
1545   if (GNUNET_OK != get_node_uid(&second_uid, &second->hashPubKey))
1546     return GNUNET_SYSERR;
1547
1548   if (GNUNET_OK !=
1549       (ret = prepared_statement_run (extend_topology,
1550                                      NULL,
1551                                      MYSQL_TYPE_LONGLONG,
1552                                      &topologyuid,
1553                                      GNUNET_YES,
1554                                      MYSQL_TYPE_LONGLONG,
1555                                      &first_uid,
1556                                      GNUNET_YES,
1557                                      MYSQL_TYPE_LONGLONG,
1558                                      &second_uid,
1559                                      GNUNET_YES,-1)))
1560     {
1561       if (ret == GNUNET_SYSERR)
1562         {
1563           return GNUNET_SYSERR;
1564         }
1565     }
1566   if (ret > 0)
1567     return GNUNET_OK;
1568   return GNUNET_SYSERR;
1569 }
1570
1571
1572 /*
1573  * Provides the dhtlog api
1574  *
1575  * @param c the configuration to use to connect to a server
1576  *
1577  * @return the handle to the server, or NULL on error
1578  */
1579 void *
1580 libgnunet_plugin_dhtlog_mysql_init (void * cls)
1581 {
1582   struct GNUNET_DHTLOG_Plugin *plugin = cls;
1583
1584   cfg = plugin->cfg;
1585   max_varchar_len = 255;
1586 #if DEBUG_DHTLOG
1587   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MySQL DHT Logger: initializing database\n");
1588 #endif
1589
1590   if (iopen (plugin) != GNUNET_OK)
1591     {
1592       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1593                   _("Failed to initialize MySQL database connection for dhtlog.\n"));
1594       return NULL;
1595     }
1596
1597   GNUNET_assert(plugin->dhtlog_api == NULL);
1598   plugin->dhtlog_api = GNUNET_malloc(sizeof(struct GNUNET_DHTLOG_Handle));
1599   plugin->dhtlog_api->insert_trial = &add_trial;
1600   plugin->dhtlog_api->insert_stat = &add_stat;
1601   plugin->dhtlog_api->insert_round = &add_round;
1602   plugin->dhtlog_api->insert_round_details = &add_round_details;
1603   plugin->dhtlog_api->add_generic_stat = &add_generic_stat;
1604   plugin->dhtlog_api->insert_query = &add_query;
1605   plugin->dhtlog_api->update_trial = &update_trials;
1606   plugin->dhtlog_api->insert_route = &add_route;
1607   plugin->dhtlog_api->insert_node = &add_node;
1608   plugin->dhtlog_api->insert_dhtkey = &add_dhtkey;
1609   plugin->dhtlog_api->update_connections = &add_connections;
1610   plugin->dhtlog_api->insert_topology = &add_topology;
1611   plugin->dhtlog_api->update_topology = &update_current_topology;
1612   plugin->dhtlog_api->insert_extended_topology = &add_extended_topology;
1613   plugin->dhtlog_api->set_malicious = &set_malicious;
1614   plugin->dhtlog_api->add_generic_stat = &add_generic_stat;
1615   get_current_trial (&current_trial);
1616
1617   return plugin;
1618 }
1619
1620 /**
1621  * Shutdown the plugin.
1622  */
1623 void *
1624 libgnunet_plugin_dhtlog_mysql_done (void * cls)
1625 {
1626   struct GNUNET_DHTLOG_Handle *dhtlog_api = cls;
1627
1628   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1629               "MySQL DHT Logger: database shutdown\n");
1630   GNUNET_assert(dhtlog_api != NULL);
1631   prepared_statement_close(insert_query);
1632   prepared_statement_close(insert_route);
1633   prepared_statement_close(insert_trial);
1634   prepared_statement_close(insert_round);
1635   prepared_statement_close(insert_round_details);
1636   prepared_statement_close(insert_node);
1637   prepared_statement_close(insert_dhtkey);
1638   prepared_statement_close(update_trial);
1639   prepared_statement_close(get_dhtkeyuid);
1640   prepared_statement_close(get_nodeuid);
1641   prepared_statement_close(update_connection);
1642   prepared_statement_close(get_trial);
1643   prepared_statement_close(get_topology);
1644   prepared_statement_close(insert_topology);
1645   prepared_statement_close(update_topology);
1646   prepared_statement_close(extend_topology);
1647   prepared_statement_close(update_node_malicious);
1648
1649   if (conn != NULL)
1650     mysql_close (conn);
1651   conn = NULL;
1652   mysql_library_end();
1653   GNUNET_free(dhtlog_api);
1654   return NULL;
1655 }
1656
1657 /* end of plugin_dhtlog_mysql.c */