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