e27a4120dd0c086d25b0743149b6937741ba157b
[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   struct GNUNET_TIME_Absolute time;
314
315   GNUNET_CONFIGURATION_get_value_string(pg->cfg, "TESTING", "LOGNMODIFIER", &p_string);
316   if (p_string != NULL)
317     logNModifier = atof(p_string);
318   else
319     logNModifier = 0.5; /* FIXME: default modifier? */
320
321   GNUNET_free_non_null(p_string);
322
323   GNUNET_CONFIGURATION_get_value_string(pg->cfg, "TESTING", "PERCENTAGE", &p_string);
324   if (p_string != NULL)
325     percentage = atof(p_string);
326   else
327     percentage = 0.5; /* FIXME: default percentage? */
328
329   GNUNET_free_non_null(p_string);
330
331   natLog = log (pg->total);
332   connsPerPeer = ceil (natLog * logNModifier);
333
334   if (connsPerPeer % 2 == 1)
335     connsPerPeer += 1;
336
337   time = GNUNET_TIME_absolute_get ();
338   srand ((unsigned int) time.value);
339   smallWorldConnections = 0;
340   connect_attempts = 0;
341   for (i = 0; i < pg->total; i++)
342     {
343       useAnd = 0;
344       max = i + connsPerPeer / 2;
345       min = i - connsPerPeer / 2;
346
347       if (max > pg->total - 1)
348         {
349           max = max - pg->total;
350           useAnd = 1;
351         }
352
353       if (min < 0)
354         {
355           min = pg->total - 1 + min;
356           useAnd = 1;
357         }
358
359       for (j = 0; j < connsPerPeer / 2; j++)
360         {
361           random = ((double) rand () / RAND_MAX);
362           if (random < percentage)
363             {
364               /* Connect to uniformly selected random peer */
365               randomPeer =
366                 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
367                                    pg->total);
368               while ((((randomPeer < max) && (randomPeer > min))
369                       && (useAnd == 0)) || (((randomPeer > min)
370                                              || (randomPeer < max))
371                                             && (useAnd == 1)))
372                 {
373                   randomPeer =
374                       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
375                                                          pg->total);
376                 }
377               smallWorldConnections +=
378                 add_connections (pg, i, randomPeer);
379             }
380           else
381             {
382               nodeToConnect = i + j + 1;
383               if (nodeToConnect > pg->total - 1)
384                 {
385                   nodeToConnect = nodeToConnect - pg->total;
386                 }
387               connect_attempts +=
388                 add_connections (pg, i, nodeToConnect);
389             }
390         }
391
392     }
393
394   connect_attempts += smallWorldConnections;
395
396   return GNUNET_OK;
397 }
398
399
400 static int
401 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg)
402 {
403   unsigned int outer_count, inner_count;
404   unsigned int cutoff;
405   int connect_attempts;
406   double nat_percentage;
407   char *p_string;
408
409   GNUNET_CONFIGURATION_get_value_string(pg->cfg, "TESTING", "NATPERCENTAGE", &p_string);
410   if (p_string != NULL)
411     nat_percentage = atof(p_string);
412   else
413     nat_percentage = 0.0; /* FIXME: default modifier? */
414
415   GNUNET_free_non_null(p_string);
416
417   cutoff = (unsigned int) (nat_percentage * pg->total);
418
419   connect_attempts = 0;
420
421   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
422     {
423       for (inner_count = outer_count + 1; inner_count < pg->total;
424            inner_count++)
425         {
426           if ((outer_count > cutoff) || (inner_count > cutoff))
427             {
428 #if VERBOSE_TESTING
429               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
430                           "Connecting peer %d to peer %d\n",
431                           outer_count, inner_count);
432 #endif
433               connect_attempts += add_connections(pg, outer_count, inner_count);
434             }
435         }
436     }
437
438   return connect_attempts;
439
440 }
441
442
443
444 static int
445 create_small_world (struct GNUNET_TESTING_PeerGroup *pg)
446 {
447   unsigned int i, j, k;
448   unsigned int square;
449   unsigned int rows;
450   unsigned int cols;
451   unsigned int toggle = 1;
452   unsigned int nodeToConnect;
453   unsigned int natLog;
454   unsigned int node1Row;
455   unsigned int node1Col;
456   unsigned int node2Row;
457   unsigned int node2Col;
458   unsigned int distance;
459   double probability, random, percentage;
460   unsigned int smallWorldConnections;
461   char *p_string;
462   int connect_attempts;
463   square = floor (sqrt (pg->total));
464   rows = square;
465   cols = square;
466
467   GNUNET_CONFIGURATION_get_value_string(pg->cfg, "TESTING", "PERCENTAGE", &p_string);
468   if (p_string != NULL)
469     percentage = atof(p_string);
470   else
471     percentage = 0.5; /* FIXME: default percentage? */
472
473   GNUNET_free_non_null(p_string);
474
475   GNUNET_CONFIGURATION_get_value_string(pg->cfg, "TESTING", "PROBABILITY", &p_string);
476   if (p_string != NULL)
477     probability = atof(p_string);
478   else
479     probability = 0.5; /* FIXME: default probability? */
480
481   GNUNET_free_non_null(p_string);
482
483   if (square * square != pg->total)
484     {
485       while (rows * cols < pg->total)
486         {
487           if (toggle % 2 == 0)
488             rows++;
489           else
490             cols++;
491
492           toggle++;
493         }
494     }
495 #if VERBOSE_TESTING
496       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
497                   _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
498                   rows, cols);
499 #endif
500
501   connect_attempts = 0;
502   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
503    * to the node to its right and above.  Once this is over, we'll have our torus!
504    * Special case for the last node (if the rows and columns are not equal), connect
505    * to the first in the row to maintain topology.
506    */
507   for (i = 0; i < pg->total; i++)
508     {
509       /* First connect to the node to the right */
510       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
511         nodeToConnect = i + 1;
512       else if (i + 1 == pg->total)
513         nodeToConnect = rows * cols - cols;
514       else
515         nodeToConnect = i - cols + 1;
516
517       connect_attempts += add_connections (pg, i, nodeToConnect);
518
519       if (i < cols)
520         nodeToConnect = (rows * cols) - cols + i;
521       else
522         nodeToConnect = i - cols;
523
524       if (nodeToConnect < pg->total)
525         connect_attempts += add_connections (pg, i, nodeToConnect);
526     }
527   natLog = log (pg->total);
528 #if VERBOSE_TESTING
529   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
530               _("natural log of %d is %d, will run %d iterations\n"),
531              pg->total, natLog, (int) (natLog * percentage));
532   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Total connections added thus far: %u!\n"), connect_attempts);
533 #endif
534   smallWorldConnections = 0;
535   for (i = 0; i < (int) (natLog * percentage); i++)
536     {
537       for (j = 0; j < pg->total; j++)
538         {
539           /* Determine the row and column of node at position j on the 2d torus */
540           node1Row = j / cols;
541           node1Col = j - (node1Row * cols);
542           for (k = 0; k < pg->total; k++)
543             {
544               /* Determine the row and column of node at position k on the 2d torus */
545               node2Row = k / cols;
546               node2Col = k - (node2Row * cols);
547               /* Simple Cartesian distance */
548               distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
549               if (distance > 1)
550                 {
551                   /* Calculate probability as 1 over the square of the distance */
552                   probability = 1.0 / (distance * distance);
553                   /* Choose a random, divide by RAND_MAX to get a number between 0 and 1 */
554                   random = ((double) rand () / RAND_MAX);
555                   /* If random < probability, then connect the two nodes */
556                   if (random < probability)
557                     smallWorldConnections += add_connections (pg, j, k);
558
559                 }
560             }
561         }
562     }
563   connect_attempts += smallWorldConnections;
564 #if VERBOSE_TESTING
565           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
566                       _("Total connections added for small world: %d!\n"),
567                       smallWorldConnections);
568 #endif
569   return GNUNET_OK;
570 }
571
572
573
574 static int
575 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg)
576 {
577   double temp_rand;
578   unsigned int outer_count;
579   unsigned int inner_count;
580   int connect_attempts;
581   double probability;
582   char *p_string;
583   connect_attempts = 0;
584
585   GNUNET_CONFIGURATION_get_value_string(pg->cfg, "TESTING", "PROBABILITY", &p_string);
586   if (p_string != NULL)
587     {
588       probability = atof(p_string);
589     }
590   else
591     {
592       probability = 0.0; /* FIXME: default probability? */
593     }
594   GNUNET_free (p_string);
595   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
596     {
597       for (inner_count = outer_count + 1; inner_count < pg->total;
598            inner_count++)
599         {
600           temp_rand = ((double) RANDOM () / RAND_MAX);
601 #if VERBOSE_TESTING
602           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
603                       _("rand is %f probability is %f\n"), temp_rand,
604                       probability);
605 #endif
606           if (temp_rand < probability)
607             {
608               connect_attempts += add_connections (pg, outer_count, inner_count);
609             }
610         }
611     }
612
613   return connect_attempts;
614 }
615
616 static int
617 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg)
618 {
619   unsigned int i;
620   unsigned int square;
621   unsigned int rows;
622   unsigned int cols;
623   unsigned int toggle = 1;
624   unsigned int nodeToConnect;
625   int connect_attempts;
626
627   connect_attempts = 0;
628
629   square = floor (sqrt (pg->total));
630   rows = square;
631   cols = square;
632
633   if (square * square != pg->total)
634     {
635       while (rows * cols < pg->total)
636         {
637           if (toggle % 2 == 0)
638             rows++;
639           else
640             cols++;
641
642           toggle++;
643         }
644     }
645 #if VERBOSE_TESTING
646       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
647                   _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
648                   rows, cols);
649 #endif
650   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
651    * to the node to its right and above.  Once this is over, we'll have our torus!
652    * Special case for the last node (if the rows and columns are not equal), connect
653    * to the first in the row to maintain topology.
654    */
655   for (i = 0; i < pg->total; i++)
656     {
657       /* First connect to the node to the right */
658       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
659         nodeToConnect = i + 1;
660       else if (i + 1 == pg->total)
661         nodeToConnect = rows * cols - cols;
662       else
663         nodeToConnect = i - cols + 1;
664 #if VERBOSE_TESTING
665           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
666                       "Connecting peer %d to peer %d\n",
667                       i, nodeToConnect);
668 #endif
669       connect_attempts += add_connections(pg, i, nodeToConnect);
670
671       /* Second connect to the node immediately above */
672       if (i < cols)
673         nodeToConnect = (rows * cols) - cols + i;
674       else
675         nodeToConnect = i - cols;
676
677       if (nodeToConnect < pg->total)
678         {
679 #if VERBOSE_TESTING
680           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
681                       "Connecting peer %d to peer %d\n",
682                       i, nodeToConnect);
683 #endif
684           connect_attempts += add_connections(pg, i, nodeToConnect);
685         }
686
687     }
688
689   return connect_attempts;
690 }
691
692
693
694 static int
695 create_clique (struct GNUNET_TESTING_PeerGroup *pg)
696 {
697   unsigned int outer_count;
698   unsigned int inner_count;
699   int connect_attempts;
700
701   connect_attempts = 0;
702
703   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
704     {
705       for (inner_count = outer_count + 1; inner_count < pg->total;
706            inner_count++)
707         {
708 #if VERBOSE_TESTING
709           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
710                       "Connecting peer %d to peer %d\n",
711                       outer_count, inner_count);
712 #endif
713           connect_attempts += add_connections(pg, outer_count, inner_count);
714         }
715     }
716
717   return connect_attempts;
718 }
719
720
721 static int
722 create_ring (struct GNUNET_TESTING_PeerGroup *pg)
723 {
724   unsigned int count;
725   int connect_attempts;
726
727   connect_attempts = 0;
728
729   /* Connect each peer to the next highest numbered peer */
730   for (count = 0; count < pg->total - 1; count++)
731     {
732 #if VERBOSE_TESTING
733           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
734                       "Connecting peer %d to peer %d\n",
735                       count, count + 1);
736 #endif
737       connect_attempts += add_connections(pg, count, count + 1);
738     }
739
740   /* Connect the last peer to the first peer */
741   connect_attempts += add_connections(pg, pg->total - 1, 0);
742
743   return connect_attempts;
744 }
745
746
747 /*
748  * Create the friend files based on the PeerConnection's
749  * of each peer in the peer group, and copy the files
750  * to the appropriate place
751  *
752  * @param pg the peer group we are dealing with
753  */
754 static void
755 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
756 {
757   FILE *temp_friend_handle;
758   unsigned int pg_iter;
759   struct PeerConnection *connection_iter;
760   struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
761   char *temp_service_path;
762   pid_t pid;
763   char *arg;
764   struct GNUNET_PeerIdentity *temppeer;
765   char * mytemp;
766
767   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
768     {
769       mytemp = GNUNET_DISK_mktemp("friends");
770       temp_friend_handle = fopen (mytemp, "wt");
771       connection_iter = pg->peers[pg_iter].connected_peers;
772       while (connection_iter != NULL)
773         {
774           temppeer = &connection_iter->daemon->id;
775           GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
776           fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
777           connection_iter = connection_iter->next;
778         }
779
780       fclose(temp_friend_handle);
781
782       GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path);
783
784       if (temp_service_path == NULL)
785         {
786           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
787                     _("No SERVICEHOME specified in peer configuration, can't copy friends file!\n"));
788           if (unlink(mytemp) != 0)
789             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
790                                 _("Couldn't remove friends file %s!\n"), mytemp);
791           GNUNET_free (mytemp);
792           break;
793         }
794
795       if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
796         {
797           GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
798           pid = GNUNET_OS_start_process (NULL, NULL, "mv",
799                                          "mv", mytemp, arg, NULL);
800 #if VERBOSE_TESTING
801           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
802                       _("Copying file with command cp %s %s\n"), mytemp, arg);
803 #endif
804           GNUNET_free(arg);
805         }
806       else /* Remote, scp the file to the correct place */
807         {
808           if (NULL != pg->peers[pg_iter].daemon->username)
809             GNUNET_asprintf (&arg, "%s@%s:%s/friends", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
810           else
811             GNUNET_asprintf (&arg, "%s:%s/friends", pg->peers[pg_iter].daemon->hostname, temp_service_path);
812           pid = GNUNET_OS_start_process (NULL, NULL, "scp",
813                                          "scp", mytemp, arg, NULL);
814 #if VERBOSE_TESTING
815           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
816                       _("Copying file with command scp %s %s\n"), mytemp, arg);
817 #endif
818           GNUNET_free(arg);
819         }
820       GNUNET_free (temp_service_path);
821       GNUNET_free (mytemp);
822     }
823 }
824
825
826
827 /*
828  * Connect the topology as specified by the PeerConnection's
829  * of each peer in the peer group
830  *
831  * @param pg the peer group we are dealing with
832  */
833 static void
834 connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
835 {
836   unsigned int pg_iter;
837   struct PeerConnection *connection_iter;
838
839   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
840     {
841       connection_iter = pg->peers[pg_iter].connected_peers;
842       while (connection_iter != NULL)
843         {
844           GNUNET_TESTING_daemons_connect (pg->peers[pg_iter].daemon,
845                                           connection_iter->daemon,
846                                           CONNECT_TIMEOUT,
847                                           pg->notify_connection,
848                                           pg->notify_connection_cls);
849           connection_iter = connection_iter->next;
850         }
851     }
852 }
853
854
855 /*
856  * Takes a peer group and attempts to create a topology based on the
857  * one specified in the configuration file.  Returns the number of connections
858  * that will attempt to be created, but this will happen asynchronously(?) so
859  * the caller will have to keep track (via the callback) of whether or not
860  * the connection actually happened.
861  *
862  * @param pg the peer group struct representing the running peers
863  *
864  */
865 int
866 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg)
867 {
868   unsigned long long topology_num;
869   int ret;
870
871   GNUNET_assert (pg->notify_connection != NULL);
872   ret = 0;
873   if (GNUNET_YES ==
874       GNUNET_CONFIGURATION_get_value_number (pg->cfg, "testing", "topology",
875                                              &topology_num))
876     {
877       switch (topology_num)
878         {
879         case GNUNET_TESTING_TOPOLOGY_CLIQUE:
880 #if VERBOSE_TESTING
881           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
882                       _("Creating clique topology (may take a bit!)\n"));
883 #endif
884           ret = create_clique (pg);
885           break;
886         case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
887 #if VERBOSE_TESTING
888           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
889                       _("Creating small world (ring) topology (may take a bit!)\n"));
890 #endif
891           ret = create_small_world_ring (pg);
892           break;
893         case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
894 #if VERBOSE_TESTING
895           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
896                       _("Creating small world (2d-torus) topology (may take a bit!)\n"));
897 #endif
898           ret = create_small_world (pg);
899           break;
900         case GNUNET_TESTING_TOPOLOGY_RING:
901 #if VERBOSE_TESTING
902           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
903                       _("Creating ring topology (may take a bit!)\n"));
904 #endif
905           ret = create_ring (pg);
906           break;
907         case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
908 #if VERBOSE_TESTING
909           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
910                       _("Creating 2d torus topology (may take a bit!)\n"));
911 #endif
912           ret = create_2d_torus (pg);
913           break;
914         case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
915 #if VERBOSE_TESTING
916           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
917                       _("Creating Erdos-Renyi topology (may take a bit!)\n"));
918 #endif
919           ret = create_erdos_renyi (pg);
920           break;
921         case GNUNET_TESTING_TOPOLOGY_INTERNAT:
922 #if VERBOSE_TESTING
923           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
924                       _("Creating InterNAT topology (may take a bit!)\n"));
925 #endif
926           ret = create_nated_internet (pg);
927           break;
928         case GNUNET_TESTING_TOPOLOGY_NONE:
929           ret = 0;
930           break;
931         default:
932           ret = GNUNET_SYSERR;
933           break;
934         }
935
936       if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
937         create_and_copy_friend_files(pg);
938
939       connect_topology(pg);
940     }
941   else
942     {
943       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
944                   _("No topology specified, was one intended?\n"));
945     }
946
947   return ret;
948 }
949
950 /**
951  * Start count gnunetd processes with the same set of transports and
952  * applications.  The port numbers (any option called "PORT") will be
953  * adjusted to ensure that no two peers running on the same system
954  * have the same port(s) in their respective configurations.
955  *
956  * @param sched scheduler to use 
957  * @param cfg configuration template to use
958  * @param total number of daemons to start
959  * @param cb function to call on each daemon that was started
960  * @param cb_cls closure for cb
961  * @param connect_callback function to call each time two hosts are connected
962  * @param connect_callback_cls closure for connect_callback
963  * @param hostnames space-separated list of hostnames to use; can be NULL (to run
964  *        everything on localhost).
965  * @return NULL on error, otherwise handle to control peer group
966  */
967 struct GNUNET_TESTING_PeerGroup *
968 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
969                               const struct GNUNET_CONFIGURATION_Handle *cfg,
970                               unsigned int total,
971                               GNUNET_TESTING_NotifyDaemonRunning cb,
972                               void *cb_cls,
973                               GNUNET_TESTING_NotifyConnection
974                               connect_callback, void *connect_callback_cls,
975                               const char *hostnames)
976 {
977   struct GNUNET_TESTING_PeerGroup *pg;
978   const char *rpos;
979   char *pos;
980   char *start;
981   const char *hostname;
982   char *baseservicehome;
983   char *newservicehome;
984   char *tmpdir;
985   struct GNUNET_CONFIGURATION_Handle *pcfg;
986   unsigned int off;
987   unsigned int hostcnt;
988   uint16_t minport;
989
990   if (0 == total)
991     {
992       GNUNET_break (0);
993       return NULL;
994     }
995   pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
996   pg->sched = sched;
997   pg->cfg = cfg;
998   pg->cb = cb;
999   pg->cb_cls = cb_cls;
1000   pg->notify_connection = connect_callback;
1001   pg->notify_connection_cls = connect_callback_cls;
1002   pg->total = total;
1003   pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
1004   if (NULL != hostnames)
1005     {
1006       off = 2;
1007       /* skip leading spaces */
1008       while ((0 != *hostnames) && (isspace (*hostnames)))
1009         hostnames++;
1010       rpos = hostnames;
1011       while ('\0' != *rpos)
1012         {
1013           if (isspace (*rpos))
1014             off++;
1015           rpos++;
1016         }
1017       pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
1018       off = 0;
1019       start = GNUNET_strdup (hostnames);
1020       pos = start;
1021       while ('\0' != *pos)
1022         {
1023           if (isspace (*pos))
1024             {
1025               *pos = '\0';
1026               if (strlen (start) > 0)
1027                 {
1028                   pg->hosts[off].minport = LOW_PORT;
1029                   pg->hosts[off++].hostname = start;
1030                 }
1031               start = pos + 1;
1032             }
1033           pos++;
1034         }
1035       if (strlen (start) > 0)
1036         {
1037           pg->hosts[off].minport = LOW_PORT;
1038           pg->hosts[off++].hostname = start;
1039         }
1040       if (off == 0)
1041         {
1042           GNUNET_free (start);
1043           GNUNET_free (pg->hosts);
1044           pg->hosts = NULL;
1045         }
1046       hostcnt = off;
1047       minport = 0;              /* make gcc happy */
1048     }
1049   else
1050     {
1051       hostcnt = 0;
1052       minport = LOW_PORT;
1053     }
1054   for (off = 0; off < total; off++)
1055     {
1056       if (hostcnt > 0)
1057         {
1058           hostname = pg->hosts[off % hostcnt].hostname;
1059           pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport);
1060         }
1061       else
1062         {
1063           hostname = NULL;
1064           pcfg = make_config (cfg, &minport);
1065         }
1066       if (NULL == pcfg)
1067         {
1068           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1069                       _
1070                       ("Could not create configuration for peer number %u on `%s'!\n"),
1071                       off, hostname == NULL ? "localhost" : hostname);
1072           continue;
1073         }
1074
1075       if (GNUNET_YES ==
1076           GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
1077                                                  &baseservicehome))
1078         {
1079           GNUNET_asprintf (&newservicehome,
1080                            "%s/%d/", baseservicehome, off);
1081           GNUNET_free (baseservicehome);
1082         }
1083       else
1084         {
1085           tmpdir = getenv ("TMPDIR");
1086           tmpdir = tmpdir ? tmpdir : "/tmp";
1087           GNUNET_asprintf (&newservicehome,
1088                            "%s/%s/%d/",
1089                            tmpdir,
1090                            "gnunet-testing-test-test", off);
1091         }
1092       GNUNET_CONFIGURATION_set_value_string (pcfg,
1093                                              "PATHS",
1094                                              "SERVICEHOME", newservicehome);
1095       GNUNET_free (newservicehome);
1096       pg->peers[off].cfg = pcfg;
1097       pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
1098                                                            pcfg,
1099                                                            hostname,
1100                                                            cb, cb_cls);
1101       if (NULL == pg->peers[off].daemon)
1102         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1103                     _("Could not start peer number %u!\n"), off);
1104     }
1105   return pg;
1106 }
1107
1108 /*
1109  * Get a daemon by number, so callers don't have to do nasty
1110  * offsetting operation.
1111  */
1112 struct GNUNET_TESTING_Daemon *
1113 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int position)
1114 {
1115   if (position < pg->total)
1116     return pg->peers[position].daemon;
1117   else
1118     return NULL;
1119 }
1120
1121 /**
1122  * Shutdown all peers started in the given group.
1123  * 
1124  * @param pg handle to the peer group
1125  */
1126 void
1127 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
1128 {
1129   unsigned int off;
1130
1131   for (off = 0; off < pg->total; off++)
1132     {
1133       /* FIXME: should we wait for our
1134          continuations to be called here? This
1135          would require us to take a continuation
1136          as well... */
1137
1138       if (NULL != pg->peers[off].daemon)
1139         GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL);
1140       if (NULL != pg->peers[off].cfg)
1141         GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
1142     }
1143   GNUNET_free (pg->peers);
1144   if (NULL != pg->hosts)
1145     {
1146       GNUNET_free (pg->hosts[0].hostname);
1147       GNUNET_free (pg->hosts);
1148     }
1149   GNUNET_free (pg);
1150 }
1151
1152
1153 /* end of testing_group.c */