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