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