Added statistics service connection to regex profiler
[oweals/gnunet.git] / src / mesh / gnunet-regex-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 mesh/gnunet-regex-profiler.c
23  * @brief Regex profiler for testing distributed regex use.
24  * @author Bart Polot
25  * @author Max Szengel
26  */
27
28 #include <string.h>
29
30 #include "platform.h"
31 #include "gnunet_applications.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_mesh_service.h"
34 #include "gnunet_stream_lib.h"
35 #include "gnunet_testbed_service.h"
36
37 /**
38  * DLL of operations
39  */
40 struct DLLOperation
41 {
42   /**
43    * The testbed operation handle
44    */
45   struct GNUNET_TESTBED_Operation *op;
46
47   /**
48    * Closure
49    */
50   void *cls;
51
52   /**
53    * The next pointer for DLL
54    */
55   struct DLLOperation *next;
56
57   /**
58    * The prev pointer for DLL
59    */
60   struct DLLOperation *prev;
61 };
62
63
64 /**
65  * Available states during profiling
66  */
67 enum State
68 {
69   /**
70    * Initial state
71    */
72   STATE_INIT = 0,
73
74   /**
75    * Starting slaves
76    */
77   STATE_SLAVES_STARTING,
78
79   /**
80    * Creating peers
81    */
82   STATE_PEERS_CREATING,
83
84   /**
85    * Starting peers
86    */
87   STATE_PEERS_STARTING,
88
89   /**
90    * Linking peers
91    */
92   STATE_PEERS_LINKING,
93
94   /**
95    * Destroying peers; we can do this as the controller takes care of stopping a
96    * peer if it is running
97    */
98   STATE_PEERS_DESTROYING
99 };
100
101
102 /**
103  * An array of hosts loaded from the hostkeys file
104  */
105 static struct GNUNET_TESTBED_Host **hosts;
106
107 /**
108  * Peer handles.
109  */
110 struct RegexPeer
111 {
112   /**
113    * Peer id.
114    */
115   unsigned int id;
116
117   /**
118    * The actual testbed peer handle.
119    */
120   struct GNUNET_TESTBED_Peer *peer_handle;
121
122   /**
123    * Host on which the peer is running.
124    */
125   struct GNUNET_TESTBED_Host *host_handle;
126
127   /**
128    * Filename of the peer's policy file.
129    */
130   char *policy_file;
131
132   /**
133    * Peers search string.
134    */
135   const char *search_str;
136
137   /**
138    * Peer's mesh handle.
139    */
140   struct GNUNET_MESH_Handle *mesh_handle;
141
142   /**
143    * Peer's mesh tunnel handle.
144    */
145   struct GNUNET_MESH_Tunnel *mesh_tunnel_handle;
146
147   /**
148    * Testbed operation handle for the mesh service.
149    */
150   struct GNUNET_TESTBED_Operation *mesh_op_handle;
151
152   /**
153    * Peers's statistics handle.
154    */
155   struct GNUNET_STATISTICS_Handle *stats_handle;
156
157   /**
158    * Testbed operation handle for the statistics service.
159    */
160   struct GNUNET_TESTBED_Operation *stats_op_handle;
161 };
162
163 /**
164  * Array of peer handles used to pass to
165  * GNUNET_TESTBED_overlay_configure_topology
166  */
167 struct GNUNET_TESTBED_Peer **peer_handles;
168
169 /**
170  * The array of peers; we fill this as the peers are given to us by the testbed
171  */
172 static struct RegexPeer *peers;
173
174 /**
175  * Host registration handle
176  */
177 static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
178
179 /**
180  * Handle to the master controller process
181  */
182 struct GNUNET_TESTBED_ControllerProc *mc_proc;
183
184 /**
185  * Handle to the master controller
186  */
187 struct GNUNET_TESTBED_Controller *mc;
188
189 /**
190  * Handle to global configuration
191  */
192 struct GNUNET_CONFIGURATION_Handle *cfg;
193
194 /**
195  * Head of the operations list
196  */
197 struct DLLOperation *dll_op_head;
198
199 /**
200  * Tail of the operations list
201  */
202 struct DLLOperation *dll_op_tail;
203
204 /**
205  * Peer linking - topology operation
206  */
207 struct GNUNET_TESTBED_Operation *topology_op;
208
209 /**
210  * Abort task identifier
211  */
212 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
213
214 /**
215  * Host registration task identifier
216  */
217 static GNUNET_SCHEDULER_TaskIdentifier register_hosts_task;
218
219 /**
220  * Global event mask for all testbed events
221  */
222 uint64_t event_mask;
223
224 /**
225  * The starting time of a profiling step
226  */
227 struct GNUNET_TIME_Absolute prof_start_time;
228
229 /**
230  * Duration profiling step has taken
231  */
232 struct GNUNET_TIME_Relative prof_time;
233
234 /**
235  * Current peer id
236  */
237 unsigned int peer_id;
238
239 /**
240  * Number of peers to be started by the profiler
241  */
242 static unsigned int num_peers;
243
244 /**
245  * Number of hosts in the hosts array
246  */
247 static unsigned int num_hosts;
248
249 /**
250  * Number of random links to be established between peers
251  */
252 static unsigned int num_links;
253
254 /**
255  * Number of timeout failures to tolerate
256  */
257 static unsigned int num_cont_fails;
258
259 /**
260  * Number of times we try overlay connect operations
261  */
262 static unsigned int retry_links;
263
264 /**
265  * Continuous failures during overlay connect operations
266  */
267 static unsigned int cont_fails;
268
269 /**
270  * Global testing status
271  */
272 static int result;
273
274 /**
275  * current state of profiling
276  */
277 enum State state;
278
279 /**
280  * Folder where policy files are stored.
281  */
282 static char * policy_dir;
283
284 /**
285  * Search strings.
286  */
287 static char **search_strings;
288
289 /**
290  * Number of search strings.
291  */
292 static int num_search_strings;
293
294 /**
295  * Number of peers found with search strings.
296  */
297 static unsigned int peers_found;
298
299 /**
300  * Search task identifier
301  */
302 static GNUNET_SCHEDULER_TaskIdentifier search_task;
303
304 /**
305  * Search timeout task identifier.
306  */
307 static GNUNET_SCHEDULER_TaskIdentifier search_timeout_task;
308
309 /**
310  * Search timeout in seconds.
311  */
312 static struct GNUNET_TIME_Relative search_timeout = { 60000 };
313
314 /**
315  * How long do we wait before starting the search?
316  * Default: 1 m.
317  */
318 static struct GNUNET_TIME_Relative search_delay = { 60000 };
319
320
321 /**
322  * Shutdown nicely
323  *
324  * @param cls NULL
325  * @param tc the task context
326  */
327 static void
328 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
329 {
330   struct DLLOperation *dll_op;
331   unsigned int nhost;
332   unsigned int peer_cnt;
333   unsigned int search_str_cnt;
334  
335   for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
336   {
337     if (NULL != peers[peer_cnt].mesh_op_handle)
338       GNUNET_TESTBED_operation_cancel (peers[peer_cnt].mesh_op_handle);
339     if (NULL != peers[peer_cnt].stats_op_handle)
340       GNUNET_TESTBED_operation_cancel (peers[peer_cnt].stats_op_handle);
341   }
342   for (search_str_cnt = 0; search_str_cnt < num_search_strings; search_str_cnt++)
343   {
344     GNUNET_free (search_strings[search_str_cnt]);
345   }
346   GNUNET_free (search_strings);
347   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
348     GNUNET_SCHEDULER_cancel (abort_task);
349   if (GNUNET_SCHEDULER_NO_TASK != register_hosts_task)
350     GNUNET_SCHEDULER_cancel (register_hosts_task);
351   if (NULL != reg_handle)
352     GNUNET_TESTBED_cancel_registration (reg_handle);
353   if (NULL != topology_op)
354     GNUNET_TESTBED_operation_cancel (topology_op);
355   for (nhost = 0; nhost < num_hosts; nhost++)
356     if (NULL != hosts[nhost])
357       GNUNET_TESTBED_host_destroy (hosts[nhost]);
358   GNUNET_free_non_null (hosts);
359   while (NULL != (dll_op = dll_op_head))
360   {
361     GNUNET_TESTBED_operation_cancel (dll_op->op);
362     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
363     GNUNET_free (dll_op);
364   }
365   if (NULL != mc)
366     GNUNET_TESTBED_controller_disconnect (mc);
367   if (NULL != mc_proc)
368     GNUNET_TESTBED_controller_stop (mc_proc);
369   if (NULL != cfg)
370     GNUNET_CONFIGURATION_destroy (cfg);
371
372   GNUNET_SCHEDULER_shutdown (); /* Stop scheduler to shutdown testbed run */
373 }
374
375
376 /**
377  * abort task to run on test timed out
378  *
379  * @param cls NULL
380  * @param tc the task context
381  */
382 static void
383 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
384 {
385   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Aborting\n");
386   abort_task = GNUNET_SCHEDULER_NO_TASK;
387   result = GNUNET_SYSERR;
388   GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
389 }
390
391
392 /******************************************************************************/
393 /*********************  STATISTICS SERVICE CONNECTIONS  ***********************/
394 /******************************************************************************/
395
396 /**
397  * Adapter function called to establish a connection to
398  * statistics service.
399  * 
400  * @param cls closure
401  * @param cfg configuration of the peer to connect to; will be available until
402  *          GNUNET_TESTBED_operation_done() is called on the operation returned
403  *          from GNUNET_TESTBED_service_connect()
404  * @return service handle to return in 'op_result', NULL on error
405  */
406 static void *
407 stats_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
408 {
409   return GNUNET_STATISTICS_create ("<driver>", cfg);
410 }
411
412
413 /**
414  * Adapter function called to destroy a connection to
415  * statistics service.
416  * 
417  * @param cls closure
418  * @param op_result service handle returned from the connect adapter
419  */
420 static void 
421 stats_da (void *cls, void *op_result)
422 {
423   GNUNET_STATISTICS_destroy (op_result, GNUNET_NO);
424 }
425
426
427 /**
428  * Process statistic values.
429  *
430  * @param cls closure
431  * @param subsystem name of subsystem that created the statistic
432  * @param name the name of the datum
433  * @param value the current value
434  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
435  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
436  */
437 static int
438 stats_iterator (void *cls, const char *subsystem, const char *name,
439                 uint64_t value, int is_persistent)
440 {
441   struct RegexPeer *peer = cls;
442   //  char output_buffer[512];
443   //  size_t size;
444
445   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stats iterator callback for peer %u\n", peer->id);
446   /*
447   if (NULL == output_file)
448   {*/
449     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
450                 "%p -> %s [%s]: %llu\n",
451                 peer, subsystem, name, value);
452     return GNUNET_OK;
453     /*
454   }
455   size =
456     GNUNET_snprintf (output_buffer,
457                      sizeof (output_buffer),
458                      "%p [%s] %s %llu\n",
459                      peer,
460                      subsystem, name, value);
461   if (size != GNUNET_DISK_file_write (output_file, output_buffer, size))
462     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
463   */
464   return GNUNET_OK;
465 }
466
467
468 /**
469  * Continuation callback for stats.
470  *
471  * @param cls closure
472  * @param success GNUNET_OK if statistics were
473  *        successfully obtained, GNUNET_SYSERR if not.
474  */
475 static void
476 stats_cont_cb (void *cls, 
477                int success)
478 {
479   struct RegexPeer *peer = cls;
480
481   if (GNUNET_OK != success)
482     return;
483
484   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
485               "Stats continuation callback for peer %u.\n", peer->id);
486
487 }
488
489
490 /**
491  * Function called by testbed once we are connected to stats service.
492  *
493  * @param cls the 'struct RegexPeer' for which we connected to stats
494  * @param op connect operation handle
495  * @param ca_result handle to stats service
496  * @param emsg error message on failure
497  */
498 static void
499 stats_connect_cb (void *cls, 
500                   struct GNUNET_TESTBED_Operation *op,
501                   void *ca_result,
502                   const char *emsg)
503 {
504   struct RegexPeer *peer = cls;
505
506   if (NULL == ca_result)
507   {
508     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
509                 "Failed to connect to statistics service on peer %u: %s\n",
510                 peer->id, emsg);
511     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
512     return;
513   }
514
515   peer->stats_handle = ca_result;
516 }
517
518
519 /******************************************************************************/
520 /************************  MESH SERVICE CONNECTIONS  **************************/
521 /******************************************************************************/
522
523 /**
524  * Method called whenever another peer has added us to a tunnel
525  * the other peer initiated.
526  * Only called (once) upon reception of data with a message type which was
527  * subscribed to in GNUNET_MESH_connect. A call to GNUNET_MESH_tunnel_destroy
528  * causes te tunnel to be ignored and no further notifications are sent about
529  * the same tunnel.
530  *
531  * @param cls closure
532  * @param tunnel new handle to the tunnel
533  * @param initiator peer that started the tunnel
534  * @param atsi performance information for the tunnel
535  * @return initial tunnel context for the tunnel
536  *         (can be NULL -- that's not an error)
537  */
538 void *
539 mesh_inbound_tunnel_handler (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
540                              const struct GNUNET_PeerIdentity *initiator,
541                              const struct GNUNET_ATS_Information *atsi)
542 {
543   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh inbound tunnel handler.\n");
544
545   return NULL;
546 }
547
548
549 /**
550  * Function called whenever an inbound tunnel is destroyed.  Should clean up
551  * any associated state.  This function is NOT called if the client has
552  * explicitly asked for the tunnel to be destroyed using
553  * GNUNET_MESH_tunnel_destroy. It must NOT call GNUNET_MESH_tunnel_destroy on
554  * the tunnel.
555  *
556  * @param cls closure (set from GNUNET_MESH_connect)
557  * @param tunnel connection to the other end (henceforth invalid)
558  * @param tunnel_ctx place where local state associated
559  *                   with the tunnel is stored
560  */
561 void
562 mesh_tunnel_end_handler (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
563                          void *tunnel_ctx)
564 {
565   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh tunnel end handler.\n");
566 }
567
568
569 /**
570  * Method called whenever a peer has disconnected from the tunnel.
571  * Implementations of this callback must NOT call
572  * GNUNET_MESH_tunnel_destroy immediately, but instead schedule those
573  * to run in some other task later.  However, calling
574  * "GNUNET_MESH_notify_transmit_ready_cancel" is allowed.
575  *
576  * @param cls closure
577  * @param peer_id peer identity the tunnel stopped working with
578  */
579 void
580 mesh_peer_disconnect_handler (void *cls,
581                               const struct GNUNET_PeerIdentity * peer_id)
582 {
583   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh peer disconnect handler.\n");
584 }
585
586
587 /**
588  * Method called whenever a peer has connected to the tunnel.
589  *
590  * @param cls closure
591  * @param peer_id peer identity the tunnel was created to, NULL on timeout
592  * @param atsi performance data for the connection
593  *
594  */
595 void
596 mesh_peer_connect_handler (void *cls,
597                            const struct GNUNET_PeerIdentity* peer_id,
598                            const struct GNUNET_ATS_Information * atsi)
599 {
600   struct RegexPeer *peer = cls;
601
602   peers_found++;
603
604   if (NULL == peer_id)
605   {
606     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
607                 "String matching timed out for string %s on peer %u (%i/%i)\n",
608                 peer->search_str, peer->id, peers_found, num_search_strings);
609   }
610   else
611   {
612     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
613                 "String %s successfully matched on peer %u (%i/%i)\n", 
614                 peer->search_str, peer->id, peers_found, num_search_strings);
615
616     GNUNET_STATISTICS_get (peer->stats_handle, "mesh", NULL,
617                            GNUNET_TIME_UNIT_FOREVER_REL,
618                            &stats_cont_cb, 
619                            &stats_iterator, peer);
620   }
621
622   if (peers_found == num_search_strings)
623   {
624
625     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
626     printf ("\nAll strings successfully matched in %.2f minutes\n", ((double)prof_time.rel_value / 1000.0 / 60.0));
627
628     if (GNUNET_SCHEDULER_NO_TASK != search_timeout_task)
629       GNUNET_SCHEDULER_cancel (search_timeout_task);
630     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
631   }
632 }
633
634
635 /**
636  * Connect by string timeout task
637  *
638  * @param cls NULL
639  * @param tc the task context
640  */
641 static void
642 do_connect_by_string_timeout (void *cls,
643                               const struct GNUNET_SCHEDULER_TaskContext * tc)
644 {
645   printf ("Searching for all strings did not succeed after %s.\n", 
646           GNUNET_STRINGS_relative_time_to_string (search_timeout, GNUNET_NO));
647   printf ("Found %i of %i strings\n", peers_found, num_search_strings);
648
649   GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
650 }
651
652
653 /**
654  * Connect by string task that is run to search for a string in the NFA
655  *
656  * @param cls NULL
657  * @param tc the task context
658  */
659 static void
660 do_connect_by_string (void *cls,
661                       const struct GNUNET_SCHEDULER_TaskContext * tc)
662 {
663   unsigned int search_cnt;
664   struct RegexPeer *peer;
665
666   for (search_cnt = 0; search_cnt < num_search_strings; search_cnt++)
667   {
668     peer = &peers[search_cnt % num_peers];
669     peer->search_str = search_strings[search_cnt];
670
671     printf ("Searching for string \"%s\" on peer %d with file %s\n", 
672             peer->search_str, (search_cnt % num_peers), peer->policy_file);
673
674     peer->mesh_tunnel_handle = GNUNET_MESH_tunnel_create (peer->mesh_handle,
675                                                           NULL,
676                                                           &mesh_peer_connect_handler,
677                                                           &mesh_peer_disconnect_handler,
678                                                           peer);
679                                                           
680     GNUNET_MESH_peer_request_connect_by_string (peer->mesh_tunnel_handle,
681                                                 peer->search_str);
682       
683   }
684
685   prof_start_time = GNUNET_TIME_absolute_get ();
686
687   search_timeout_task = GNUNET_SCHEDULER_add_delayed (search_timeout,
688                                                       &do_connect_by_string_timeout, NULL);
689 }
690
691
692 /**
693  * Mesh connect callback.
694  *
695  * @param cls internal peer id.
696  * @param op operation handle.
697  * @param ca_result connect adapter result.
698  * @param emsg error message.
699  */
700 void
701 mesh_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
702                  void *ca_result, const char *emsg)
703 {
704   static unsigned int connected_mesh_handles;
705   struct RegexPeer *peer = (struct RegexPeer *) cls;
706   char *regex;
707   char *data;
708   char *buf;
709   uint64_t filesize;
710   unsigned int offset;
711
712   if (NULL != emsg)
713   {
714     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Mesh connect failed: %s\n", emsg);
715     GNUNET_assert (0);
716   }
717
718   GNUNET_assert (peer->mesh_op_handle == op);
719   GNUNET_assert (peer->mesh_handle == ca_result);
720   GNUNET_assert (NULL != peer->policy_file);
721
722   printf ("Announcing regexes for peer with file %s\n", peer->policy_file);
723   fflush (stdout);
724
725   if (GNUNET_YES != GNUNET_DISK_file_test (peer->policy_file))
726   {
727     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
728                 "Could not find policy file %s\n", peer->policy_file);
729     return;
730   }
731   if (GNUNET_OK != GNUNET_DISK_file_size (peer->policy_file, &filesize, GNUNET_YES, GNUNET_YES))
732     filesize = 0;
733   if (0 == filesize)
734   {
735     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Policy file %s is empty.\n", peer->policy_file);
736     return;
737   }
738   data = GNUNET_malloc (filesize);
739   if (filesize != GNUNET_DISK_fn_read (peer->policy_file, data, filesize))
740   {
741     GNUNET_free (data);
742     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read policy file %s.\n",
743          peer->policy_file);
744     return;
745   }
746   buf = data;
747   offset = 0;
748   regex = NULL;
749   while (offset < (filesize - 1))
750   {
751     offset++;
752     if (((data[offset] == '\n')) && (buf != &data[offset]))
753     {
754       data[offset] = '\0';
755       regex = buf;
756       GNUNET_assert (NULL != regex);
757       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Announcing regex: %s on peer\n", regex);
758       GNUNET_MESH_announce_regex (peer->mesh_handle, regex);
759       buf = &data[offset + 1];
760     }
761     else if ((data[offset] == '\n') || (data[offset] == '\0'))
762       buf = &data[offset + 1];
763   }
764   GNUNET_free (data);
765
766   if (++connected_mesh_handles == num_peers)
767   {
768     printf ("\nAll mesh handles connected.\nWaiting %s before starting to search.\n", 
769             GNUNET_STRINGS_relative_time_to_string (search_delay, GNUNET_YES));
770
771     search_task = GNUNET_SCHEDULER_add_delayed (search_delay,
772                                                 &do_connect_by_string, NULL);
773   }
774 }
775
776
777 /**
778  * Mesh connect adapter.
779  *
780  * @param cls not used.
781  * @param cfg configuration handle.
782  *
783  * @return
784  */
785 void *
786 mesh_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
787 {
788   GNUNET_MESH_ApplicationType app;
789   struct RegexPeer *peer = cls;
790
791   static struct GNUNET_MESH_MessageHandler handlers[] = {
792     {NULL, 0, 0}
793   };
794
795   app = (GNUNET_MESH_ApplicationType)0;
796
797   peer->mesh_handle =
798     GNUNET_MESH_connect (cfg, cls, NULL, NULL, handlers, &app);
799
800   return peer->mesh_handle;
801 }
802
803
804 /**
805  * Adapter function called to destroy a connection to
806  * the mesh service
807  *
808  * @param cls closure
809  * @param op_result service handle returned from the connect adapter
810  */
811 void
812 mesh_da (void *cls, void *op_result)
813 {
814   struct RegexPeer *peer = (struct RegexPeer *) cls;
815
816   GNUNET_assert (peer->mesh_handle == op_result);
817
818   if (NULL != peer->mesh_tunnel_handle)
819   {
820     GNUNET_MESH_tunnel_destroy (peer->mesh_tunnel_handle);
821     peer->mesh_tunnel_handle = NULL;
822   }
823
824   if (NULL != peer->mesh_handle)
825   {
826     GNUNET_MESH_disconnect (peer->mesh_handle);
827     peer->mesh_handle = NULL;
828   }
829 }
830
831
832 /******************************************************************************/
833 /***************************  TESTBED PEER SETUP  *****************************/
834 /******************************************************************************/
835
836
837 /**
838  * Functions of this signature are called when a peer has been successfully
839  * started or stopped.
840  *
841  * @param cls the closure from GNUNET_TESTBED_peer_start/stop()
842  * @param emsg NULL on success; otherwise an error description
843  */
844 static void
845 peer_churn_cb (void *cls, const char *emsg)
846 {
847   struct DLLOperation *dll_op = cls;
848   struct GNUNET_TESTBED_Operation *op;
849   static unsigned int started_peers;
850   unsigned int peer_cnt;
851
852   op = dll_op->op;
853   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
854   GNUNET_free (dll_op);
855   if (NULL != emsg)
856   {
857     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
858          _("An operation has failed while starting peers\n"));
859     GNUNET_TESTBED_operation_done (op);
860     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
861       GNUNET_SCHEDULER_cancel (abort_task);
862     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
863     return;
864   }
865   GNUNET_TESTBED_operation_done (op);
866   if (++started_peers == num_peers)
867   {
868     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
869     printf ("All peers started successfully in %.2f seconds\n",
870             ((double) prof_time.rel_value) / 1000.00);
871     result = GNUNET_OK;
872
873     if (0 == num_links)
874       num_links = num_peers * 5;
875
876     peer_handles = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *) * num_peers);
877     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
878       peer_handles[peer_cnt] = peers[peer_cnt].peer_handle;
879
880     state = STATE_PEERS_LINKING;
881     /* Do overlay connect */
882     prof_start_time = GNUNET_TIME_absolute_get ();
883     topology_op =
884         GNUNET_TESTBED_overlay_configure_topology (NULL, num_peers, peer_handles,
885                                                    GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI,
886                                                    num_links,
887                                                    GNUNET_TESTBED_TOPOLOGY_OPTION_END);
888     if (NULL == topology_op)
889     {
890       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
891                   "Cannot create topology, op handle was NULL\n");
892       GNUNET_assert (0);
893     }
894   }
895 }
896
897
898 /**
899  * Functions of this signature are called when a peer has been successfully
900  * created
901  *
902  * @param cls the closure from GNUNET_TESTBED_peer_create()
903  * @param peer the handle for the created peer; NULL on any error during
904  *          creation
905  * @param emsg NULL if peer is not NULL; else MAY contain the error description
906  */
907 static void
908 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
909 {
910   struct DLLOperation *dll_op = cls;
911   struct RegexPeer *peer_ptr;
912   static unsigned int created_peers;
913   unsigned int peer_cnt;
914
915   if (NULL != emsg)
916   {
917     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
918          _("Creating a peer failed. Error: %s\n"), emsg);
919     GNUNET_TESTBED_operation_done (dll_op->op);
920     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
921     GNUNET_free (dll_op);
922     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
923       GNUNET_SCHEDULER_cancel (abort_task);
924     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
925     return;
926   }
927
928   peer_ptr = dll_op->cls;
929   GNUNET_assert (NULL == peer_ptr->peer_handle);
930   peer_ptr->peer_handle = peer;
931   GNUNET_TESTBED_operation_done (dll_op->op);
932   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
933   GNUNET_free (dll_op);
934
935   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %i created on host %s\n",
936               peer_ptr->id,
937               GNUNET_TESTBED_host_get_hostname (peer_ptr->host_handle));
938
939   if (++created_peers == num_peers)
940   {
941     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
942     printf ("All peers created successfully in %.2f seconds\n",
943             ((double) prof_time.rel_value) / 1000.00);
944     /* Now peers are to be started */
945     state = STATE_PEERS_STARTING;
946     prof_start_time = GNUNET_TIME_absolute_get ();
947     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
948     {
949       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
950       dll_op->op = GNUNET_TESTBED_peer_start (dll_op, peers[peer_cnt].peer_handle,
951                                               &peer_churn_cb, dll_op);
952       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
953     }
954   }
955 }
956
957 /**
958  * Function called with a filename.
959  *
960  * @param cls closure
961  * @param filename complete filename (absolute path)
962  * @return GNUNET_OK to continue to iterate,
963  *  GNUNET_SYSERR to abort iteration with error!
964  */
965 int
966 policy_filename_cb (void *cls, const char *filename)
967 {
968   static unsigned int peer_cnt;
969   struct DLLOperation *dll_op;
970
971   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating peer %i on host %s for policy file %s\n",
972               peer_cnt,
973               GNUNET_TESTBED_host_get_hostname (hosts[peer_cnt % num_hosts]),
974               filename);
975
976   peers[peer_cnt].id = peer_cnt;
977   peers[peer_cnt].policy_file = GNUNET_strdup (filename);
978   peers[peer_cnt].host_handle = hosts[peer_cnt % num_hosts];
979   peers[peer_cnt].mesh_handle = NULL;
980   peers[peer_cnt].mesh_tunnel_handle = NULL;
981   peers[peer_cnt].stats_handle = NULL;
982   peers[peer_cnt].stats_op_handle = NULL;
983
984   dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
985   dll_op->cls = &peers[peer_cnt];
986   dll_op->op = GNUNET_TESTBED_peer_create (mc,
987                                            hosts[peer_cnt % num_hosts],
988                                            cfg,
989                                            &peer_create_cb,
990                                            dll_op);
991   GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
992   peer_cnt++;
993
994   return GNUNET_OK;
995 }
996
997
998 /**
999  * Controller event callback.
1000  *
1001  * @param cls NULL
1002  * @param event the controller event
1003  */
1004 static void
1005 controller_event_cb (void *cls,
1006                      const struct GNUNET_TESTBED_EventInformation *event)
1007 {
1008   struct DLLOperation *dll_op;
1009   struct GNUNET_TESTBED_Operation *op;
1010
1011   switch (state)
1012   {
1013   case STATE_SLAVES_STARTING:
1014     switch (event->type)
1015     {
1016     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1017       {
1018         static unsigned int slaves_started;
1019
1020         dll_op = event->details.operation_finished.op_cls;
1021         GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1022         GNUNET_free (dll_op);
1023         op = event->details.operation_finished.operation;
1024         if (NULL != event->details.operation_finished.emsg)
1025         {
1026           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1027                _("An operation has failed while starting slaves\n"));
1028           GNUNET_TESTBED_operation_done (op);
1029           if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1030             GNUNET_SCHEDULER_cancel (abort_task);
1031           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1032           return;
1033         }
1034         GNUNET_TESTBED_operation_done (op);
1035         /* Proceed to start peers */
1036         if (++slaves_started == num_hosts - 1)
1037         {
1038           printf ("All slaves started successfully\n");
1039
1040           state = STATE_PEERS_CREATING;
1041           prof_start_time = GNUNET_TIME_absolute_get ();
1042
1043           num_peers = GNUNET_DISK_directory_scan (policy_dir,
1044                                                   NULL,
1045                                                   NULL);
1046           peers = GNUNET_malloc (sizeof (struct RegexPeer) * num_peers);
1047
1048           GNUNET_DISK_directory_scan (policy_dir,
1049                                       &policy_filename_cb,
1050                                       NULL);
1051         }
1052       }
1053       break;
1054     default:
1055       GNUNET_assert (0);
1056     }
1057     break;
1058   case STATE_PEERS_STARTING:
1059     switch (event->type)
1060     {
1061     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1062       /* Control reaches here when peer start fails */
1063     case GNUNET_TESTBED_ET_PEER_START:
1064       /* we handle peer starts in peer_churn_cb */
1065       break;
1066     default:
1067       GNUNET_assert (0);
1068     }
1069     break;
1070   case STATE_PEERS_LINKING:
1071    switch (event->type)
1072     {
1073     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1074       /* Control reaches here when a peer linking operation fails */
1075       if (NULL != event->details.operation_finished.emsg)
1076       {
1077         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1078              _("An operation has failed while linking\n"));
1079         retry_links++;
1080         if (++cont_fails > num_cont_fails)
1081         {
1082           printf ("\nAborting due to very high failure rate");
1083           if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1084             GNUNET_SCHEDULER_cancel (abort_task);
1085           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1086         }
1087       }
1088       break;
1089     case GNUNET_TESTBED_ET_CONNECT:
1090       {
1091         static unsigned int established_links;
1092         unsigned int peer_cnt;
1093
1094         if (0 == established_links)
1095           printf ("Establishing links\n .");
1096         else
1097         {
1098           printf (".");
1099           fflush (stdout);
1100         }
1101         if (++established_links == num_links)
1102         {
1103           prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1104           printf ("\n%u links established in %.2f seconds\n",
1105                   num_links, ((double) prof_time.rel_value) / 1000.00);
1106           result = GNUNET_OK;
1107           GNUNET_free (peer_handles);
1108           printf ("\nConnecting to mesh and statistics service...\n");
1109           for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1110           {
1111             peers[peer_cnt].mesh_op_handle = 
1112               GNUNET_TESTBED_service_connect (NULL,
1113                                               peers[peer_cnt].peer_handle,
1114                                               "mesh",
1115                                               &mesh_connect_cb,
1116                                               &peers[peer_cnt],
1117                                               &mesh_ca,
1118                                               &mesh_da,
1119                                               &peers[peer_cnt]);
1120
1121             peers[peer_cnt].stats_op_handle = 
1122               GNUNET_TESTBED_service_connect (NULL,
1123                                               peers[peer_cnt].peer_handle,
1124                                               "statistics",
1125                                               &stats_connect_cb,
1126                                               &peers[peer_cnt],
1127                                               &stats_ca,
1128                                               &stats_da,
1129                                               &peers[peer_cnt]);
1130           }
1131         }
1132       }
1133       break;
1134     default:
1135       GNUNET_assert (0);
1136     }
1137     break;
1138   default:
1139     GNUNET_assert (0);
1140   }
1141 }
1142
1143
1144 /**
1145  * Task to register all hosts available in the global host list
1146  *
1147  * @param cls NULL
1148  * @param tc the scheduler task context
1149  */
1150 static void
1151 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1152
1153
1154 /**
1155  * Callback which will be called to after a host registration succeeded or failed
1156  *
1157  * @param cls the closure
1158  * @param emsg the error message; NULL if host registration is successful
1159  */
1160 static void
1161 host_registration_completion (void *cls, const char *emsg)
1162 {
1163   reg_handle = NULL;
1164   if (NULL != emsg)
1165   {
1166     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1167                 _("Host registration failed for a host. Error: %s\n"), emsg);
1168     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1169       GNUNET_SCHEDULER_cancel (abort_task);
1170     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1171     return;
1172   }
1173   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1174 }
1175
1176
1177 /**
1178  * Task to register all hosts available in the global host list
1179  *
1180  * @param cls NULL
1181  * @param tc the scheduler task context
1182  */
1183 static void
1184 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1185 {
1186   struct DLLOperation *dll_op;
1187   static unsigned int reg_host;
1188   unsigned int slave;
1189
1190   register_hosts_task = GNUNET_SCHEDULER_NO_TASK;
1191   if (reg_host == num_hosts - 1)
1192   {
1193     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1194                 "All hosts successfully registered\n");
1195     /* Start slaves */
1196     state = STATE_SLAVES_STARTING;
1197     for (slave = 1; slave < num_hosts; slave++)
1198     {
1199       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1200       dll_op->op = GNUNET_TESTBED_controller_link (dll_op,
1201                                                    mc,
1202                                                    hosts[slave],
1203                                                    hosts[0],
1204                                                    cfg,
1205                                                    GNUNET_YES);
1206       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1207     }
1208     return;
1209   }
1210   reg_handle = GNUNET_TESTBED_register_host (mc, hosts[++reg_host],
1211                                              host_registration_completion,
1212                                              NULL);
1213 }
1214
1215
1216 /**
1217  * Callback to signal successfull startup of the controller process
1218  *
1219  * @param cls the closure from GNUNET_TESTBED_controller_start()
1220  * @param config the configuration with which the controller has been started;
1221  *          NULL if status is not GNUNET_OK
1222  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
1223  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
1224  */
1225 static void
1226 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
1227 {
1228   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1229     GNUNET_SCHEDULER_cancel (abort_task);
1230   if (GNUNET_OK != status)
1231   {
1232     mc_proc = NULL;
1233     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1234     return;
1235   }
1236   event_mask = 0;
1237   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
1238   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
1239   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1240   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
1241   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
1242   mc = GNUNET_TESTBED_controller_connect (config, hosts[0], event_mask,
1243                                           &controller_event_cb, NULL);
1244   if (NULL == mc)
1245   {
1246     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1247                 _("Unable to connect to master controller -- Check config\n"));
1248     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1249     return;
1250   }
1251   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1252   abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1253                                              &do_abort, NULL);
1254 }
1255
1256 /**
1257  * Load search strings from given filename. One search string per line.
1258  *
1259  * @param filename filename of the file containing the search strings.
1260  * @param strings set of strings loaded from file. Caller needs to free this 
1261  *                if number returned is greater than zero.
1262  * @return number of strings found in the file. GNUNET_SYSERR on error.
1263  */
1264 static int
1265 load_search_strings (const char *filename, char ***strings)
1266 {
1267   char *data;
1268   char *buf;
1269   uint64_t filesize;
1270   unsigned int offset;
1271   int str_cnt;
1272   unsigned int i;
1273
1274   if (NULL == filename)
1275   {
1276     return GNUNET_SYSERR;
1277   }
1278
1279   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
1280   {
1281     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1282                 "Could not find search strings file %s\n", filename);
1283     return GNUNET_SYSERR;
1284   }
1285   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &filesize, GNUNET_YES, GNUNET_YES))
1286     filesize = 0;
1287   if (0 == filesize)
1288   {
1289     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Search strings file %s is empty.\n", filename);
1290     return GNUNET_SYSERR;
1291   }
1292   data = GNUNET_malloc (filesize);
1293   if (filesize != GNUNET_DISK_fn_read (filename, data, filesize))
1294   {
1295     GNUNET_free (data);
1296     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read search strings file %s.\n",
1297          filename);
1298     return GNUNET_SYSERR;
1299   }
1300   buf = data;
1301   offset = 0;
1302   str_cnt = 0;
1303   while (offset < (filesize - 1))
1304   {
1305     offset++;
1306     if (((data[offset] == '\n')) && (buf != &data[offset]))
1307     {
1308       data[offset] = '\0';
1309       str_cnt++;
1310       buf = &data[offset + 1];
1311     }
1312     else if ((data[offset] == '\n') || (data[offset] == '\0'))
1313       buf = &data[offset + 1];
1314   }
1315   *strings = GNUNET_malloc (sizeof (char *) * str_cnt);
1316   offset = 0;
1317   for (i = 0; i < str_cnt; i++)
1318   {
1319     (*strings)[i] = GNUNET_strdup (&data[offset]);
1320     offset += strlen ((*strings)[i]) + 1;
1321   }
1322   free (data);
1323   return str_cnt;
1324 }
1325
1326 /**
1327  * Main function that will be run by the scheduler.
1328  *
1329  * @param cls closure
1330  * @param args remaining command-line arguments
1331  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1332  * @param config configuration
1333  */
1334 static void
1335 run (void *cls, char *const *args, const char *cfgfile,
1336      const struct GNUNET_CONFIGURATION_Handle *config)
1337 {
1338   unsigned int nhost;
1339
1340   if (NULL == args[0])
1341   {
1342     fprintf (stderr, _("No hosts-file specified on command line. Exiting.\n"));
1343     return;
1344   }
1345   if (NULL == args[1])
1346   {
1347     fprintf (stderr, _("No policy directory specified on command line. Exiting.\n"));
1348     return;
1349   }
1350   num_hosts = GNUNET_TESTBED_hosts_load_from_file (args[0], &hosts);
1351   if (0 == num_hosts)
1352   {
1353     fprintf (stderr, _("No hosts loaded. Need at least one host\n"));
1354     return;
1355   }
1356   for (nhost = 0; nhost < num_hosts; nhost++)
1357   {
1358     if (GNUNET_YES != GNUNET_TESTBED_is_host_habitable (hosts[nhost]))
1359     {
1360       fprintf (stderr, _("Host %s cannot start testbed\n"),
1361                          GNUNET_TESTBED_host_get_hostname (hosts[nhost]));
1362       break;
1363     }
1364   }
1365   if (num_hosts != nhost)
1366   {
1367     fprintf (stderr, _("Exiting\n"));
1368     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1369     return;
1370   }
1371   if (NULL == config)
1372   {
1373     fprintf (stderr, _("No configuration file given. Exiting\n"));
1374     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1375     return;
1376   }
1377   if (GNUNET_YES != GNUNET_DISK_directory_test (args[1]))
1378   {
1379     fprintf (stderr, _("Specified policies directory does not exist. Exiting.\n"));
1380     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1381     return;
1382   }
1383   policy_dir = args[1];
1384   if (GNUNET_YES != GNUNET_DISK_file_test (args[2]))
1385   {
1386     fprintf (stderr, _("No search strings file given. Exiting.\n"));
1387     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1388     return;
1389   }
1390   num_search_strings = load_search_strings (args[2], &search_strings);
1391   if (0 >= num_search_strings || NULL == search_strings)
1392   {
1393     fprintf (stderr, _("Error loading search strings. Exiting.\n"));
1394     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1395     return;
1396   }
1397   unsigned int i;
1398   for (i = 0; i < num_search_strings; i++)
1399     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "search string: %s\n", search_strings[i]);
1400   cfg = GNUNET_CONFIGURATION_dup (config);
1401   mc_proc =
1402       GNUNET_TESTBED_controller_start (GNUNET_TESTBED_host_get_hostname
1403                                        (hosts[0]),
1404                                        hosts[0],
1405                                        cfg,
1406                                        status_cb,
1407                                        NULL);
1408   abort_task =
1409       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
1410                                     (GNUNET_TIME_UNIT_SECONDS, 5), &do_abort,
1411                                     NULL);
1412 }
1413
1414
1415 /**
1416  * Main function.
1417  *
1418  * @param argc argument count
1419  * @param argv argument values
1420  * @return 0 on success
1421  */
1422 int
1423 main (int argc, char *const *argv)
1424 {
1425   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1426     { 'n', "num-links", "COUNT",
1427       gettext_noop ("create COUNT number of random links"),
1428       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_links },
1429     { 'e', "num-errors", "COUNT",
1430       gettext_noop ("tolerate COUNT number of continious timeout failures"),
1431       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_cont_fails },
1432     { 't', "matching-timeout", "TIMEOUT",
1433       gettext_noop ("wait TIMEOUT seconds before considering a string match as failed"),
1434       GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &search_timeout },
1435     { 's', "search-delay", "DELAY",
1436       gettext_noop ("wait DELAY minutes before starting string search"),
1437       GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &search_delay },
1438     GNUNET_GETOPT_OPTION_END
1439   };
1440   int ret;
1441
1442   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1443     return 2;
1444
1445   result = GNUNET_SYSERR;
1446   ret =
1447       GNUNET_PROGRAM_run (argc, argv, "gnunet-regex-profiler [OPTIONS] hosts-file policy-dir search-strings-file",
1448                           _("Profiler for regex/mesh"),
1449                           options, &run, NULL);
1450   if (GNUNET_OK != ret)
1451     return ret;
1452   if (GNUNET_OK != result)
1453     return 1;
1454   return 0;
1455 }