9eaf702a3342c8392e39fbcec458bb8cb87980ed
[oweals/gnunet.git] / src / testbed / gnunet-testbed-profiler.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011, 2012 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 3, 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 testbed/gnunet-testbed-profiler.c
23  * @brief Profiling driver for the testbed.
24  * @author Sree Harsha Totakura <sreeharsha@totakura.in> 
25  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_testbed_service.h"
31 #include "testbed_api_hosts.h"
32
33 /**
34  * Generic loggins shorthand
35  */
36 #define LOG(kind,...)                                           \
37   GNUNET_log (kind, __VA_ARGS__)
38
39
40 /**
41  * DLL of operations
42  */
43 struct DLLOperation
44 {
45   /**
46    * The testbed operation handle
47    */
48   struct GNUNET_TESTBED_Operation *op;
49
50   /**
51    * Closure
52    */
53   void *cls;
54
55   /**
56    * The next pointer for DLL
57    */
58   struct DLLOperation *next;
59
60   /**
61    * The prev pointer for DLL
62    */
63   struct DLLOperation *prev;
64 };
65
66
67 /**
68  * Availanle states during profiling
69  */
70 enum State
71 {
72   /**
73    * Initial state
74    */
75   STATE_INIT = 0,
76
77   /**
78    * Starting slaves
79    */
80   STATE_SLAVES_STARTING,
81
82   /**
83    * Creating peers
84    */
85   STATE_PEERS_CREATING,
86
87   /**
88    * Starting peers
89    */
90   STATE_PEERS_STARTING,
91
92   /**
93    * Linking peers
94    */
95   STATE_PEERS_LINKING,
96
97   /**
98    * Destroying peers; we can do this as the controller takes care of stopping a
99    * peer if it is running
100    */
101   STATE_PEERS_DESTROYING
102 };
103
104
105 /**
106  * An array of hosts loaded from the hostkeys file
107  */
108 static struct GNUNET_TESTBED_Host **hosts;
109
110 /**
111  * The array of peers; we fill this as the peers are given to us by the testbed
112  */
113 static struct GNUNET_TESTBED_Peer **peers;
114
115 /* /\** */
116 /*  * Operation handle */
117 /*  *\/ */
118 /* static struct GNUNET_TESTBED_Operation *op; */
119
120 /**
121  * Host registration handle
122  */
123 static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
124
125 /**
126  * Handle to the master controller process
127  */
128 struct GNUNET_TESTBED_ControllerProc *mc_proc;
129
130 /**
131  * Handle to the master controller
132  */
133 struct GNUNET_TESTBED_Controller *mc;
134
135 /**
136  * Handle to global configuration
137  */
138 struct GNUNET_CONFIGURATION_Handle *cfg;
139
140 /**
141  * Head of the operations list
142  */
143 struct DLLOperation *dll_op_head;
144
145 /**
146  * Tail of the operations list
147  */
148 struct DLLOperation *dll_op_tail;
149
150 /**
151  * Peer linking - topology operation
152  */
153 struct GNUNET_TESTBED_Operation *topology_op;
154
155 /**
156  * The handle for whether a host is habitable or not
157  */
158 struct GNUNET_TESTBED_HostHabitableCheckHandle **hc_handles;
159
160 /**
161  * Abort task identifier
162  */
163 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
164
165 /**
166  * Shutdown task identifier
167  */
168 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
169
170 /**
171  * Host registration task identifier
172  */
173 static GNUNET_SCHEDULER_TaskIdentifier register_hosts_task;
174
175 /**
176  * Global event mask for all testbed events
177  */
178 uint64_t event_mask;
179
180 /**
181  * The starting time of a profiling step
182  */
183 struct GNUNET_TIME_Absolute prof_start_time;
184
185 /**
186  * Duration profiling step has taken
187  */
188 struct GNUNET_TIME_Relative prof_time;
189
190 /**
191  * Current peer id
192  */
193 unsigned int peer_id;
194
195 /**
196  * Number of peers to be started by the profiler
197  */
198 static unsigned int num_peers;
199
200 /**
201  * Number of hosts in the hosts array
202  */
203 static unsigned int num_hosts;
204
205 /**
206  * Number of random links to be established between peers
207  */
208 static unsigned int num_links;
209
210 /**
211  * Number of timeout failures to tolerate
212  */
213 static unsigned int num_cont_fails;
214
215 /**
216  * Continuous failures during overlay connect operations
217  */
218 static unsigned int cont_fails;
219
220 /**
221  * Links which are successfully established
222  */
223 static unsigned int established_links;
224
225 /**
226  * Links which are not successfully established
227  */
228 static unsigned int failed_links;
229
230 /**
231  * Global testing status
232  */
233 static int result;
234
235 /**
236  * current state of profiling
237  */
238 enum State state;
239
240 /**
241  * The topology we want to acheive
242  */
243 enum GNUNET_TESTBED_TopologyOption topology;
244
245
246 /**
247  * Shutdown nicely
248  *
249  * @param cls NULL
250  * @param tc the task context
251  */
252 static void
253 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
254 {
255   struct DLLOperation *dll_op;
256   unsigned int nhost;
257
258   shutdown_task = GNUNET_SCHEDULER_NO_TASK;
259   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
260     GNUNET_SCHEDULER_cancel (abort_task);
261   if (NULL != hc_handles)
262   {
263     for (nhost = 0; nhost < num_hosts; nhost++)
264       if (NULL != hc_handles[num_hosts])
265         GNUNET_TESTBED_is_host_habitable_cancel (hc_handles[num_hosts]);
266     GNUNET_free (hc_handles);
267     hc_handles = NULL;
268   }
269   if (GNUNET_SCHEDULER_NO_TASK != register_hosts_task)
270     GNUNET_SCHEDULER_cancel (register_hosts_task);
271   if (NULL != reg_handle)
272     GNUNET_TESTBED_cancel_registration (reg_handle);
273   if (NULL != topology_op)
274     GNUNET_TESTBED_operation_done (topology_op);
275   for (nhost = 0; nhost < num_hosts; nhost++)
276     if (NULL != hosts[nhost])
277       GNUNET_TESTBED_host_destroy (hosts[nhost]);
278   GNUNET_free_non_null (hosts);
279   while (NULL != (dll_op = dll_op_head))
280   {
281     GNUNET_TESTBED_operation_done (dll_op->op);
282     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
283     GNUNET_free (dll_op);
284   }
285   if (NULL != mc)
286     GNUNET_TESTBED_controller_disconnect (mc);
287   if (NULL != mc_proc)
288     GNUNET_TESTBED_controller_stop (mc_proc);
289   if (NULL != cfg)
290     GNUNET_CONFIGURATION_destroy (cfg);
291   GNUNET_SCHEDULER_shutdown (); /* Stop scheduler to shutdown testbed run */
292 }
293
294
295 /**
296  * abort task to run on test timed out
297  *
298  * @param cls NULL
299  * @param tc the task context
300  */
301 static void
302 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
303 {
304   LOG (GNUNET_ERROR_TYPE_WARNING, "Aborting\n");
305   abort_task = GNUNET_SCHEDULER_NO_TASK;
306   result = GNUNET_SYSERR;
307   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
308     GNUNET_SCHEDULER_cancel (shutdown_task);
309   shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
310 }
311
312
313 /**
314  * Functions of this signature are called when a peer has been successfully
315  * started or stopped.
316  *
317  * @param cls the closure from GNUNET_TESTBED_peer_start/stop()
318  * @param emsg NULL on success; otherwise an error description
319  */
320 static void 
321 peer_churn_cb (void *cls, const char *emsg)
322 {
323   struct DLLOperation *dll_op = cls;
324   struct GNUNET_TESTBED_Operation *op;  
325   static unsigned int started_peers;
326
327   op = dll_op->op;
328   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
329   GNUNET_free (dll_op);
330   if (NULL != emsg)
331   {
332     LOG (GNUNET_ERROR_TYPE_WARNING,
333          _("An operation has failed while starting peers\n"));
334     GNUNET_TESTBED_operation_done (op);
335     GNUNET_SCHEDULER_cancel (abort_task);
336     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
337     return;
338   }
339   GNUNET_TESTBED_operation_done (op);
340   if (++started_peers == num_peers)
341   {
342     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
343     printf ("%u peers started successfully in %.2f seconds\n",
344             num_peers, ((double) prof_time.rel_value) / 1000.00);
345     fflush (stdout);
346     result = GNUNET_OK;
347     if ((0 == num_links) && (topology == GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI))
348     { 
349       fprintf (stdout, "Testbed running, waiting for keystroke to shut down\n");
350       (void) getc (stdin);
351       shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
352       return;
353     }
354     state = STATE_PEERS_LINKING;
355     /* Do overlay connect */
356     prof_start_time = GNUNET_TIME_absolute_get ();
357     switch (topology)
358     {
359     case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI:
360       topology_op =
361           GNUNET_TESTBED_overlay_configure_topology (NULL, num_peers, peers, NULL,
362                                                      topology,
363                                                      num_links,
364                                                      GNUNET_TESTBED_TOPOLOGY_DISABLE_AUTO_RETRY,
365                                                      GNUNET_TESTBED_TOPOLOGY_OPTION_END);
366       break;
367     case GNUNET_TESTBED_TOPOLOGY_CLIQUE:
368       topology_op =
369           GNUNET_TESTBED_overlay_configure_topology (NULL, num_peers, peers, NULL,
370                                                      topology,
371                                                      GNUNET_TESTBED_TOPOLOGY_DISABLE_AUTO_RETRY,
372                                                      GNUNET_TESTBED_TOPOLOGY_OPTION_END);
373       num_links = num_peers * (num_peers - 1);
374       break;
375     default:
376       GNUNET_assert (0);
377     }
378   }
379 }
380
381
382 /**
383  * Functions of this signature are called when a peer has been successfully
384  * created
385  *
386  * @param cls the closure from GNUNET_TESTBED_peer_create()
387  * @param peer the handle for the created peer; NULL on any error during
388  *          creation
389  * @param emsg NULL if peer is not NULL; else MAY contain the error description
390  */
391 static void 
392 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
393 {
394   struct DLLOperation *dll_op = cls;
395   struct GNUNET_TESTBED_Peer **peer_ptr;
396   static unsigned int created_peers;
397   unsigned int peer_cnt;
398
399   if (NULL != emsg)
400   {
401     LOG (GNUNET_ERROR_TYPE_WARNING,
402          _("Creating a peer failed. Error: %s\n"), emsg);
403     GNUNET_TESTBED_operation_done (dll_op->op);
404     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
405     GNUNET_free (dll_op);
406     GNUNET_SCHEDULER_cancel (abort_task);
407     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
408     return;
409   }
410   peer_ptr = dll_op->cls;
411   GNUNET_assert (NULL == *peer_ptr);
412   *peer_ptr = peer;
413   GNUNET_TESTBED_operation_done (dll_op->op);
414   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
415   GNUNET_free (dll_op);
416   if (++created_peers == num_peers)
417   {
418     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);    
419     printf ("%u peers created successfully in %.2f seconds\n",
420             num_peers, ((double) prof_time.rel_value) / 1000.00);
421     fflush (stdout);
422     /* Now peers are to be started */
423     state = STATE_PEERS_STARTING;
424     prof_start_time = GNUNET_TIME_absolute_get ();
425     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
426     {
427       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
428       dll_op->op = GNUNET_TESTBED_peer_start (dll_op, peers[peer_cnt], 
429                                               &peer_churn_cb, dll_op);
430       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
431     }
432   }
433 }
434
435
436 /**
437  * Function to print summary about how many overlay links we have made and how
438  * many failed
439  */
440 static void
441 print_overlay_links_summary ()
442 {
443   prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
444   printf ("\n%u links established in %.2f seconds\n",
445           established_links, ((double) prof_time.rel_value) / 1000.00);
446   printf ("%u links failed due to timeouts\n", failed_links);
447 }
448
449
450 /**
451  * Function to start peers
452  */
453 static void
454 start_peers ()
455 {
456   struct DLLOperation *dll_op;
457   unsigned int peer_cnt;
458   
459   state = STATE_PEERS_CREATING;
460   prof_start_time = GNUNET_TIME_absolute_get ();
461   peers = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *)
462                          * num_peers);
463   for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
464   {
465     dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
466     dll_op->cls = &peers[peer_cnt];
467     dll_op->op = GNUNET_TESTBED_peer_create (mc,
468                                              hosts
469                                              [peer_cnt % num_hosts],
470                                              cfg,
471                                              &peer_create_cb,
472                                              dll_op);
473     GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
474   }
475 }
476
477
478 /**
479  * Controller event callback
480  *
481  * @param cls NULL
482  * @param event the controller event
483  */
484 static void
485 controller_event_cb (void *cls,
486                      const struct GNUNET_TESTBED_EventInformation *event)
487 {
488   struct DLLOperation *dll_op;
489   struct GNUNET_TESTBED_Operation *op;
490
491   switch (state)
492   {
493   case STATE_SLAVES_STARTING:
494     switch (event->type)
495     {
496     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
497       {
498         static unsigned int slaves_started;
499         
500         dll_op = event->details.operation_finished.op_cls;
501         GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
502         GNUNET_free (dll_op);
503         op = event->details.operation_finished.operation;
504         if (NULL != event->details.operation_finished.emsg)
505         {
506           LOG (GNUNET_ERROR_TYPE_WARNING,
507                _("An operation has failed while starting slaves\n"));
508           GNUNET_TESTBED_operation_done (op);
509           GNUNET_SCHEDULER_cancel (abort_task);
510           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
511           return;
512         }
513         GNUNET_TESTBED_operation_done (op);
514         /* Proceed to start peers */
515         if (++slaves_started == num_hosts - 1)
516         {
517           printf ("%u controllers started successfully\n", num_hosts);
518           fflush (stdout);
519           start_peers ();
520         }
521       }
522       break;
523     default:
524       GNUNET_assert (0);
525     }
526     break;
527   case STATE_PEERS_STARTING:
528     switch (event->type)
529     {
530     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
531       /* Control reaches here when peer start fails */
532     case GNUNET_TESTBED_ET_PEER_START:
533       /* we handle peer starts in peer_churn_cb */
534       break;
535     default:
536       GNUNET_assert (0);
537     }
538     break;
539   case STATE_PEERS_LINKING:
540     switch (event->type)
541     {
542     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
543       /* Control reaches here when a peer linking operation fails */
544       if (NULL != event->details.operation_finished.emsg)
545       {
546         printf ("F");
547         fflush (stdout);
548         failed_links++;
549         if (++cont_fails > num_cont_fails)
550         {
551           printf ("\nAborting due to very high failure rate");
552           print_overlay_links_summary ();         
553           GNUNET_SCHEDULER_cancel (abort_task);
554           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
555           return;
556         }
557       }
558       break;
559     case GNUNET_TESTBED_ET_CONNECT:
560       {
561         if (0 != cont_fails)
562           cont_fails--;
563         if (0 == established_links)
564           printf ("Establishing links. Please wait\n");
565         printf (".");
566         fflush (stdout);
567         established_links++;
568       }
569       break;
570     default:
571       GNUNET_assert (0);
572     }
573     if ((established_links + failed_links) == num_links)
574     {
575       print_overlay_links_summary ();
576       result = GNUNET_OK;
577       fprintf (stdout, "Testbed running, waiting for keystroke to shut down\n");
578       (void) getc (stdin);
579       shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
580     }    
581     break;
582   default:
583     GNUNET_assert (0);
584   }
585 }
586
587
588 /**
589  * Task to register all hosts available in the global host list
590  *
591  * @param cls NULL
592  * @param tc the scheduler task context
593  */
594 static void
595 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
596
597
598 /**
599  * Callback which will be called to after a host registration succeeded or failed
600  *
601  * @param cls the closure
602  * @param emsg the error message; NULL if host registration is successful
603  */
604 static void
605 host_registration_completion (void *cls, const char *emsg)
606 {
607   reg_handle = NULL;
608   if (NULL != emsg)
609   {
610     LOG (GNUNET_ERROR_TYPE_WARNING,
611          _("Host registration failed for a host. Error: %s\n"), emsg);
612     GNUNET_SCHEDULER_cancel (abort_task);
613     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
614     return;
615   }
616   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
617 }
618
619
620 /**
621  * Task to register all hosts available in the global host list
622  *
623  * @param cls NULL
624  * @param tc the scheduler task context
625  */
626 static void
627 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
628 {
629   struct DLLOperation *dll_op;
630   static unsigned int reg_host;
631   unsigned int slave;
632
633   register_hosts_task = GNUNET_SCHEDULER_NO_TASK;  
634   if (reg_host == num_hosts - 1)
635   {
636     LOG (GNUNET_ERROR_TYPE_DEBUG,
637          "All hosts successfully registered\n");
638     /* Start slaves */
639     state = STATE_SLAVES_STARTING;
640     for (slave = 1; slave < num_hosts; slave++)
641     {
642       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
643       dll_op->op = GNUNET_TESTBED_controller_link (dll_op,
644                                                    mc,
645                                                    hosts[slave],
646                                                    hosts[0],
647                                                    cfg,
648                                                    GNUNET_YES);
649       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
650     }
651     return;
652   }
653   reg_handle = GNUNET_TESTBED_register_host (mc, hosts[++reg_host],
654                                              host_registration_completion,
655                                              NULL);
656 }
657
658
659 /**
660  * Callback to signal successfull startup of the controller process
661  *
662  * @param cls the closure from GNUNET_TESTBED_controller_start()
663  * @param config the configuration with which the controller has been started;
664  *          NULL if status is not GNUNET_OK
665  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
666  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
667  */
668 static void
669 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
670 {
671   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
672     GNUNET_SCHEDULER_cancel (abort_task);
673   if (GNUNET_OK != status)
674   {
675     mc_proc = NULL;
676     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
677     return;
678   }
679   event_mask = 0;
680   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
681   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
682   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
683   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
684   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
685   mc = GNUNET_TESTBED_controller_connect (config, hosts[0], event_mask,
686                                           &controller_event_cb, NULL);
687   if (NULL == mc)
688   {
689     LOG (GNUNET_ERROR_TYPE_WARNING,
690          _("Unable to connect to master controller -- Check config\n"));
691     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
692     return;
693   }
694   if (num_hosts > 1)
695     register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
696   else
697     start_peers ();
698   abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
699                                              &do_abort, NULL);
700 }
701
702
703 /**
704  * Callbacks of this type are called by GNUNET_TESTBED_is_host_habitable to
705  * inform whether the given host is habitable or not. The Handle returned by
706  * GNUNET_TESTBED_is_host_habitable() is invalid after this callback is called
707  *
708  * @param cls NULL
709  * @param status GNUNET_YES if it is habitable; GNUNET_NO if not
710  */
711 static void 
712 host_habitable_cb (void *cls, int status)
713 {
714   struct GNUNET_TESTBED_HostHabitableCheckHandle **hc_handle = cls;
715   static unsigned int hosts_checked;
716
717   *hc_handle = NULL;
718   if (++hosts_checked < num_hosts)
719     return;
720   GNUNET_free (hc_handles);
721   hc_handles = NULL;
722   mc_proc = 
723       GNUNET_TESTBED_controller_start (GNUNET_TESTBED_host_get_hostname_ 
724                                        (hosts[0]),
725                                        hosts[0],
726                                        cfg,
727                                        status_cb,
728                                        NULL);
729 }
730
731
732 /**
733  * Main function that will be run by the scheduler.
734  *
735  * @param cls closure
736  * @param args remaining command-line arguments
737  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
738  * @param config configuration
739  */
740 static void
741 run (void *cls, char *const *args, const char *cfgfile,
742      const struct GNUNET_CONFIGURATION_Handle *config)
743 {
744   unsigned int nhost;
745
746   if (NULL == args[0])
747   {
748     fprintf (stderr, _("No hosts-file specified on command line\n"));
749     return;
750   }
751   if (0 == num_peers)
752   {
753     result = GNUNET_OK;
754     return;
755   }
756   num_hosts = GNUNET_TESTBED_hosts_load_from_file (args[0], &hosts);
757   if (0 == num_hosts)
758   {
759     fprintf (stderr, _("No hosts loaded. Need at least one host\n"));
760     return;
761   }
762   hc_handles = GNUNET_malloc (sizeof (struct
763                                       GNUNET_TESTBED_HostHabitableCheckHandle *) 
764                               * num_hosts);
765   for (nhost = 0; nhost < num_hosts; nhost++)
766   {    
767     if (NULL == (hc_handles[nhost] = GNUNET_TESTBED_is_host_habitable (hosts[nhost], config,
768                                                                        &host_habitable_cb,
769                                                                        &hc_handles[nhost])))
770     {
771       fprintf (stderr, _("Host %s cannot start testbed\n"),
772                GNUNET_TESTBED_host_get_hostname_ (hosts[nhost]));
773       for (nhost = 0; nhost < num_hosts; nhost++)
774         if (NULL != hc_handles[num_hosts])
775           GNUNET_TESTBED_is_host_habitable_cancel (hc_handles[num_hosts]);
776       GNUNET_free (hc_handles);
777       hc_handles = NULL;
778       break;
779     }
780   }
781   if (num_hosts != nhost)
782   {
783     fprintf (stderr, _("Exiting\n"));
784     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
785     return;
786   }
787   cfg = GNUNET_CONFIGURATION_dup (config);
788   abort_task =
789       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
790                                     (GNUNET_TIME_UNIT_SECONDS, 5), &do_abort,
791                                     NULL);
792 }
793
794
795 /**
796  * Set an option of type 'char *' from the command line.
797  * A pointer to this function should be passed as part of the
798  * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
799  * of this type.  It should be followed by a pointer to a value of
800  * type 'char *'.
801  *
802  * @param ctx command line processing context
803  * @param scls additional closure (will point to the 'char *',
804  *             which will be allocated)
805  * @param option name of the option
806  * @param value actual value of the option (a string)
807  * @return GNUNET_OK to continue procesing; GNUNET_SYSERR to signal error
808  */
809 int
810 set_topology (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
811               void *scls, const char *option, const char *value)
812 {
813   enum GNUNET_TESTBED_TopologyOption *val = scls;
814
815   if (0 == strncasecmp ("CLIQUE", value, strlen ("CLIQUE")))
816   {  
817     *val = GNUNET_TESTBED_TOPOLOGY_CLIQUE;
818     return GNUNET_OK;
819   }
820   if (0 == strncasecmp ("RANDOM", value, strlen ("RANDOM")))
821   {  
822     *val = GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI;
823     return GNUNET_OK;
824   }
825   FPRINTF (stderr, "%s", _("Only `CLIQUE' and `RANDOM' are permitted.\n"));
826   return GNUNET_SYSERR;
827 }
828
829
830 /**
831  * Main function.
832  *
833  * @return 0 on success
834  */
835 int
836 main (int argc, char *const *argv)
837 {
838   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
839     { 'p', "num-peers", "COUNT",
840       gettext_noop ("create COUNT number of peers"),
841       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_peers },
842     { 'n', "num-links", "COUNT",
843       gettext_noop ("create COUNT number of random links"),
844       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_links },
845     { 'e', "num-errors", "COUNT",
846       gettext_noop ("tolerate COUNT number of continious timeout failures"),
847       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_cont_fails },
848     { 't', "topology", "TOPOLOGY",
849       gettext_noop ("Try to acheive TOPOLOGY. This options takes either CLIQUE "
850                     "or RANDOM. For CLIQUE the parameter -n is ignored. The "
851                     "default is to acheive a random graph topology."),
852       GNUNET_YES, &set_topology, &topology },
853     GNUNET_GETOPT_OPTION_END
854   };
855   int ret;
856
857   topology = GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI;
858   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
859     return 2;
860   
861   result = GNUNET_SYSERR;
862   ret =
863       GNUNET_PROGRAM_run (argc, argv, "gnunet-testbed-profiler [OPTIONS] hosts-file",
864                           _("Profiler for testbed"),
865                           options, &run, NULL);
866   GNUNET_free ((void*) argv);
867   if (GNUNET_OK != ret)
868     return ret;
869   if (GNUNET_OK != result)
870     return 1;
871   return 0;
872 }