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