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