more tweaks to allow testing to actually work
[oweals/gnunet.git] / src / testing / testing_group.c
1 /*
2       This file is part of GNUnet
3       (C) 2008, 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 testing/testing_group.c
23  * @brief convenience API for writing testcases for GNUnet
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_testing_lib.h"
29
30 #define VERBOSE_TESTING GNUNET_YES
31
32 /**
33  * Lowest port used for GNUnet testing.  Should be high enough to not
34  * conflict with other applications running on the hosts but be low
35  * enough to not conflict with client-ports (typically starting around
36  * 32k).
37  */
38 #define LOW_PORT 10000
39
40 /**
41  * Highest port used for GNUnet testing.  Should be low enough to not
42  * conflict with the port range for "local" ports (client apps; see
43  * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
44  */
45 #define HIGH_PORT 32000
46
47 #define MAX_OUTSTANDING_CONNECTIONS 50
48
49 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 180)
50
51 struct PeerConnection
52 {
53   /*
54    * Linked list
55    */
56   struct PeerConnection *next;
57
58   /*
59    * Pointer to daemon handle
60    */
61   struct GNUNET_TESTING_Daemon *daemon;
62
63 };
64
65 /**
66  * Data we keep per peer.
67  */
68 struct PeerData
69 {
70   /**
71    * (Initial) configuration of the host.
72    * (initial because clients could change
73    *  it and we would not know about those
74    *  updates).
75    */
76   struct GNUNET_CONFIGURATION_Handle *cfg;
77
78   /**
79    * Handle for controlling the daemon.
80    */
81   struct GNUNET_TESTING_Daemon *daemon;
82
83   /*
84    * Linked list of peer connections (simply indexes of PeerGroup)
85    * FIXME: Question, store pointer or integer?  Pointer for now...
86    */
87   struct PeerConnection *connected_peers;
88 };
89
90
91 /**
92  * Data we keep per host.
93  */
94 struct HostData
95 {
96   /**
97    * Name of the host.
98    */
99   char *hostname;
100
101   /**
102    * Lowest port that we have not yet used
103    * for GNUnet.
104    */
105   uint16_t minport;
106 };
107
108
109 /**
110  * Handle to a group of GNUnet peers.
111  */
112 struct GNUNET_TESTING_PeerGroup
113 {
114   /**
115    * Our scheduler.
116    */
117   struct GNUNET_SCHEDULER_Handle *sched;
118
119   /**
120    * Configuration template.
121    */
122   const struct GNUNET_CONFIGURATION_Handle *cfg;
123
124   /**
125    * Function to call on each started daemon.
126    */
127   GNUNET_TESTING_NotifyDaemonRunning cb;
128
129   /**
130    * Closure for cb.
131    */
132   void *cb_cls;
133
134   /*
135    * Function to call on each topology connection created
136    */
137   GNUNET_TESTING_NotifyConnection notify_connection;
138
139   /*
140    * Callback for notify_connection
141    */
142   void *notify_connection_cls;
143
144   /**
145    * NULL-terminated array of information about
146    * hosts.
147    */
148   struct HostData *hosts;
149
150   /**
151    * Array of "total" peers.
152    */
153   struct PeerData *peers;
154
155   /**
156    * Number of peers in this group.
157    */
158   unsigned int total;
159
160 };
161
162
163 struct UpdateContext
164 {
165   struct GNUNET_CONFIGURATION_Handle *ret;
166   unsigned int nport;
167   const char *hostname;
168 };
169
170
171 struct ConnectContext
172 {
173   struct GNUNET_TESTING_Daemon *first;
174
175   struct GNUNET_TESTING_Daemon *second;
176
177   struct GNUNET_TESTING_PeerGroup *pg;
178 };
179
180 /**
181  * Number of connects we are waiting on, allows us to rate limit
182  * connect attempts.
183  */
184 static int outstanding_connects;
185
186
187 /**
188  * Function to iterate over options.  Copies
189  * the options to the target configuration,
190  * updating PORT values as needed.
191  *
192  * @param cls closure
193  * @param section name of the section
194  * @param option name of the option
195  * @param value value of the option
196  */
197 static void
198 update_config (void *cls,
199                const char *section, const char *option, const char *value)
200 {
201   struct UpdateContext *ctx = cls;
202   unsigned int ival;
203   char cval[12];
204
205   if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
206     {
207       GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
208       value = cval;
209     }
210
211   if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
212     {
213       value = ctx->hostname;
214     }
215
216   GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
217 }
218
219
220 /**
221  * Create a new configuration using the given configuration
222  * as a template; however, each PORT in the existing cfg
223  * must be renumbered by incrementing "*port".  If we run
224  * out of "*port" numbers, return NULL.
225  *
226  * @param cfg template configuration
227  * @param port port numbers to use, update to reflect
228  *             port numbers that were used
229  * @return new configuration, NULL on error
230  */
231 static struct GNUNET_CONFIGURATION_Handle *
232 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg, uint16_t * port, const char *hostname)
233 {
234   struct UpdateContext uc;
235   uint16_t orig;
236   char *control_host;
237   char *allowed_hosts;
238
239   orig = *port;
240   uc.nport = *port;
241   uc.ret = GNUNET_CONFIGURATION_create ();
242   uc.hostname = hostname;
243
244   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
245   if (uc.nport >= HIGH_PORT)
246     {
247       *port = orig;
248       GNUNET_CONFIGURATION_destroy (uc.ret);
249       return NULL;
250     }
251
252   if (GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "control_host", &control_host) == GNUNET_OK)
253     {
254       GNUNET_asprintf(&allowed_hosts, "%s 127.0.0.1;", control_host);
255       GNUNET_CONFIGURATION_set_value_string(uc.ret, "core", "ACCEPT_FROM", allowed_hosts);
256       GNUNET_free_non_null(control_host);
257       GNUNET_free(allowed_hosts);
258     }
259
260
261   /* arm needs to know to allow connections from the host on which it is running,
262    * otherwise gnunet-arm is unable to connect to it in some instances */
263   if (hostname != NULL)
264     {
265       GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", hostname);
266       GNUNET_CONFIGURATION_set_value_string(uc.ret, "arm", "ACCEPT_FROM", allowed_hosts);
267       GNUNET_free(allowed_hosts);
268     }
269
270   *port = (uint16_t) uc.nport;
271   return uc.ret;
272 }
273
274 /*
275  * Add entries to the peers connected list
276  *
277  * @param pg the peer group we are working with
278  * @param first index of the first peer
279  * @param second index of the second peer
280  *
281  * @return the number of connections added (can be 0, 1 or 2)
282  *
283  * FIXME: add both, or only add one?
284  *      - if both are added, then we have to keep track
285  *        when connecting so we don't double connect
286  *      - if only one is added, we need to iterate over
287  *        both lists to find out if connection already exists
288  *      - having both allows the whitelisting/friend file
289  *        creation to be easier
290  *
291  *      -- For now, add both, we have to iterate over each to
292  *         check for duplicates anyways, so we'll take the performance
293  *         hit assuming we don't have __too__ many connections
294  *
295  */
296 static int
297 add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
298 {
299   int added;
300   struct PeerConnection *first_iter;
301   struct PeerConnection *second_iter;
302   int add_first;
303   int add_second;
304   struct PeerConnection *new_first;
305   struct PeerConnection *new_second;
306
307   first_iter = pg->peers[first].connected_peers;
308   add_first = GNUNET_YES;
309   while (first_iter != NULL)
310     {
311       if (first_iter->daemon == pg->peers[second].daemon)
312         add_first = GNUNET_NO;
313       first_iter = first_iter->next;
314     }
315
316   second_iter = pg->peers[second].connected_peers;
317   add_second = GNUNET_YES;
318   while (second_iter != NULL)
319     {
320       if (second_iter->daemon == pg->peers[first].daemon)
321         add_second = GNUNET_NO;
322       second_iter = second_iter->next;
323     }
324
325   added = 0;
326   if (add_first)
327     {
328       new_first = GNUNET_malloc(sizeof(struct PeerConnection));
329       new_first->daemon = pg->peers[second].daemon;
330       new_first->next = pg->peers[first].connected_peers;
331       pg->peers[first].connected_peers = new_first;
332       added++;
333     }
334
335   if (add_second)
336     {
337       new_second = GNUNET_malloc(sizeof(struct PeerConnection));
338       new_second->daemon = pg->peers[first].daemon;
339       new_second->next = pg->peers[second].connected_peers;
340       pg->peers[second].connected_peers = new_second;
341       added++;
342     }
343
344   return added;
345 }
346
347 int
348 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg)
349 {
350   unsigned int i, j;
351   int nodeToConnect;
352   unsigned int natLog;
353   unsigned int randomPeer;
354   double random, logNModifier, percentage;
355   unsigned int smallWorldConnections;
356   int connsPerPeer;
357   char *p_string;
358   int max;
359   int min;
360   unsigned int useAnd;
361   int connect_attempts;
362
363   logNModifier = 0.5; /* FIXME: default value? */
364   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
365                                                          "TESTING",
366                                                          "LOGNMODIFIER",
367                                                          &p_string))
368     {
369       if (sscanf(p_string, "%lf", &logNModifier) != 1)
370         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
371                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
372                     p_string,
373                     "LOGNMODIFIER",
374                     "TESTING");
375       GNUNET_free (p_string);
376     }
377   percentage = 0.5; /* FIXME: default percentage? */
378   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
379                                                          "TESTING",
380                                                          "PERCENTAGE",
381                                                          &p_string))
382     {
383       if (sscanf(p_string, "%lf", &percentage) != 1)
384         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
385                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
386                     p_string,
387                     "PERCENTAGE",
388                     "TESTING");
389       GNUNET_free (p_string);
390     }
391   natLog = log (pg->total);
392   connsPerPeer = ceil (natLog * logNModifier);
393
394   if (connsPerPeer % 2 == 1)
395     connsPerPeer += 1;
396
397   smallWorldConnections = 0;
398   connect_attempts = 0;
399   for (i = 0; i < pg->total; i++)
400     {
401       useAnd = 0;
402       max = i + connsPerPeer / 2;
403       min = i - connsPerPeer / 2;
404
405       if (max > pg->total - 1)
406         {
407           max = max - pg->total;
408           useAnd = 1;
409         }
410
411       if (min < 0)
412         {
413           min = pg->total - 1 + min;
414           useAnd = 1;
415         }
416
417       for (j = 0; j < connsPerPeer / 2; j++)
418         {
419           random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
420                                                       (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
421           if (random < percentage)
422             {
423               /* Connect to uniformly selected random peer */
424               randomPeer =
425                 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
426                                    pg->total);
427               while ((((randomPeer < max) && (randomPeer > min))
428                       && (useAnd == 0)) || (((randomPeer > min)
429                                              || (randomPeer < max))
430                                             && (useAnd == 1)))
431                 {
432                   randomPeer =
433                       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
434                                                          pg->total);
435                 }
436               smallWorldConnections +=
437                 add_connections (pg, i, randomPeer);
438             }
439           else
440             {
441               nodeToConnect = i + j + 1;
442               if (nodeToConnect > pg->total - 1)
443                 {
444                   nodeToConnect = nodeToConnect - pg->total;
445                 }
446               connect_attempts +=
447                 add_connections (pg, i, nodeToConnect);
448             }
449         }
450
451     }
452
453   connect_attempts += smallWorldConnections;
454
455   return connect_attempts;
456 }
457
458
459 static int
460 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg)
461 {
462   unsigned int outer_count, inner_count;
463   unsigned int cutoff;
464   int connect_attempts;
465   double nat_percentage;
466   char *p_string;
467
468   nat_percentage = 0.6; /* FIXME: default percentage? */
469   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
470                                                          "TESTING",
471                                                          "NATPERCENTAGE",
472                                                          &p_string))
473     {
474       if (sscanf(p_string, "%lf", &nat_percentage) != 1)
475         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
476                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
477                     p_string,
478                     "NATPERCENTAGE",
479                     "TESTING");
480       GNUNET_free (p_string);
481     }
482
483
484
485   cutoff = (unsigned int) (nat_percentage * pg->total);
486
487   connect_attempts = 0;
488
489   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
490     {
491       for (inner_count = outer_count + 1; inner_count < pg->total;
492            inner_count++)
493         {
494           if ((outer_count > cutoff) || (inner_count > cutoff))
495             {
496 #if VERBOSE_TESTING
497               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
498                           "Connecting peer %d to peer %d\n",
499                           outer_count, inner_count);
500 #endif
501               connect_attempts += add_connections(pg, outer_count, inner_count);
502             }
503         }
504     }
505
506   return connect_attempts;
507
508 }
509
510
511
512 static int
513 create_small_world (struct GNUNET_TESTING_PeerGroup *pg)
514 {
515   unsigned int i, j, k;
516   unsigned int square;
517   unsigned int rows;
518   unsigned int cols;
519   unsigned int toggle = 1;
520   unsigned int nodeToConnect;
521   unsigned int natLog;
522   unsigned int node1Row;
523   unsigned int node1Col;
524   unsigned int node2Row;
525   unsigned int node2Col;
526   unsigned int distance;
527   double probability, random, percentage;
528   unsigned int smallWorldConnections;
529   char *p_string;
530   int connect_attempts;
531   square = floor (sqrt (pg->total));
532   rows = square;
533   cols = square;
534
535   percentage = 0.5; /* FIXME: default percentage? */
536   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
537                                                          "TESTING",
538                                                          "PERCENTAGE",
539                                                          &p_string))
540     {
541       if (sscanf(p_string, "%lf", &percentage) != 1)
542         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
543                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
544                     p_string,
545                     "PERCENTAGE",
546                     "TESTING");
547       GNUNET_free (p_string);
548     }
549   probability = 0.5; /* FIXME: default percentage? */
550   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
551                                                          "TESTING",
552                                                          "PROBABILITY",
553                                                          &p_string))
554     {
555       if (sscanf(p_string, "%lf", &probability) != 1)
556         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
557                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
558                     p_string,
559                     "PROBABILITY",
560                     "TESTING");
561       GNUNET_free (p_string);
562     }
563   if (square * square != pg->total)
564     {
565       while (rows * cols < pg->total)
566         {
567           if (toggle % 2 == 0)
568             rows++;
569           else
570             cols++;
571
572           toggle++;
573         }
574     }
575 #if VERBOSE_TESTING
576       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
577                   _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
578                   rows, cols);
579 #endif
580
581   connect_attempts = 0;
582   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
583    * to the node to its right and above.  Once this is over, we'll have our torus!
584    * Special case for the last node (if the rows and columns are not equal), connect
585    * to the first in the row to maintain topology.
586    */
587   for (i = 0; i < pg->total; i++)
588     {
589       /* First connect to the node to the right */
590       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
591         nodeToConnect = i + 1;
592       else if (i + 1 == pg->total)
593         nodeToConnect = rows * cols - cols;
594       else
595         nodeToConnect = i - cols + 1;
596
597       connect_attempts += add_connections (pg, i, nodeToConnect);
598
599       if (i < cols)
600         nodeToConnect = (rows * cols) - cols + i;
601       else
602         nodeToConnect = i - cols;
603
604       if (nodeToConnect < pg->total)
605         connect_attempts += add_connections (pg, i, nodeToConnect);
606     }
607   natLog = log (pg->total);
608 #if VERBOSE_TESTING
609   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610               _("natural log of %d is %d, will run %d iterations\n"),
611              pg->total, natLog, (int) (natLog * percentage));
612   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Total connections added thus far: %u!\n"), connect_attempts);
613 #endif
614   smallWorldConnections = 0;
615   for (i = 0; i < (int) (natLog * percentage); i++)
616     {
617       for (j = 0; j < pg->total; j++)
618         {
619           /* Determine the row and column of node at position j on the 2d torus */
620           node1Row = j / cols;
621           node1Col = j - (node1Row * cols);
622           for (k = 0; k < pg->total; k++)
623             {
624               /* Determine the row and column of node at position k on the 2d torus */
625               node2Row = k / cols;
626               node2Col = k - (node2Row * cols);
627               /* Simple Cartesian distance */
628               distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
629               if (distance > 1)
630                 {
631                   /* Calculate probability as 1 over the square of the distance */
632                   probability = 1.0 / (distance * distance);
633                   /* Choose a random value between 0 and 1 */
634                   random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
635                                                               (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
636                   /* If random < probability, then connect the two nodes */
637                   if (random < probability)
638                     smallWorldConnections += add_connections (pg, j, k);
639
640                 }
641             }
642         }
643     }
644   connect_attempts += smallWorldConnections;
645 #if VERBOSE_TESTING
646           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
647                       _("Total connections added for small world: %d!\n"),
648                       smallWorldConnections);
649 #endif
650   return connect_attempts;
651 }
652
653
654
655 static int
656 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg)
657 {
658   double temp_rand;
659   unsigned int outer_count;
660   unsigned int inner_count;
661   int connect_attempts;
662   double probability;
663   char *p_string;
664
665   probability = 0.5; /* FIXME: default percentage? */
666   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
667                                                          "TESTING",
668                                                          "PROBABILITY",
669                                                          &p_string))
670     {
671       if (sscanf(p_string, "%lf", &probability) != 1)
672         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
673                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
674                     p_string,
675                     "PROBABILITY",
676                     "TESTING");
677       GNUNET_free (p_string);
678     }
679   connect_attempts = 0;
680   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
681     {
682       for (inner_count = outer_count + 1; inner_count < pg->total;
683            inner_count++)
684         {
685           temp_rand = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
686                                                          (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
687 #if VERBOSE_TESTING
688           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
689                       _("rand is %f probability is %f\n"), temp_rand,
690                       probability);
691 #endif
692           if (temp_rand < probability)
693             {
694               connect_attempts += add_connections (pg, outer_count, inner_count);
695             }
696         }
697     }
698
699   return connect_attempts;
700 }
701
702 static int
703 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg)
704 {
705   unsigned int i;
706   unsigned int square;
707   unsigned int rows;
708   unsigned int cols;
709   unsigned int toggle = 1;
710   unsigned int nodeToConnect;
711   int connect_attempts;
712
713   connect_attempts = 0;
714
715   square = floor (sqrt (pg->total));
716   rows = square;
717   cols = square;
718
719   if (square * square != pg->total)
720     {
721       while (rows * cols < pg->total)
722         {
723           if (toggle % 2 == 0)
724             rows++;
725           else
726             cols++;
727
728           toggle++;
729         }
730     }
731 #if VERBOSE_TESTING
732       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
733                   _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
734                   rows, cols);
735 #endif
736   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
737    * to the node to its right and above.  Once this is over, we'll have our torus!
738    * Special case for the last node (if the rows and columns are not equal), connect
739    * to the first in the row to maintain topology.
740    */
741   for (i = 0; i < pg->total; i++)
742     {
743       /* First connect to the node to the right */
744       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
745         nodeToConnect = i + 1;
746       else if (i + 1 == pg->total)
747         nodeToConnect = rows * cols - cols;
748       else
749         nodeToConnect = i - cols + 1;
750 #if VERBOSE_TESTING
751           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
752                       "Connecting peer %d to peer %d\n",
753                       i, nodeToConnect);
754 #endif
755       connect_attempts += add_connections(pg, i, nodeToConnect);
756
757       /* Second connect to the node immediately above */
758       if (i < cols)
759         nodeToConnect = (rows * cols) - cols + i;
760       else
761         nodeToConnect = i - cols;
762
763       if (nodeToConnect < pg->total)
764         {
765 #if VERBOSE_TESTING
766           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
767                       "Connecting peer %d to peer %d\n",
768                       i, nodeToConnect);
769 #endif
770           connect_attempts += add_connections(pg, i, nodeToConnect);
771         }
772
773     }
774
775   return connect_attempts;
776 }
777
778
779
780 static int
781 create_clique (struct GNUNET_TESTING_PeerGroup *pg)
782 {
783   unsigned int outer_count;
784   unsigned int inner_count;
785   int connect_attempts;
786
787   connect_attempts = 0;
788
789   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
790     {
791       for (inner_count = outer_count + 1; inner_count < pg->total;
792            inner_count++)
793         {
794 #if VERBOSE_TESTING
795           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
796                       "Connecting peer %d to peer %d\n",
797                       outer_count, inner_count);
798 #endif
799           connect_attempts += add_connections(pg, outer_count, inner_count);
800         }
801     }
802
803   return connect_attempts;
804 }
805
806
807 static int
808 create_ring (struct GNUNET_TESTING_PeerGroup *pg)
809 {
810   unsigned int count;
811   int connect_attempts;
812
813   connect_attempts = 0;
814
815   /* Connect each peer to the next highest numbered peer */
816   for (count = 0; count < pg->total - 1; count++)
817     {
818 #if VERBOSE_TESTING
819           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
820                       "Connecting peer %d to peer %d\n",
821                       count, count + 1);
822 #endif
823       connect_attempts += add_connections(pg, count, count + 1);
824     }
825
826   /* Connect the last peer to the first peer */
827   connect_attempts += add_connections(pg, pg->total - 1, 0);
828
829   return connect_attempts;
830 }
831
832
833 /*
834  * Create the friend files based on the PeerConnection's
835  * of each peer in the peer group, and copy the files
836  * to the appropriate place
837  *
838  * @param pg the peer group we are dealing with
839  */
840 static int
841 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
842 {
843   FILE *temp_friend_handle;
844   unsigned int pg_iter;
845   struct PeerConnection *connection_iter;
846   struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
847   char *temp_service_path;
848   pid_t *pidarr;
849   char *arg;
850   struct GNUNET_PeerIdentity *temppeer;
851   char * mytemp;
852   enum GNUNET_OS_ProcessStatusType type;
853   unsigned long return_code;
854   int count;
855   int ret;
856   int max_wait = 10;
857
858   pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
859   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
860     {
861       mytemp = GNUNET_DISK_mktemp("friends");
862       temp_friend_handle = fopen (mytemp, "wt");
863       connection_iter = pg->peers[pg_iter].connected_peers;
864       while (connection_iter != NULL)
865         {
866           temppeer = &connection_iter->daemon->id;
867           GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
868           fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
869           connection_iter = connection_iter->next;
870         }
871
872       fclose(temp_friend_handle);
873
874       if (GNUNET_OK !=
875           GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
876         {
877           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
878                       _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
879                       "SERVICEHOME",
880                       "PATHS");
881           if (UNLINK (mytemp) != 0)
882             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
883           GNUNET_free (mytemp);
884           break;
885         }
886
887       if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
888         {
889           GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
890           pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
891                                          "mv", mytemp, arg, NULL);
892 #if VERBOSE_TESTING
893           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
894                       _("Copying file with command cp %s %s\n"), mytemp, arg);
895 #endif
896
897           GNUNET_free(arg);
898         }
899       else /* Remote, scp the file to the correct place */
900         {
901           if (NULL != pg->peers[pg_iter].daemon->username)
902             GNUNET_asprintf (&arg, "%s@%s:%s/friends", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
903           else
904             GNUNET_asprintf (&arg, "%s:%s/friends", pg->peers[pg_iter].daemon->hostname, temp_service_path);
905           pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
906                                          "scp", mytemp, arg, NULL);
907
908 #if VERBOSE_TESTING
909           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
910                       _("Copying file with command scp %s %s\n"), mytemp, arg);
911 #endif
912           GNUNET_free(arg);
913         }
914       GNUNET_free (temp_service_path);
915       GNUNET_free (mytemp);
916     }
917
918   count = 0;
919   ret = GNUNET_SYSERR;
920   while ((count < max_wait) && (ret != GNUNET_OK))
921     {
922       ret = GNUNET_OK;
923       for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
924         {
925 #if VERBOSE_TESTING
926           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
927                       _("Checking copy status of file %d\n"), pg_iter);
928 #endif
929           if (pidarr[pg_iter] != 0) /* Check for already completed! */
930             {
931               if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
932                 {
933                   ret = GNUNET_SYSERR;
934                 }
935               else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
936                 {
937                   ret = GNUNET_SYSERR;
938                 }
939               else
940                 {
941                   pidarr[pg_iter] = 0;
942 #if VERBOSE_TESTING
943             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
944                       _("File %d copied\n"), pg_iter);
945 #endif
946                 }
947             }
948         }
949       count++;
950       if (ret == GNUNET_SYSERR)
951         {
952           sleep(1);
953         }
954     }
955
956   GNUNET_free(pidarr);
957   return ret;
958 }
959
960 /**
961  * Internal notification of a connection, kept so that we can ensure some connections
962  * happen instead of flooding all testing daemons with requests to connect.
963  */
964 static void internal_connect_notify (void *cls,
965                                      const struct GNUNET_PeerIdentity *first,
966                                      const struct GNUNET_PeerIdentity *second,
967                                      const struct GNUNET_CONFIGURATION_Handle *first_cfg,
968                                      const struct GNUNET_CONFIGURATION_Handle *second_cfg,
969                                      struct GNUNET_TESTING_Daemon *first_daemon,
970                                      struct GNUNET_TESTING_Daemon *second_daemon,
971                                      const char *emsg)
972 {
973   struct GNUNET_TESTING_PeerGroup *pg = cls;
974   outstanding_connects--;
975
976   pg->notify_connection(pg->notify_connection_cls, first, second, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
977
978 }
979
980 static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
981 {
982   struct ConnectContext *connect_context = cls;
983
984   if (outstanding_connects > MAX_OUTSTANDING_CONNECTIONS)
985     {
986 #if VERBOSE_TESTING
987           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
988                       _("Delaying connect, we have too many outstanding connections!\n"));
989 #endif
990       GNUNET_SCHEDULER_add_delayed(connect_context->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 3), &schedule_connect, connect_context);
991     }
992   else
993     {
994 #if VERBOSE_TESTING
995           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
996                       _("Creating connection, outstanding_connections is %d\n"), outstanding_connects);
997 #endif
998       outstanding_connects++;
999       GNUNET_TESTING_daemons_connect (connect_context->first,
1000                                       connect_context->second,
1001                                       CONNECT_TIMEOUT,
1002                                       &internal_connect_notify,
1003                                       connect_context->pg);
1004       GNUNET_free(connect_context);
1005     }
1006 }
1007
1008 /*
1009  * Connect the topology as specified by the PeerConnection's
1010  * of each peer in the peer group
1011  *
1012  * @param pg the peer group we are dealing with
1013  */
1014 static void
1015 connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
1016 {
1017   unsigned int pg_iter;
1018   struct PeerConnection *connection_iter;
1019   struct ConnectContext *connect_context;
1020
1021   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1022     {
1023       connection_iter = pg->peers[pg_iter].connected_peers;
1024       while (connection_iter != NULL)
1025         {
1026           connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
1027           connect_context->pg = pg;
1028           connect_context->first = pg->peers[pg_iter].daemon;
1029           connect_context->second = connection_iter->daemon;
1030
1031           GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context);
1032           /*GNUNET_TESTING_daemons_connect (pg->peers[pg_iter].daemon,
1033                                           connection_iter->daemon,
1034                                           CONNECT_TIMEOUT,
1035                                           pg->notify_connection,
1036                                           pg->notify_connection_cls);*/
1037           connection_iter = connection_iter->next;
1038
1039           /*if (outstanding_connects > MAX_OUTSTANDING_CONNECTS)
1040             {
1041 #if VERBOSE_TESTING
1042               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1043                           _("Sleeping to give peers a chance to connect!\n"));
1044 #endif
1045               sleep(2);
1046             } */
1047         }
1048     }
1049 }
1050
1051
1052 /*
1053  * Takes a peer group and attempts to create a topology based on the
1054  * one specified in the configuration file.  Returns the number of connections
1055  * that will attempt to be created, but this will happen asynchronously(?) so
1056  * the caller will have to keep track (via the callback) of whether or not
1057  * the connection actually happened.
1058  *
1059  * @param pg the peer group struct representing the running peers
1060  *
1061  * @return the number of connections should be created by the topology, so the
1062  * caller knows how many to wait for (if it so chooses)
1063  *
1064  */
1065 int
1066 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg)
1067 {
1068   unsigned long long topology_num;
1069   int ret;
1070   int num_connections;
1071
1072   GNUNET_assert (pg->notify_connection != NULL);
1073   ret = GNUNET_OK;
1074   if (GNUNET_YES ==
1075       GNUNET_CONFIGURATION_get_value_number (pg->cfg, "testing", "topology",
1076                                              &topology_num))
1077     {
1078       switch (topology_num)
1079         {
1080         case GNUNET_TESTING_TOPOLOGY_CLIQUE:
1081 #if VERBOSE_TESTING
1082           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1083                       _("Creating clique topology (may take a bit!)\n"));
1084 #endif
1085           num_connections = create_clique (pg);
1086           break;
1087         case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
1088 #if VERBOSE_TESTING
1089           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1090                       _("Creating small world (ring) topology (may take a bit!)\n"));
1091 #endif
1092           num_connections = create_small_world_ring (pg);
1093           break;
1094         case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
1095 #if VERBOSE_TESTING
1096           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1097                       _("Creating small world (2d-torus) topology (may take a bit!)\n"));
1098 #endif
1099           num_connections = create_small_world (pg);
1100           break;
1101         case GNUNET_TESTING_TOPOLOGY_RING:
1102 #if VERBOSE_TESTING
1103           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1104                       _("Creating ring topology (may take a bit!)\n"));
1105 #endif
1106           num_connections = create_ring (pg);
1107           break;
1108         case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
1109 #if VERBOSE_TESTING
1110           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1111                       _("Creating 2d torus topology (may take a bit!)\n"));
1112 #endif
1113           num_connections = create_2d_torus (pg);
1114           break;
1115         case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
1116 #if VERBOSE_TESTING
1117           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1118                       _("Creating Erdos-Renyi topology (may take a bit!)\n"));
1119 #endif
1120           num_connections = create_erdos_renyi (pg);
1121           break;
1122         case GNUNET_TESTING_TOPOLOGY_INTERNAT:
1123 #if VERBOSE_TESTING
1124           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1125                       _("Creating InterNAT topology (may take a bit!)\n"));
1126 #endif
1127           num_connections = create_nated_internet (pg);
1128           break;
1129         case GNUNET_TESTING_TOPOLOGY_NONE:
1130           num_connections = 0;
1131           break;
1132         default:
1133           num_connections = 0;
1134           break;
1135         }
1136       if (num_connections < 1)
1137         return GNUNET_SYSERR;
1138
1139       if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
1140         ret = create_and_copy_friend_files(pg);
1141       if (ret == GNUNET_OK)
1142         connect_topology(pg);
1143       else
1144         {
1145 #if VERBOSE_TESTING
1146           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1147                       _("Failed during friend file copying!\n"));
1148 #endif
1149           return GNUNET_SYSERR;
1150         }
1151     }
1152   else
1153     {
1154       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1155                   _("No topology specified, was one intended?\n"));
1156       return GNUNET_SYSERR;
1157     }
1158
1159   return num_connections;
1160 }
1161
1162 /**
1163  * Start count gnunetd processes with the same set of transports and
1164  * applications.  The port numbers (any option called "PORT") will be
1165  * adjusted to ensure that no two peers running on the same system
1166  * have the same port(s) in their respective configurations.
1167  *
1168  * @param sched scheduler to use
1169  * @param cfg configuration template to use
1170  * @param total number of daemons to start
1171  * @param cb function to call on each daemon that was started
1172  * @param cb_cls closure for cb
1173  * @param connect_callback function to call each time two hosts are connected
1174  * @param connect_callback_cls closure for connect_callback
1175  * @param hostnames space-separated list of hostnames to use; can be NULL (to run
1176  *        everything on localhost).
1177  * @return NULL on error, otherwise handle to control peer group
1178  */
1179 struct GNUNET_TESTING_PeerGroup *
1180 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
1181                               const struct GNUNET_CONFIGURATION_Handle *cfg,
1182                               unsigned int total,
1183                               GNUNET_TESTING_NotifyDaemonRunning cb,
1184                               void *cb_cls,
1185                               GNUNET_TESTING_NotifyConnection
1186                               connect_callback, void *connect_callback_cls,
1187                               const char *hostnames)
1188 {
1189   struct GNUNET_TESTING_PeerGroup *pg;
1190   const char *rpos;
1191   char *pos;
1192   char *start;
1193   const char *hostname;
1194   char *baseservicehome;
1195   char *newservicehome;
1196   char *tmpdir;
1197   struct GNUNET_CONFIGURATION_Handle *pcfg;
1198   unsigned int off;
1199   unsigned int hostcnt;
1200   uint16_t minport;
1201
1202   if (0 == total)
1203     {
1204       GNUNET_break (0);
1205       return NULL;
1206     }
1207
1208   pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
1209   pg->sched = sched;
1210   pg->cfg = cfg;
1211   pg->cb = cb;
1212   pg->cb_cls = cb_cls;
1213   pg->notify_connection = connect_callback;
1214   pg->notify_connection_cls = connect_callback_cls;
1215   pg->total = total;
1216   pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
1217   if (NULL != hostnames)
1218     {
1219       off = 2;
1220       /* skip leading spaces */
1221       while ((0 != *hostnames) && (isspace (*hostnames)))
1222         hostnames++;
1223       rpos = hostnames;
1224       while ('\0' != *rpos)
1225         {
1226           if (isspace (*rpos))
1227             off++;
1228           rpos++;
1229         }
1230       pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
1231       off = 0;
1232       start = GNUNET_strdup (hostnames);
1233       pos = start;
1234       while ('\0' != *pos)
1235         {
1236           if (isspace (*pos))
1237             {
1238               *pos = '\0';
1239               if (strlen (start) > 0)
1240                 {
1241                   pg->hosts[off].minport = LOW_PORT;
1242                   pg->hosts[off++].hostname = start;
1243                 }
1244               start = pos + 1;
1245             }
1246           pos++;
1247         }
1248       if (strlen (start) > 0)
1249         {
1250           pg->hosts[off].minport = LOW_PORT;
1251           pg->hosts[off++].hostname = start;
1252         }
1253       if (off == 0)
1254         {
1255           GNUNET_free (start);
1256           GNUNET_free (pg->hosts);
1257           pg->hosts = NULL;
1258         }
1259       hostcnt = off;
1260       minport = 0;              /* make gcc happy */
1261     }
1262   else
1263     {
1264       hostcnt = 0;
1265       minport = LOW_PORT;
1266     }
1267   for (off = 0; off < total; off++)
1268     {
1269       if (hostcnt > 0)
1270         {
1271           hostname = pg->hosts[off % hostcnt].hostname;
1272           pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport, hostname);
1273         }
1274       else
1275         {
1276           hostname = NULL;
1277           pcfg = make_config (cfg, &minport, hostname);
1278         }
1279
1280       if (NULL == pcfg)
1281         {
1282           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1283                       _
1284                       ("Could not create configuration for peer number %u on `%s'!\n"),
1285                       off, hostname == NULL ? "localhost" : hostname);
1286           continue;
1287         }
1288
1289       if (GNUNET_YES ==
1290           GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
1291                                                  &baseservicehome))
1292         {
1293           GNUNET_asprintf (&newservicehome,
1294                            "%s/%d/", baseservicehome, off);
1295           GNUNET_free (baseservicehome);
1296         }
1297       else
1298         {
1299           tmpdir = getenv ("TMPDIR");
1300           tmpdir = tmpdir ? tmpdir : "/tmp";
1301           GNUNET_asprintf (&newservicehome,
1302                            "%s/%s/%d/",
1303                            tmpdir,
1304                            "gnunet-testing-test-test", off);
1305         }
1306       GNUNET_CONFIGURATION_set_value_string (pcfg,
1307                                              "PATHS",
1308                                              "SERVICEHOME", newservicehome);
1309       GNUNET_free (newservicehome);
1310       pg->peers[off].cfg = pcfg;
1311       pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
1312                                                            pcfg,
1313                                                            hostname,
1314                                                            cb, cb_cls);
1315       if (NULL == pg->peers[off].daemon)
1316         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1317                     _("Could not start peer number %u!\n"), off);
1318     }
1319   return pg;
1320 }
1321
1322 /*
1323  * Get a daemon by number, so callers don't have to do nasty
1324  * offsetting operation.
1325  */
1326 struct GNUNET_TESTING_Daemon *
1327 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int position)
1328 {
1329   if (position < pg->total)
1330     return pg->peers[position].daemon;
1331   else
1332     return NULL;
1333 }
1334
1335 /**
1336  * Shutdown all peers started in the given group.
1337  *
1338  * @param pg handle to the peer group
1339  */
1340 void
1341 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
1342 {
1343   unsigned int off;
1344   struct PeerConnection *pos;
1345   struct PeerConnection *next;
1346
1347   for (off = 0; off < pg->total; off++)
1348     {
1349       /* FIXME: should we wait for our
1350          continuations to be called here? This
1351          would require us to take a continuation
1352          as well... */
1353
1354       if (NULL != pg->peers[off].daemon)
1355         GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL);
1356       if (NULL != pg->peers[off].cfg)
1357         GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
1358
1359       pos = pg->peers[off].connected_peers;
1360       while (pos != NULL)
1361         {
1362           next = pos->next;
1363           GNUNET_free(pos);
1364           pos = next;
1365         }
1366
1367     }
1368   GNUNET_free (pg->peers);
1369   if (NULL != pg->hosts)
1370     {
1371       GNUNET_free (pg->hosts[0].hostname);
1372       GNUNET_free (pg->hosts);
1373     }
1374   GNUNET_free (pg);
1375 }
1376
1377
1378 /* end of testing_group.c */