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