stub
[oweals/gnunet.git] / src / dht / gnunet-dht-driver.c
1 /*
2      This file is part of GNUnet.
3      (C) 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 3, 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  * @file dht/gnunet-dht-driver.c
22  * @brief Driver for setting up a group of gnunet peers and
23  *        then issuing GETS and PUTS on the DHT.  Coarse results
24  *        are reported, fine grained results (if requested) are
25  *        logged to a (mysql) database.
26  *
27  *  TODO: Add multiple database support; alternatively, dump
28  *        sql readable (or easily transformed) logs to disk
29  *        for reassembly later.  This could remove the mysql
30  *        server as a bottleneck during testing.
31  */
32 #include "platform.h"
33 #include "gnunet_testing_lib.h"
34 #include "gnunet_core_service.h"
35 #include "gnunet_dht_service.h"
36 #include "dhtlog.h"
37
38 /* DEFINES */
39 #define VERBOSE GNUNET_YES
40
41 /* Timeout for entire driver to run */
42 #define DEFAULT_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 5)
43
44 /* Timeout for waiting for (individual) replies to get requests */
45 #define DEFAULT_GET_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
46
47 /* Timeout for waiting for gets to be sent to the service */
48 #define DEFAULT_GET_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 10)
49
50 /* Timeout for waiting for puts to be sent to the service */
51 #define DEFAULT_PUT_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 10)
52
53 #define DEFAULT_SECONDS_PER_PEER_START GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 45)
54
55 #define DEFAULT_TEST_DATA_SIZE 8
56
57 #define DEFAULT_MAX_OUTSTANDING_PUTS 10
58
59 #define DEFAULT_MAX_OUTSTANDING_GETS 10
60
61 #define DEFAULT_CONNECT_TIMEOUT 60
62
63 #define DEFAULT_TOPOLOGY_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 8)
64
65 /* Structs */
66
67 struct TestPutContext
68 {
69   /* This is a linked list */
70   struct TestPutContext *next;
71
72   /**
73    * Handle to the first peers DHT service (via the API)
74    */
75   struct GNUNET_DHT_Handle *dht_handle;
76
77   /**
78    *  Handle to the PUT peer daemon
79    */
80   struct GNUNET_TESTING_Daemon *daemon;
81
82   /**
83    *  Identifier for this PUT
84    */
85   uint32_t uid;
86
87   /**
88    * Task for disconnecting DHT handles
89    */
90   GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
91 };
92
93 struct TestGetContext
94 {
95   /* This is a linked list */
96   struct TestGetContext *next;
97
98   /**
99    * Handle to the first peers DHT service (via the API)
100    */
101   struct GNUNET_DHT_Handle *dht_handle;
102
103   /**
104    * Handle for the DHT get request
105    */
106   struct GNUNET_DHT_GetHandle *get_handle;
107
108   /**
109    *  Handle to the GET peer daemon
110    */
111   struct GNUNET_TESTING_Daemon *daemon;
112
113   /**
114    *  Identifier for this GET
115    */
116   uint32_t uid;
117
118   /**
119    * Task for disconnecting DHT handles (and stopping GET)
120    */
121   GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
122
123   /**
124    * Whether or not this request has been fulfilled already.
125    */
126   int succeeded;
127 };
128
129 /**
130  * Simple struct to keep track of progress, and print a
131  * nice little percentage meter for long running tasks.
132  */
133 struct ProgressMeter
134 {
135   unsigned int total;
136
137   unsigned int modnum;
138
139   unsigned int dotnum;
140
141   unsigned int completed;
142
143   int print;
144
145   char *startup_string;
146 };
147
148 /* Globals */
149
150 /**
151  * Timeout to let all get requests happen.
152  */
153 static struct GNUNET_TIME_Relative all_get_timeout;
154
155 /**
156  * Per get timeout
157  */
158 static struct GNUNET_TIME_Relative get_timeout;
159
160 static struct GNUNET_TIME_Relative get_delay;
161
162 static struct GNUNET_TIME_Relative put_delay;
163
164 static struct GNUNET_TIME_Relative seconds_per_peer_start;
165
166 static unsigned long long test_data_size = DEFAULT_TEST_DATA_SIZE;
167
168 static unsigned long long max_outstanding_puts = DEFAULT_MAX_OUTSTANDING_PUTS;
169
170 static unsigned long long max_outstanding_gets = DEFAULT_MAX_OUTSTANDING_GETS;
171
172 static unsigned long long malicious_getters;
173
174 static unsigned long long malicious_putters;
175
176 static unsigned long long malicious_droppers;
177
178 static unsigned long long settle_time;
179
180 static struct GNUNET_DHTLOG_Handle *dhtlog_handle;
181
182 static unsigned long long trialuid;
183
184 /**
185  * List of GETS to perform
186  */
187 struct TestGetContext *all_gets;
188
189 /**
190  * List of PUTS to perform
191  */
192 struct TestPutContext *all_puts;
193
194 /**
195  * Directory to store temporary data in, defined in config file
196  */
197 static char *test_directory;
198
199 /**
200  * Variable used to store the number of connections we should wait for.
201  */
202 static unsigned int expected_connections;
203
204 /**
205  * Variable used to keep track of how many peers aren't yet started.
206  */
207 static unsigned long long peers_left;
208
209 /**
210  * Handle to the set of all peers run for this test.
211  */
212 static struct GNUNET_TESTING_PeerGroup *pg;
213
214 /**
215  * Global scheduler, used for all GNUNET_SCHEDULER_* functions.
216  */
217 static struct GNUNET_SCHEDULER_Handle *sched;
218
219 /**
220  * Total number of peers to run, set based on config file.
221  */
222 static unsigned long long num_peers;
223
224 /**
225  * Total number of items to insert.
226  */
227 static unsigned long long num_puts;
228
229 /**
230  * Total number of items to attempt to get.
231  */
232 static unsigned long long num_gets;
233
234 /**
235  * How many puts do we currently have in flight?
236  */
237 static unsigned long long outstanding_puts;
238
239 /**
240  * How many puts are done?
241  */
242 static unsigned long long puts_completed;
243
244 /**
245  * How many puts do we currently have in flight?
246  */
247 static unsigned long long outstanding_gets;
248
249 /**
250  * How many gets are done?
251  */
252 static unsigned long long gets_completed;
253
254 /**
255  * How many gets failed?
256  */
257 static unsigned long long gets_failed;
258
259 /**
260  * Global used to count how many connections we have currently
261  * been notified about (how many times has topology_callback been called
262  * with success?)
263  */
264 static unsigned int total_connections;
265
266 /**
267  * Global used to count how many failed connections we have
268  * been notified about (how many times has topology_callback
269  * been called with failure?)
270  */
271 static unsigned int failed_connections;
272
273 /* Task handle to use to schedule shutdown if something goes wrong */
274 GNUNET_SCHEDULER_TaskIdentifier die_task;
275
276 static char *blacklist_transports;
277
278 static enum GNUNET_TESTING_Topology topology;
279
280 static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* Don't do any blacklisting */
281
282 static enum GNUNET_TESTING_Topology connect_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* NONE actually means connect all allowed peers */
283
284 static enum GNUNET_TESTING_TopologyOption connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL;
285
286 static double connect_topology_option_modifier = 0.0;
287
288 static struct ProgressMeter *hostkey_meter;
289
290 static struct ProgressMeter *peer_start_meter;
291
292 static struct ProgressMeter *peer_connect_meter;
293
294 static struct ProgressMeter *put_meter;
295
296 static struct ProgressMeter *get_meter;
297
298 /* Global return value (0 for success, anything else for failure) */
299 static int ok;
300
301 /**
302  * Create a meter to keep track of the progress of some task.
303  *
304  * @param total the total number of items to complete
305  * @param start_string a string to prefix the meter with (if printing)
306  * @param print GNUNET_YES to print the meter, GNUNET_NO to count
307  *              internally only
308  *
309  * @return the progress meter
310  */
311 static struct ProgressMeter *
312 create_meter(unsigned int total, char * start_string, int print)
313 {
314   struct ProgressMeter *ret;
315   ret = GNUNET_malloc(sizeof(struct ProgressMeter));
316   ret->print = print;
317   ret->total = total;
318   ret->modnum = total / 4;
319   ret->dotnum = (total / 50) + 1;
320   if (start_string != NULL)
321     ret->startup_string = GNUNET_strdup(start_string);
322   else
323     ret->startup_string = GNUNET_strdup("");
324
325   return ret;
326 }
327
328 /**
329  * Update progress meter (increment by one).
330  *
331  * @param meter the meter to update and print info for
332  *
333  * @return GNUNET_YES if called the total requested,
334  *         GNUNET_NO if more items expected
335  */
336 static int
337 update_meter(struct ProgressMeter *meter)
338 {
339   if (meter->print == GNUNET_YES)
340     {
341       if (meter->completed % meter->modnum == 0)
342         {
343           if (meter->completed == 0)
344             {
345               fprintf(stdout, "%sProgress: [0%%", meter->startup_string);
346             }
347           else
348             fprintf(stdout, "%d%%", (int)(((float)meter->completed / meter->total) * 100));
349         }
350       else if (meter->completed % meter->dotnum == 0)
351         fprintf(stdout, ".");
352
353       if (meter->completed + 1 == meter->total)
354         fprintf(stdout, "%d%%]\n", 100);
355       fflush(stdout);
356     }
357   meter->completed++;
358
359   if (meter->completed == meter->total)
360     return GNUNET_YES;
361   return GNUNET_NO;
362 }
363
364 /**
365  * Release resources for meter
366  *
367  * @param meter the meter to free
368  */
369 static void
370 free_meter(struct ProgressMeter *meter)
371 {
372   GNUNET_free_non_null(meter->startup_string);
373   GNUNET_free_non_null(meter);
374 }
375
376 /**
377  * Check whether peers successfully shut down.
378  */
379 void shutdown_callback (void *cls,
380                         const char *emsg)
381 {
382   if (emsg != NULL)
383     {
384       if (ok == 0)
385         ok = 2;
386     }
387 }
388
389 /**
390  * Task to release DHT handles for PUT
391  */
392 static void
393 put_disconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
394 {
395   struct TestPutContext *test_put = cls;
396   test_put->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
397   GNUNET_DHT_disconnect(test_put->dht_handle);
398   test_put->dht_handle = NULL;
399 }
400
401 /**
402  * Function scheduled to be run on the successful completion of this
403  * testcase.
404  */
405 static void
406 finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
407 {
408   GNUNET_assert (pg != NULL);
409   struct TestPutContext *test_put = all_puts;
410   struct TestGetContext *test_get = all_gets;
411
412   while (test_put != NULL)
413     {
414       if (test_put->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
415         GNUNET_SCHEDULER_cancel(sched, test_put->disconnect_task);
416       if (test_put->dht_handle != NULL)
417         GNUNET_DHT_disconnect(test_put->dht_handle);
418       test_put = test_put->next;
419     }
420
421   while (test_get != NULL)
422     {
423       if (test_get->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
424         GNUNET_SCHEDULER_cancel(sched, test_get->disconnect_task);
425       if (test_get->get_handle != NULL)
426         GNUNET_DHT_get_stop(test_get->get_handle, NULL, NULL);
427       if (test_get->dht_handle != NULL)
428         GNUNET_DHT_disconnect(test_get->dht_handle);
429       test_get = test_get->next;
430     }
431
432   GNUNET_TESTING_daemons_stop (pg, DEFAULT_TIMEOUT, &shutdown_callback, NULL);
433
434   /* FIXME: optionally get stats for dropped messages, etc. */
435   if (dhtlog_handle != NULL)
436     dhtlog_handle->update_trial (trialuid, 0, 0, 0);
437
438   if (hostkey_meter != NULL)
439     free_meter(hostkey_meter);
440   if (hostkey_meter != NULL)
441     free_meter(peer_start_meter);
442   if (hostkey_meter != NULL)
443     free_meter(peer_connect_meter);
444   if (hostkey_meter != NULL)
445     free_meter(put_meter);
446   if (hostkey_meter != NULL)
447     free_meter(get_meter);
448
449   ok = 0;
450 }
451
452
453 /**
454  * Check if the get_handle is being used, if so stop the request.  Either
455  * way, schedule the end_badly_cont function which actually shuts down the
456  * test.
457  */
458 static void
459 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
460 {
461   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failing test with error: `%s'!\n", (char *)cls);
462
463   struct TestPutContext *test_put = all_puts;
464   struct TestGetContext *test_get = all_gets;
465
466   while (test_put != NULL)
467     {
468       if (test_put->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
469         GNUNET_SCHEDULER_cancel(sched, test_put->disconnect_task);
470       if (test_put->dht_handle != NULL)
471         GNUNET_DHT_disconnect(test_put->dht_handle);
472       test_put = test_put->next;
473     }
474
475   while (test_get != NULL)
476     {
477       if (test_get->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
478         GNUNET_SCHEDULER_cancel(sched, test_get->disconnect_task);
479       if (test_get->get_handle != NULL)
480         GNUNET_DHT_get_stop(test_get->get_handle, NULL, NULL);
481       if (test_get->dht_handle != NULL)
482         GNUNET_DHT_disconnect(test_get->dht_handle);
483       test_get = test_get->next;
484     }
485
486   GNUNET_TESTING_daemons_stop (pg, DEFAULT_TIMEOUT, &shutdown_callback, NULL);
487
488   if (hostkey_meter != NULL)
489     free_meter(hostkey_meter);
490   if (hostkey_meter != NULL)
491     free_meter(peer_start_meter);
492   if (hostkey_meter != NULL)
493     free_meter(peer_connect_meter);
494   if (hostkey_meter != NULL)
495     free_meter(put_meter);
496   if (hostkey_meter != NULL)
497     free_meter(get_meter);
498
499   ok = 1;
500 }
501
502 /**
503  * Task to release DHT handle associated with GET request.
504  */
505 static void
506 get_stop_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
507 {
508   struct TestGetContext *test_get = cls;
509   outstanding_gets--; /* GET is really finished */
510   GNUNET_DHT_disconnect(test_get->dht_handle);
511   test_get->dht_handle = NULL;
512
513 #if VERBOSE > 1
514   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%d gets succeeded, %d gets failed!\n", gets_completed, gets_failed);
515 #endif
516   update_meter(get_meter);
517   if ((gets_completed == num_gets) && (outstanding_gets == 0))/* All gets successful */
518     {
519       GNUNET_SCHEDULER_cancel(sched, die_task);
520       GNUNET_SCHEDULER_add_now(sched, &finish_testing, NULL);
521     }
522   else if ((gets_completed + gets_failed == num_gets) && (outstanding_gets == 0)) /* Had some failures */
523     {
524       GNUNET_SCHEDULER_cancel(sched, die_task);
525       GNUNET_SCHEDULER_add_now(sched, &finish_testing, NULL);
526     }
527 }
528
529 /**
530  * Task to release get handle.
531  */
532 static void
533 get_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
534 {
535   struct TestGetContext *test_get = cls;
536
537   if (tc->reason == GNUNET_SCHEDULER_REASON_TIMEOUT)
538     gets_failed++;
539   GNUNET_assert(test_get->get_handle != NULL);
540   GNUNET_DHT_get_stop(test_get->get_handle, &get_stop_finished, test_get);
541   test_get->get_handle = NULL;
542   test_get->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
543 }
544
545 /**
546  * Iterator called if the GET request initiated returns a response.
547  *
548  * @param cls closure
549  * @param exp when will this value expire
550  * @param key key of the result
551  * @param type type of the result
552  * @param size number of bytes in data
553  * @param data pointer to the result data
554  */
555 void get_result_iterator (void *cls,
556                           struct GNUNET_TIME_Absolute exp,
557                           const GNUNET_HashCode * key,
558                           uint32_t type,
559                           uint32_t size,
560                           const void *data)
561 {
562   struct TestGetContext *test_get = cls;
563   GNUNET_HashCode search_key; /* Key stored under */
564   char original_data[test_data_size]; /* Made up data to store */
565
566   memset(original_data, test_get->uid, sizeof(original_data));
567   GNUNET_CRYPTO_hash(original_data, test_data_size, &search_key);
568
569   if (test_get->succeeded == GNUNET_YES)
570     return; /* Get has already been successful, probably ending now */
571
572   if ((0 != memcmp(&search_key, key, sizeof (GNUNET_HashCode))) || (0 != memcmp(original_data, data, sizeof(original_data))))
573     {
574       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Key or data is not the same as was inserted!\n");
575     }
576   else
577     {
578       gets_completed++;
579       test_get->succeeded = GNUNET_YES;
580     }
581 #if VERBOSE > 1
582   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received correct GET response!\n");
583 #endif
584   GNUNET_SCHEDULER_cancel(sched, test_get->disconnect_task);
585   GNUNET_SCHEDULER_add_continuation(sched, &get_stop_task, test_get, GNUNET_SCHEDULER_REASON_PREREQ_DONE);
586 }
587
588 /**
589  * Continuation telling us GET request was sent.
590  */
591 static void
592 get_continuation (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
593 {
594   // Is there something to be done here?
595   if (tc->reason != GNUNET_SCHEDULER_REASON_PREREQ_DONE)
596     return;
597 }
598
599 /**
600  * Set up some data, and call API PUT function
601  */
602 static void
603 do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
604 {
605   struct TestGetContext *test_get = cls;
606   GNUNET_HashCode key; /* Made up key to store data under */
607   char data[test_data_size]; /* Made up data to store */
608
609   if (num_gets == 0)
610     {
611       GNUNET_SCHEDULER_cancel(sched, die_task);
612       GNUNET_SCHEDULER_add_now(sched, &finish_testing, NULL);
613     }
614   if (test_get == NULL)
615     return; /* End of the list */
616   memset(data, test_get->uid, sizeof(data));
617   GNUNET_CRYPTO_hash(data, test_data_size, &key);
618
619   if (outstanding_gets > max_outstanding_gets)
620     {
621       GNUNET_SCHEDULER_add_delayed (sched, get_delay, &do_get, test_get);
622       return;
623     }
624
625   test_get->dht_handle = GNUNET_DHT_connect(sched, test_get->daemon->cfg, 10);
626   /* Insert the data at the first peer */
627   GNUNET_assert(test_get->dht_handle != NULL);
628   outstanding_gets++;
629   test_get->get_handle = GNUNET_DHT_get_start(test_get->dht_handle,
630                                               GNUNET_TIME_relative_get_forever(),
631                                               1,
632                                               &key,
633                                               &get_result_iterator,
634                                               test_get,
635                                               &get_continuation,
636                                               test_get);
637 #if VERBOSE > 1
638   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting get for uid %u from peer %s\n",
639              test_get->uid,
640              test_get->daemon->shortname);
641 #endif
642   test_get->disconnect_task = GNUNET_SCHEDULER_add_delayed(sched, get_timeout, &get_stop_task, test_get);
643   GNUNET_SCHEDULER_add_now (sched, &do_get, test_get->next);
644 }
645
646 /**
647  * Called when the PUT request has been transmitted to the DHT service.
648  * Schedule the GET request for some time in the future.
649  */
650 static void
651 put_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
652 {
653   struct TestPutContext *test_put = cls;
654   outstanding_puts--;
655   puts_completed++;
656
657   GNUNET_SCHEDULER_cancel(sched, test_put->disconnect_task);
658   test_put->disconnect_task = GNUNET_SCHEDULER_add_now(sched, &put_disconnect_task, test_put);
659   if (GNUNET_YES == update_meter(put_meter))
660     {
661       GNUNET_assert(outstanding_puts == 0);
662       GNUNET_SCHEDULER_cancel (sched, die_task);
663       die_task = GNUNET_SCHEDULER_add_delayed (sched, all_get_timeout,
664                                                &end_badly, "from do gets");
665       GNUNET_SCHEDULER_add_delayed(sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 100), &do_get, all_gets);
666       return;
667     }
668 }
669
670 /**
671  * Set up some data, and call API PUT function
672  */
673 static void
674 do_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
675 {
676   struct TestPutContext *test_put = cls;
677   GNUNET_HashCode key; /* Made up key to store data under */
678   char data[test_data_size]; /* Made up data to store */
679   uint32_t rand;
680
681   if (test_put == NULL)
682     return; /* End of list */
683
684   memset(data, test_put->uid, sizeof(data));
685   GNUNET_CRYPTO_hash(data, test_data_size, &key);
686
687   if (outstanding_puts > max_outstanding_puts)
688     {
689       GNUNET_SCHEDULER_add_delayed (sched, put_delay, &do_put, test_put);
690       return;
691     }
692
693 #if VERBOSE > 1
694     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting put for uid %u from peer %s\n",
695                test_put->uid,
696                test_put->daemon->shortname);
697 #endif
698   test_put->dht_handle = GNUNET_DHT_connect(sched, test_put->daemon->cfg, 10);
699
700   GNUNET_assert(test_put->dht_handle != NULL);
701   outstanding_puts++;
702   GNUNET_DHT_put(test_put->dht_handle,
703                  &key,
704                  1,
705                  sizeof(data), data,
706                  GNUNET_TIME_absolute_get_forever(),
707                  GNUNET_TIME_relative_get_forever(),
708                  &put_finished, test_put);
709   test_put->disconnect_task = GNUNET_SCHEDULER_add_delayed(sched, GNUNET_TIME_relative_get_forever(), &put_disconnect_task, test_put);
710   rand = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, 2);
711   GNUNET_SCHEDULER_add_delayed(sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, rand), &do_put, test_put->next);
712 }
713
714
715 /**
716  * Set up some all of the put and get operations we want
717  * to do.  Allocate data structure for each, add to list,
718  * then call actual insert functions.
719  */
720 static void
721 setup_puts_and_gets (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
722 {
723   int i;
724   uint32_t temp_daemon;
725   struct TestPutContext *test_put;
726   struct TestGetContext *test_get;
727   int remember[num_puts][num_peers];
728
729   for (i = 0; i < num_puts; i++)
730     {
731       test_put = GNUNET_malloc(sizeof(struct TestPutContext));
732       test_put->uid = i;
733       temp_daemon = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, num_peers);
734       test_put->daemon = GNUNET_TESTING_daemon_get(pg, temp_daemon);
735       test_put->next = all_puts;
736       all_puts = test_put;
737     }
738
739   for (i = 0; i < num_gets; i++)
740     {
741       test_get = GNUNET_malloc(sizeof(struct TestGetContext));
742       test_get->uid = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, num_puts);
743       temp_daemon = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, num_peers);
744       while (remember[test_get->uid][temp_daemon] == 1)
745         temp_daemon = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, num_peers);
746       test_get->daemon = GNUNET_TESTING_daemon_get(pg, temp_daemon);
747       remember[test_get->uid][temp_daemon] = 1;
748       test_get->next = all_gets;
749       all_gets = test_get;
750     }
751
752   GNUNET_SCHEDULER_cancel (sched, die_task);
753   die_task = GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, num_puts * 2),
754                                            &end_badly, "from do puts");
755   GNUNET_SCHEDULER_add_now (sched, &do_put, all_puts);
756 }
757 /**
758  * This function is called whenever a connection attempt is finished between two of
759  * the started peers (started with GNUNET_TESTING_daemons_start).  The total
760  * number of times this function is called should equal the number returned
761  * from the GNUNET_TESTING_connect_topology call.
762  *
763  * The emsg variable is NULL on success (peers connected), and non-NULL on
764  * failure (peers failed to connect).
765  */
766 void
767 topology_callback (void *cls,
768                    const struct GNUNET_PeerIdentity *first,
769                    const struct GNUNET_PeerIdentity *second,
770                    uint32_t distance,
771                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
772                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
773                    struct GNUNET_TESTING_Daemon *first_daemon,
774                    struct GNUNET_TESTING_Daemon *second_daemon,
775                    const char *emsg)
776 {
777   if (emsg == NULL)
778     {
779       total_connections++;
780 #if VERBOSE > 1
781       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected peer %s to peer %s, distance %u\n",
782                  first_daemon->shortname,
783                  second_daemon->shortname,
784                  distance);
785 #endif
786     }
787 #if VERBOSE
788   else
789     {
790       failed_connections++;
791       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect peer %s to peer %s with error :\n%s\n",
792                   first_daemon->shortname,
793                   second_daemon->shortname, emsg);
794     }
795 #endif
796   GNUNET_assert(peer_connect_meter != NULL);
797   if (GNUNET_YES == update_meter(peer_connect_meter))
798     {
799 #if VERBOSE
800       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
801                   "Created %d total connections, which is our target number!  Starting next phase of testing.\n",
802                   total_connections);
803 #endif
804       if (dhtlog_handle != NULL)
805         dhtlog_handle->update_connections (trialuid, total_connections);
806
807       GNUNET_SCHEDULER_cancel (sched, die_task);
808       die_task = GNUNET_SCHEDULER_add_delayed (sched, DEFAULT_TIMEOUT,
809                                                &end_badly, "from setup puts/gets");
810
811       GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, settle_time), &setup_puts_and_gets, NULL);
812     }
813   else if (total_connections + failed_connections == expected_connections)
814     {
815       GNUNET_SCHEDULER_cancel (sched, die_task);
816       die_task = GNUNET_SCHEDULER_add_now (sched,
817                                            &end_badly, "from topology_callback (too many failed connections)");
818     }
819 }
820
821 static void
822 peers_started_callback (void *cls,
823        const struct GNUNET_PeerIdentity *id,
824        const struct GNUNET_CONFIGURATION_Handle *cfg,
825        struct GNUNET_TESTING_Daemon *d, const char *emsg)
826 {
827   if (emsg != NULL)
828     {
829       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to start daemon with error: `%s'\n",
830                   emsg);
831       return;
832     }
833   GNUNET_assert (id != NULL);
834
835 #if VERBOSE > 1
836   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
837               (num_peers - peers_left) + 1, num_peers);
838 #endif
839
840   peers_left--;
841
842   if (GNUNET_YES == update_meter(peer_start_meter))
843     {
844 #if VERBOSE
845       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
846                   "All %d daemons started, now connecting peers!\n",
847                   num_peers);
848 #endif
849       GNUNET_SCHEDULER_cancel (sched, die_task);
850
851       expected_connections = -1;
852       if ((pg != NULL) && (peers_left == 0))
853         {
854           expected_connections = GNUNET_TESTING_connect_topology (pg, connect_topology, connect_topology_option, connect_topology_option_modifier);
855           peer_connect_meter = create_meter(expected_connections, "Peer connection ", GNUNET_YES);
856 #if VERBOSE
857           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
858                       "Have %d expected connections\n", expected_connections);
859 #endif
860         }
861
862       if (expected_connections == GNUNET_SYSERR)
863         {
864           die_task = GNUNET_SCHEDULER_add_now (sched,
865                                                &end_badly, "from connect topology (bad return)");
866         }
867
868       die_task = GNUNET_SCHEDULER_add_delayed (sched,
869                                                GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, DEFAULT_CONNECT_TIMEOUT * expected_connections),
870                                                &end_badly, "from connect topology (timeout)");
871
872       ok = 0;
873     }
874 }
875
876 static void
877 create_topology ()
878 {
879   peers_left = num_peers; /* Reset counter */
880   if (GNUNET_TESTING_create_topology (pg, topology, blacklist_topology, blacklist_transports) != GNUNET_SYSERR)
881     {
882 #if VERBOSE
883       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
884                   "Topology set up, now starting peers!\n");
885 #endif
886       GNUNET_TESTING_daemons_continue_startup(pg);
887     }
888   else
889     {
890       GNUNET_SCHEDULER_cancel (sched, die_task);
891       die_task = GNUNET_SCHEDULER_add_now (sched,
892                                            &end_badly, "from create topology (bad return)");
893     }
894   GNUNET_SCHEDULER_cancel (sched, die_task);
895   die_task = GNUNET_SCHEDULER_add_delayed (sched,
896                                            GNUNET_TIME_relative_multiply(seconds_per_peer_start, num_peers),
897                                            &end_badly, "from continue startup (timeout)");
898 }
899
900 /**
901  * Callback indicating that the hostkey was created for a peer.
902  *
903  * @param cls NULL
904  * @param id the peer identity
905  * @param d the daemon handle (pretty useless at this point, remove?)
906  * @param emsg non-null on failure
907  */
908 void hostkey_callback (void *cls,
909                        const struct GNUNET_PeerIdentity *id,
910                        struct GNUNET_TESTING_Daemon *d,
911                        const char *emsg)
912 {
913   if (emsg != NULL)
914     {
915       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Hostkey callback received error: %s\n", emsg);
916     }
917
918 #if VERBOSE > 1
919     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
920                 "Hostkey (%d/%d) created for peer `%s'\n",
921                 num_peers - peers_left, num_peers, GNUNET_i2s(id));
922 #endif
923
924     peers_left--;
925     if (GNUNET_YES == update_meter(hostkey_meter))
926       {
927 #if VERBOSE
928         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
929                     "All %d hostkeys created, now creating topology!\n",
930                     num_peers);
931 #endif
932         GNUNET_SCHEDULER_cancel (sched, die_task);
933         /* Set up task in case topology creation doesn't finish
934          * within a reasonable amount of time */
935         die_task = GNUNET_SCHEDULER_add_delayed (sched,
936                                                  DEFAULT_TOPOLOGY_TIMEOUT,
937                                                  &end_badly, "from create_topology");
938         GNUNET_SCHEDULER_add_now(sched, &create_topology, NULL);
939         ok = 0;
940       }
941 }
942
943
944 static void
945 run (void *cls,
946      struct GNUNET_SCHEDULER_Handle *s,
947      char *const *args,
948      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
949 {
950   char * topology_str;
951   char * connect_topology_str;
952   char * blacklist_topology_str;
953   char * connect_topology_option_str;
954   char * connect_topology_option_modifier_string;
955   char *trialmessage;
956   char * topology_percentage_str;
957   float topology_percentage;
958   char * topology_probability_str;
959   float topology_probability;
960   unsigned long long temp_config_number;
961
962   sched = s;
963
964   /* Get path from configuration file */
965   if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_string(cfg, "paths", "servicehome", &test_directory))
966     {
967       ok = 404;
968       return;
969     }
970
971   /**
972    * Get DHT specific testing options.
973    */
974   if ((GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno(cfg, "dht_testing", "mysql_logging"))||
975       (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno(cfg, "dht_testing", "mysql_logging_extended")))
976     {
977       dhtlog_handle = GNUNET_DHTLOG_connect(cfg);
978       if (dhtlog_handle == NULL)
979         {
980           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
981                       "Could not connect to mysql server for logging, will NOT log dht operations!");
982           ok = 3306;
983           return;
984         }
985     }
986
987   if (GNUNET_OK !=
988       GNUNET_CONFIGURATION_get_value_string (cfg, "dht_testing", "comment",
989                                                  &trialmessage))
990     trialmessage = NULL;
991
992   if (GNUNET_OK !=
993           GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "malicious_getters",
994                                                  &malicious_getters))
995     malicious_getters = 0;
996
997   if (GNUNET_OK !=
998           GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "malicious_putters",
999                                                  &malicious_putters))
1000     malicious_putters = 0;
1001
1002   if (GNUNET_OK !=
1003             GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "malicious_droppers",
1004                                                    &malicious_droppers))
1005     malicious_droppers = 0;
1006
1007   if (GNUNET_OK !=
1008       GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "settle_time",
1009                                                  &settle_time))
1010     settle_time = 0;
1011
1012   if (GNUNET_SYSERR ==
1013       GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "num_puts",
1014                                              &num_puts))
1015     num_puts = num_peers;
1016
1017   if (GNUNET_SYSERR ==
1018       GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "num_gets",
1019                                              &num_gets))
1020     num_gets = num_peers;
1021
1022   if (GNUNET_OK ==
1023         GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "get_timeout",
1024                                                &temp_config_number))
1025     get_timeout = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, temp_config_number);
1026   else
1027     get_timeout = DEFAULT_GET_TIMEOUT;
1028
1029   if (GNUNET_OK ==
1030         GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "concurrent_puts",
1031                                                &temp_config_number))
1032     max_outstanding_puts = temp_config_number;
1033   else
1034     max_outstanding_puts = DEFAULT_MAX_OUTSTANDING_PUTS;
1035
1036   if (GNUNET_OK ==
1037         GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "concurrent_gets",
1038                                                &temp_config_number))
1039     max_outstanding_gets = temp_config_number;
1040   else
1041     max_outstanding_gets = DEFAULT_MAX_OUTSTANDING_GETS;
1042
1043   if (GNUNET_OK ==
1044         GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "timeout",
1045                                                &temp_config_number))
1046     all_get_timeout = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, temp_config_number);
1047   else
1048     all_get_timeout.value = get_timeout.value * ((num_gets / max_outstanding_gets) + 1);
1049
1050   if (GNUNET_OK ==
1051         GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "get_delay",
1052                                                &temp_config_number))
1053     get_delay = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, temp_config_number);
1054   else
1055     get_delay = DEFAULT_GET_DELAY;
1056
1057   if (GNUNET_OK ==
1058         GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "put_delay",
1059                                                &temp_config_number))
1060     put_delay = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, temp_config_number);
1061   else
1062     put_delay = DEFAULT_PUT_DELAY;
1063
1064   if (GNUNET_OK ==
1065       GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "peer_start_timeout",
1066                                              &temp_config_number))
1067     seconds_per_peer_start = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, temp_config_number);
1068   else
1069     seconds_per_peer_start = DEFAULT_SECONDS_PER_PEER_START;
1070
1071   if (GNUNET_OK ==
1072         GNUNET_CONFIGURATION_get_value_number (cfg, "dht_testing", "data_size",
1073                                                &temp_config_number))
1074     test_data_size = temp_config_number;
1075   else
1076     test_data_size = DEFAULT_TEST_DATA_SIZE;
1077
1078   /**
1079    * Get testing related options.
1080    */
1081   if ((GNUNET_YES ==
1082       GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "topology",
1083                                             &topology_str)) && (GNUNET_NO == GNUNET_TESTING_topology_get(&topology, topology_str)))
1084     {
1085       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1086                   "Invalid topology `%s' given for section %s option %s\n", topology_str, "TESTING", "TOPOLOGY");
1087       topology = GNUNET_TESTING_TOPOLOGY_CLIQUE; /* Defaults to NONE, so set better default here */
1088     }
1089
1090   if (GNUNET_OK !=
1091       GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "percentage",
1092                                                  &topology_percentage_str))
1093     topology_percentage = 0.5;
1094   else
1095     {
1096       topology_percentage = atof (topology_percentage_str);
1097     }
1098
1099   if (GNUNET_OK !=
1100       GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "probability",
1101                                                  &topology_probability_str))
1102     topology_probability = 0.5;
1103   else
1104     {
1105      topology_probability = atof (topology_probability_str);
1106     }
1107
1108   if ((GNUNET_YES ==
1109       GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "connect_topology",
1110                                             &connect_topology_str)) && (GNUNET_NO == GNUNET_TESTING_topology_get(&connect_topology, connect_topology_str)))
1111     {
1112       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1113                   "Invalid connect topology `%s' given for section %s option %s\n", connect_topology_str, "TESTING", "CONNECT_TOPOLOGY");
1114     }
1115   GNUNET_free_non_null(connect_topology_str);
1116   if ((GNUNET_YES ==
1117       GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "connect_topology_option",
1118                                             &connect_topology_option_str)) && (GNUNET_NO == GNUNET_TESTING_topology_option_get(&connect_topology_option, connect_topology_option_str)))
1119     {
1120       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1121                   "Invalid connect topology option `%s' given for section %s option %s\n", connect_topology_option_str, "TESTING", "CONNECT_TOPOLOGY_OPTION");
1122       connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL; /* Defaults to NONE, set to ALL */
1123     }
1124   GNUNET_free_non_null(connect_topology_option_str);
1125   if (GNUNET_YES ==
1126         GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "connect_topology_option_modifier",
1127                                                &connect_topology_option_modifier_string))
1128     {
1129       if (sscanf(connect_topology_option_modifier_string, "%lf", &connect_topology_option_modifier) != 1)
1130       {
1131         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1132         _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1133         connect_topology_option_modifier_string,
1134         "connect_topology_option_modifier",
1135         "TESTING");
1136       }
1137       GNUNET_free (connect_topology_option_modifier_string);
1138     }
1139
1140   if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "blacklist_transports",
1141                                          &blacklist_transports))
1142     blacklist_transports = NULL;
1143
1144   if ((GNUNET_YES ==
1145       GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "blacklist_topology",
1146                                             &blacklist_topology_str)) &&
1147       (GNUNET_NO == GNUNET_TESTING_topology_get(&blacklist_topology, blacklist_topology_str)))
1148     {
1149       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1150                   "Invalid topology `%s' given for section %s option %s\n", topology_str, "TESTING", "BLACKLIST_TOPOLOGY");
1151     }
1152   GNUNET_free_non_null(topology_str);
1153   GNUNET_free_non_null(blacklist_topology_str);
1154
1155   /* Get number of peers to start from configuration */
1156   if (GNUNET_SYSERR ==
1157       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
1158                                              &num_peers))
1159     {
1160       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1161                   "Number of peers must be specified in section %s option %s\n", topology_str, "TESTING", "NUM_PEERS");
1162     }
1163
1164   /* Set peers_left so we know when all peers started */
1165   peers_left = num_peers;
1166
1167   /* Set up a task to end testing if peer start fails */
1168   die_task = GNUNET_SCHEDULER_add_delayed (sched,
1169                                            GNUNET_TIME_relative_multiply(seconds_per_peer_start, num_peers),
1170                                            &end_badly, "didn't generate all hostkeys within allowed startup time!");
1171
1172   if (dhtlog_handle == NULL)
1173     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1174                 "dhtlog_handle is NULL!");
1175
1176   if ((trialmessage != NULL) && (dhtlog_handle != NULL))
1177     {
1178       dhtlog_handle->insert_trial (&trialuid, peers_left, topology,
1179                                     blacklist_topology, connect_topology,
1180                                     connect_topology_option,
1181                                     connect_topology_option_modifier, topology_percentage,
1182                                     topology_probability, num_puts, num_gets,
1183                                     max_outstanding_gets, settle_time, 1,
1184                                     malicious_getters, malicious_putters,
1185                                     malicious_droppers, trialmessage);
1186     }
1187   else if (dhtlog_handle != NULL)
1188     {
1189       dhtlog_handle->insert_trial (&trialuid, peers_left, topology,
1190                                     blacklist_topology, connect_topology,
1191                                     connect_topology_option,
1192                                     connect_topology_option_modifier, topology_percentage,
1193                                     topology_probability, num_puts, num_gets,
1194                                     max_outstanding_gets, settle_time, 1,
1195                                     malicious_getters, malicious_putters,
1196                                     malicious_droppers, "");
1197     }
1198
1199   hostkey_meter = create_meter(peers_left, "Hostkeys created ", GNUNET_YES);
1200   peer_start_meter = create_meter(peers_left, "Peers started ", GNUNET_YES);
1201
1202   put_meter = create_meter(num_gets, "Puts completed ", GNUNET_YES);
1203   get_meter = create_meter(num_gets, "Gets completed ", GNUNET_YES);
1204   pg = GNUNET_TESTING_daemons_start (sched, cfg,
1205                                      peers_left,
1206                                      GNUNET_TIME_relative_multiply(seconds_per_peer_start, num_peers),
1207                                      &hostkey_callback, NULL,
1208                                      &peers_started_callback, NULL,
1209                                      &topology_callback, NULL,
1210                                      NULL);
1211
1212 }
1213
1214
1215 int
1216 main (int argc, char *argv[])
1217 {
1218   int ret;
1219   struct GNUNET_GETOPT_CommandLineOption options[] = {
1220       GNUNET_GETOPT_OPTION_END
1221     };
1222
1223   ret = GNUNET_PROGRAM_run (argc,
1224                             argv, "gnunet-dht-driver", "nohelp",
1225                             options, &run, &ok);
1226
1227   if (ret != GNUNET_OK)
1228     {
1229       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "`gnunet-dht-driver': Failed with error code %d\n", ret);
1230     }
1231
1232   /**
1233    * Need to remove base directory, subdirectories taken care
1234    * of by the testing framework.
1235    */
1236   if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
1237     {
1238       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to remove testing directory %s\n", test_directory);
1239     }
1240   return ret;
1241 }
1242
1243 /* end of test_dht_twopeer_put_get.c */