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