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