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