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