Assosiate a configuration template while creating hosts. This paves a way to
[oweals/gnunet.git] / src / regex / gnunet-regex-profiler.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 - 2013 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 regex/gnunet-regex-profiler.c
23  * @brief Regex profiler for testing distributed regex use.
24  * @author Bartlomiej Polot
25  * @author Maximilian Szengel
26  *
27  */
28
29 #include <string.h>
30
31 #include "platform.h"
32 #include "gnunet_applications.h"
33 #include "gnunet_util_lib.h"
34 #include "gnunet_regex_lib.h"
35 #include "gnunet_arm_service.h"
36 #include "gnunet_dht_service.h"
37 #include "gnunet_testbed_service.h"
38
39 #define FIND_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
40 #define SEARCHES_IN_PARALLEL 10
41
42 /**
43  * DLL of operations
44  */
45 struct DLLOperation
46 {
47   /**
48    * The testbed operation handle
49    */
50   struct GNUNET_TESTBED_Operation *op;
51
52   /**
53    * Closure
54    */
55   void *cls;
56
57   /**
58    * The next pointer for DLL
59    */
60   struct DLLOperation *next;
61
62   /**
63    * The prev pointer for DLL
64    */
65   struct DLLOperation *prev;
66 };
67
68
69 /**
70  * Available states during profiling
71  */
72 enum State
73 {
74   /**
75    * Initial state
76    */
77   STATE_INIT = 0,
78
79   /**
80    * Starting slaves
81    */
82   STATE_SLAVES_STARTING,
83
84   /**
85    * Creating peers
86    */
87   STATE_PEERS_CREATING,
88
89   /**
90    * Starting peers
91    */
92   STATE_PEERS_STARTING,
93
94   /**
95    * Linking peers
96    */
97   STATE_PEERS_LINKING,
98
99   /**
100    * Matching strings against announced regexes
101    */
102   STATE_SEARCH_REGEX,
103
104   /**
105    * Destroying peers; we can do this as the controller takes care of stopping a
106    * peer if it is running
107    */
108   STATE_PEERS_DESTROYING
109 };
110
111
112 /**
113  * Peer handles.
114  */
115 struct RegexPeer
116 {
117   /**
118    * Peer id.
119    */
120   unsigned int id;
121
122   /**
123    * Peer configuration handle.
124    */
125   struct GNUNET_CONFIGURATION_Handle *cfg;
126
127   /**
128    * The actual testbed peer handle.
129    */
130   struct GNUNET_TESTBED_Peer *peer_handle;
131
132   /**
133    * Host on which the peer is running.
134    */
135   struct GNUNET_TESTBED_Host *host_handle;
136
137   /**
138    * Filename of the peer's policy file.
139    */
140   char *policy_file;
141
142   /**
143    * Peers search string.
144    */
145   const char *search_str;
146
147   /**
148    * Set to GNUNET_YES if the peer successfully matched the above
149    * search string. GNUNET_NO if the string could not be matched
150    * during the profiler run. GNUNET_SYSERR if the string matching
151    * timed out. Undefined if search_str is NULL
152    */
153   int search_str_matched;
154
155   /**
156    * Peer's ARM handle.
157    */
158   struct GNUNET_ARM_Handle *arm_handle;
159
160   /**
161    * Peer's DHT handle.
162    */
163   struct GNUNET_DHT_Handle *dht_handle;
164
165   /**
166    * Handle to a running regex search.
167    */
168    struct GNUNET_REGEX_search_handle *search_handle;
169
170   /**
171    * Testbed operation handle for the ARM and DHT services.
172    */
173   struct GNUNET_TESTBED_Operation *op_handle;
174
175   /**
176    * Peers's statistics handle.
177    */
178   struct GNUNET_STATISTICS_Handle *stats_handle;
179
180   /**
181    * Testbed operation handle for the statistics service.
182    */
183   struct GNUNET_TESTBED_Operation *stats_op_handle;
184
185   /**
186    * The starting time of a profiling step.
187    */
188   struct GNUNET_TIME_Absolute prof_start_time;
189
190   /**
191    * Operation timeout
192    */
193   GNUNET_SCHEDULER_TaskIdentifier timeout;
194 };
195
196
197 /**
198  * An array of hosts loaded from the hostkeys file
199  */
200 static struct GNUNET_TESTBED_Host **hosts;
201
202 /**
203  * Array of peer handles used to pass to
204  * GNUNET_TESTBED_overlay_configure_topology
205  */
206 static struct GNUNET_TESTBED_Peer **peer_handles;
207
208 /**
209  * The array of peers; we fill this as the peers are given to us by the testbed
210  */
211 static struct RegexPeer *peers;
212
213 /**
214  * Host registration handle
215  */
216 static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
217
218 /**
219  * Handle to the master controller process
220  */
221 static struct GNUNET_TESTBED_ControllerProc *mc_proc;
222
223 /**
224  * Handle to the master controller
225  */
226 static struct GNUNET_TESTBED_Controller *mc;
227
228 /**
229  * Handle to global configuration
230  */
231 static struct GNUNET_CONFIGURATION_Handle *cfg;
232
233 /**
234  * Head of the operations list
235  */
236 static struct DLLOperation *dll_op_head;
237
238 /**
239  * Tail of the operations list
240  */
241 static struct DLLOperation *dll_op_tail;
242
243 /**
244  * Peer linking - topology operation
245  */
246 static struct GNUNET_TESTBED_Operation *topology_op;
247
248 /**
249  * The handle for whether a host is habitable or not
250  */
251 struct GNUNET_TESTBED_HostHabitableCheckHandle **hc_handles;
252
253 /**
254  * Abort task identifier
255  */
256 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
257
258 /**
259  * Shutdown task identifier
260  */
261 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
262
263 /**
264  * Host registration task identifier
265  */
266 static GNUNET_SCHEDULER_TaskIdentifier register_hosts_task;
267
268 /**
269  * Global event mask for all testbed events
270  */
271 static uint64_t event_mask;
272
273 /**
274  * The starting time of a profiling step
275  */
276 static struct GNUNET_TIME_Absolute prof_start_time;
277
278 /**
279  * Duration profiling step has taken
280  */
281 static struct GNUNET_TIME_Relative prof_time;
282
283 /**
284  * Number of peers to be started by the profiler
285  */
286 static unsigned int num_peers;
287
288 /**
289  * Number of hosts in the hosts array
290  */
291 static unsigned int num_hosts;
292
293 /**
294  * Factor of number of links. num_links = num_peers * linking_factor.
295  */
296 static unsigned int linking_factor;
297
298 /**
299  * Number of random links to be established between peers
300  */
301 static unsigned int num_links;
302
303 /**
304  * Number of times we try overlay connect operations
305  */
306 static unsigned int retry_links;
307
308 /**
309  * Continuous failures during overlay connect operations
310  */
311 static unsigned int cont_fails;
312
313 /**
314  * Global testing status
315  */
316 static int result;
317
318 /**
319  * current state of profiling
320  */
321 enum State state;
322
323 /**
324  * Folder where policy files are stored.
325  */
326 static char * policy_dir;
327
328 /**
329  * Search strings.
330  */
331 static char **search_strings;
332
333 /**
334  * Number of search strings.
335  */
336 static int num_search_strings;
337
338 /**
339  * Index of peer/string search.
340  */
341 static unsigned int peer_cnt;
342
343 /**
344  * Number of peers found with search strings.
345  */
346 static unsigned int peers_found;
347
348 /**
349  * Search task identifier
350  */
351 static GNUNET_SCHEDULER_TaskIdentifier search_task;
352
353 /**
354  * Search timeout task identifier.
355  */
356 static GNUNET_SCHEDULER_TaskIdentifier search_timeout_task;
357
358 /**
359  * Search timeout in seconds.
360  */
361 static struct GNUNET_TIME_Relative search_timeout = { 60000 };
362
363 /**
364  * How long do we wait before starting the search?
365  * Default: 1 m.
366  */
367 static struct GNUNET_TIME_Relative search_delay = { 60000 };
368
369 /**
370  * File to log statistics to.
371  */
372 static struct GNUNET_DISK_FileHandle *data_file;
373
374 /**
375  * Filename to log statistics to.
376  */
377 static char *data_filename;
378
379 /**
380  * Maximal path compression length.
381  */
382 static unsigned int max_path_compression;
383
384 /**
385  * Prefix used for regex announcing. We need to prefix the search
386  * strings with it, in order to find something.
387  */
388 static char * regex_prefix;
389
390
391 /******************************************************************************/
392 /******************************  DECLARATIONS  ********************************/
393 /******************************************************************************/
394
395
396 /**
397  * Search callback function.
398  *
399  * @param cls Closure provided in GNUNET_REGEX_search.
400  * @param id Peer providing a regex that matches the string.
401  * @param get_path Path of the get request.
402  * @param get_path_length Lenght of get_path.
403  * @param put_path Path of the put request.
404  * @param put_path_length Length of the put_path.
405  */
406 static void
407 regex_found_handler (void *cls,
408                      const struct GNUNET_PeerIdentity *id,
409                      const struct GNUNET_PeerIdentity *get_path,
410                      unsigned int get_path_length,
411                      const struct GNUNET_PeerIdentity *put_path,
412                      unsigned int put_path_length);
413
414
415 /**
416  * DHT connect callback.
417  *
418  * @param cls internal peer id.
419  * @param op operation handle.
420  * @param ca_result connect adapter result.
421  * @param emsg error message.
422  */
423 static void
424 dht_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
425                 void *ca_result, const char *emsg);
426
427 /**
428  * DHT connect adapter.
429  *
430  * @param cls not used.
431  * @param cfg configuration handle.
432  *
433  * @return
434  */
435 static void *
436 dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg);
437
438
439 /**
440  * Adapter function called to destroy a connection to
441  * the DHT service
442  *
443  * @param cls closure
444  * @param op_result service handle returned from the connect adapter
445  */
446 static void
447 dht_da (void *cls, void *op_result);
448
449
450 /**
451  * Function called by testbed once we are connected to stats
452  * service. Get the statistics for the services of interest.
453  *
454  * @param cls the 'struct RegexPeer' for which we connected to stats
455  * @param op connect operation handle
456  * @param ca_result handle to stats service
457  * @param emsg error message on failure
458  */
459 static void
460 stats_connect_cb (void *cls,
461                   struct GNUNET_TESTBED_Operation *op,
462                   void *ca_result,
463                   const char *emsg);
464
465
466 /**
467  * Task to collect all statistics from all peers, will shutdown the
468  * profiler, when done.
469  *
470  * @param cls NULL
471  * @param tc the task context
472  */
473 static void
474 do_collect_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
475
476
477 /******************************************************************************/
478 /********************************  SHUTDOWN  **********************************/
479 /******************************************************************************/
480
481
482 /**
483  * Shutdown nicely
484  *
485  * @param cls NULL
486  * @param tc the task context
487  */
488 static void
489 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
490 {
491   struct DLLOperation *dll_op;
492   struct RegexPeer *peer;
493   unsigned int nhost;
494   unsigned int peer_cnt;
495   unsigned int search_str_cnt;
496   char output_buffer[512];
497   size_t size;
498
499   shutdown_task = GNUNET_SCHEDULER_NO_TASK;
500   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
501     GNUNET_SCHEDULER_cancel (abort_task);
502   if (NULL != hc_handles)
503   {
504     for (nhost = 0; nhost < num_hosts; nhost++)
505       if (NULL != hc_handles[nhost])
506         GNUNET_TESTBED_is_host_habitable_cancel (hc_handles[nhost]);
507     GNUNET_free (hc_handles);
508     hc_handles = NULL;
509   }
510   if (GNUNET_SCHEDULER_NO_TASK != register_hosts_task)
511     GNUNET_SCHEDULER_cancel (register_hosts_task);
512
513   for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
514   {
515     peer = &peers[peer_cnt];
516
517     if (GNUNET_YES != peer->search_str_matched && NULL != data_file)
518     {
519       prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time);
520       size =
521         GNUNET_snprintf (output_buffer,
522                          sizeof (output_buffer),
523                          "%p Search string not found: %s (%d)\n%p On peer: %u (%p)\n%p With policy file: %s\n%p After: %s\n",
524                          peer, peer->search_str, peer->search_str_matched,
525                          peer, peer->id, peer,
526                          peer, peer->policy_file,
527                          peer,
528                          GNUNET_STRINGS_relative_time_to_string (prof_time,
529                                                                  GNUNET_NO));
530       if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
531         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
532     }
533
534     if (NULL != peers[peer_cnt].op_handle)
535       GNUNET_TESTBED_operation_done (peers[peer_cnt].op_handle);
536     if (NULL != peers[peer_cnt].stats_op_handle)
537       GNUNET_TESTBED_operation_done (peers[peer_cnt].stats_op_handle);
538   }
539
540   if (NULL != data_file)
541     GNUNET_DISK_file_close (data_file);
542
543   for (search_str_cnt = 0;
544        search_str_cnt < num_search_strings && NULL != search_strings;
545        search_str_cnt++)
546   {
547     GNUNET_free_non_null (search_strings[search_str_cnt]);
548   }
549   GNUNET_free_non_null (search_strings);
550
551   if (NULL != reg_handle)
552     GNUNET_TESTBED_cancel_registration (reg_handle);
553   if (NULL != topology_op)
554     GNUNET_TESTBED_operation_done (topology_op);
555   for (nhost = 0; nhost < num_hosts; nhost++)
556     if (NULL != hosts[nhost])
557       GNUNET_TESTBED_host_destroy (hosts[nhost]);
558   GNUNET_free_non_null (hosts);
559
560   while (NULL != (dll_op = dll_op_head))
561   {
562     GNUNET_TESTBED_operation_done (dll_op->op);
563     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
564     GNUNET_free (dll_op);
565   }
566   if (NULL != mc)
567     GNUNET_TESTBED_controller_disconnect (mc);
568   if (NULL != mc_proc)
569     GNUNET_TESTBED_controller_stop (mc_proc);
570   if (NULL != cfg)
571     GNUNET_CONFIGURATION_destroy (cfg);
572
573   GNUNET_SCHEDULER_shutdown (); /* Stop scheduler to shutdown testbed run */
574 }
575
576
577 /**
578  * abort task to run on test timed out
579  *
580  * @param cls NULL
581  * @param tc the task context
582  */
583 static void
584 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
585 {
586   unsigned long i = (unsigned long) cls;
587
588   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Aborting %lu...\n", i);
589   abort_task = GNUNET_SCHEDULER_NO_TASK;
590   result = GNUNET_SYSERR;
591   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
592     GNUNET_SCHEDULER_cancel (shutdown_task);
593   shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
594 }
595
596
597 /******************************************************************************/
598 /*********************  STATISTICS SERVICE CONNECTIONS  ***********************/
599 /******************************************************************************/
600
601 /**
602  * Adapter function called to establish a connection to
603  * statistics service.
604  *
605  * @param cls closure
606  * @param cfg configuration of the peer to connect to; will be available until
607  *          GNUNET_TESTBED_operation_done() is called on the operation returned
608  *          from GNUNET_TESTBED_service_connect()
609  * @return service handle to return in 'op_result', NULL on error
610  */
611 static void *
612 stats_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
613 {
614   return GNUNET_STATISTICS_create ("<driver>", cfg);
615 }
616
617
618 /**
619  * Adapter function called to destroy a connection to
620  * statistics service.
621  *
622  * @param cls closure
623  * @param op_result service handle returned from the connect adapter
624  */
625 static void
626 stats_da (void *cls, void *op_result)
627 {
628   struct RegexPeer *peer = cls;
629
630   GNUNET_assert (op_result == peer->stats_handle);
631
632   GNUNET_STATISTICS_destroy (peer->stats_handle, GNUNET_NO);
633   peer->stats_handle = NULL;
634 }
635
636
637 /**
638  * Process statistic values. Write all values to global 'data_file', if present.
639  *
640  * @param cls closure
641  * @param subsystem name of subsystem that created the statistic
642  * @param name the name of the datum
643  * @param value the current value
644  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
645  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
646  */
647 static int
648 stats_iterator (void *cls, const char *subsystem, const char *name,
649                 uint64_t value, int is_persistent)
650 {
651   struct RegexPeer *peer = cls;
652   char output_buffer[512];
653   size_t size;
654
655   if (NULL == data_file)
656   {
657     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
658                 "%p -> %s [%s]: %llu\n",
659                 peer, subsystem, name, value);
660     return GNUNET_OK;
661   }
662   size =
663     GNUNET_snprintf (output_buffer,
664                      sizeof (output_buffer),
665                      "%p [%s] %llu %s\n",
666                      peer,
667                      subsystem, value, name);
668   if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
669     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
670
671   return GNUNET_OK;
672 }
673
674
675 /**
676  * Stats callback. Finish the stats testbed operation and when all stats have
677  * been iterated, shutdown the profiler.
678  *
679  * @param cls closure
680  * @param success GNUNET_OK if statistics were
681  *        successfully obtained, GNUNET_SYSERR if not.
682  */
683 static void
684 stats_cb (void *cls,
685           int success)
686 {
687   static unsigned int peer_cnt;
688   struct RegexPeer *peer = cls;
689
690   if (GNUNET_OK != success)
691   {
692     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
693                 "Getting statistics for peer %u failed!\n",
694                 peer->id);
695     return;
696   }
697
698   GNUNET_assert (NULL != peer->stats_op_handle);
699
700   GNUNET_TESTBED_operation_done (peer->stats_op_handle);
701   peer->stats_op_handle = NULL;
702
703   peer_cnt++;
704   peer = &peers[peer_cnt];
705
706   if (peer_cnt == num_peers)
707   {
708     struct GNUNET_TIME_Relative delay = { 100 };
709     shutdown_task = GNUNET_SCHEDULER_add_delayed (delay, &do_shutdown, NULL);
710   }
711   else
712   {
713     peer->stats_op_handle =
714       GNUNET_TESTBED_service_connect (NULL,
715                                       peer->peer_handle,
716                                       "statistics",
717                                       &stats_connect_cb,
718                                       peer,
719                                       &stats_ca,
720                                       &stats_da,
721                                       peer);
722   }
723 }
724
725
726 /**
727  * Function called by testbed once we are connected to stats
728  * service. Get the statistics for the services of interest.
729  *
730  * @param cls the 'struct RegexPeer' for which we connected to stats
731  * @param op connect operation handle
732  * @param ca_result handle to stats service
733  * @param emsg error message on failure
734  */
735 static void
736 stats_connect_cb (void *cls,
737                   struct GNUNET_TESTBED_Operation *op,
738                   void *ca_result,
739                   const char *emsg)
740 {
741   struct RegexPeer *peer = cls;
742
743   if (NULL == ca_result || NULL != emsg)
744   {
745     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
746                 "Failed to connect to statistics service on peer %u: %s\n",
747                 peer->id, emsg);
748
749     peer->stats_handle = NULL;
750     return;
751   }
752
753   peer->stats_handle = ca_result;
754
755   if (NULL == GNUNET_STATISTICS_get (peer->stats_handle, NULL, NULL,
756                                      GNUNET_TIME_UNIT_FOREVER_REL,
757                                      &stats_cb,
758                                      &stats_iterator, peer))
759   {
760     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
761                 "Could not get statistics of peer %u!\n", peer->id);
762   }
763 }
764
765
766 /**
767  * Task to collect all statistics from all peers, will shutdown the
768  * profiler, when done.
769  *
770  * @param cls NULL
771  * @param tc the task context
772  */
773 static void
774 do_collect_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
775 {
776   struct RegexPeer *peer = &peers[0];
777
778   GNUNET_assert (NULL != peer->peer_handle);
779
780   peer->stats_op_handle =
781     GNUNET_TESTBED_service_connect (NULL,
782                                     peer->peer_handle,
783                                     "statistics",
784                                     &stats_connect_cb,
785                                     peer,
786                                     &stats_ca,
787                                     &stats_da,
788                                     peer);
789 }
790
791
792 /******************************************************************************/
793 /************************   REGEX FIND CONNECTIONS   **************************/
794 /******************************************************************************/
795
796
797 /**
798  * Start searching for the next string in the DHT.
799  *
800  * @param cls Index of the next peer in the peers array.
801  * @param tc TaskContext.
802  */
803 static void
804 find_next_string (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
805
806
807 /**
808  * Method called when we've found a peer that announced a regex
809  * that matches our search string. Now get the statistics.
810  *
811  * @param cls Closure provided in GNUNET_REGEX_search.
812  * @param id Peer providing a regex that matches the string.
813  * @param get_path Path of the get request.
814  * @param get_path_length Lenght of get_path.
815  * @param put_path Path of the put request.
816  * @param put_path_length Length of the put_path.
817  */
818 static void
819 regex_found_handler (void *cls,
820                      const struct GNUNET_PeerIdentity *id,
821                      const struct GNUNET_PeerIdentity *get_path,
822                      unsigned int get_path_length,
823                      const struct GNUNET_PeerIdentity *put_path,
824                      unsigned int put_path_length)
825 {
826   struct RegexPeer *peer = cls;
827   char output_buffer[512];
828   size_t size;
829
830   if (GNUNET_YES == peer->search_str_matched)
831   {
832     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
833                 "String %s on peer %u already matched!\n",
834                 peer->search_str, peer->id);
835     return;
836   }
837
838   peers_found++;
839
840   if (GNUNET_SCHEDULER_NO_TASK != peer->timeout)
841   {
842     GNUNET_SCHEDULER_cancel (peer->timeout);
843     peer->timeout = GNUNET_SCHEDULER_NO_TASK;
844     GNUNET_SCHEDULER_add_now (&find_next_string, NULL);
845   }
846
847   if (NULL == id)
848   {
849     // FIXME not possible right now
850     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
851                 "String matching timed out for string %s on peer %u (%i/%i)\n",
852                 peer->search_str, peer->id, peers_found, num_search_strings);
853
854     printf ("String matching timed out for string %s on peer %u (%i/%i)\n",
855             peer->search_str, peer->id, peers_found, num_search_strings);
856
857     peer->search_str_matched = GNUNET_SYSERR;
858   }
859   else
860   {
861     prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time);
862     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
863                 "String %s successfully matched on peer %u after %s (%i/%i)\n",
864                 peer->search_str, peer->id, GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
865                 peers_found, num_search_strings);
866
867     printf ("String %s successfully matched on peer %u after %s (%i/%i)\n",
868             peer->search_str, peer->id, GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
869             peers_found, num_search_strings);
870     fflush (stdout);
871
872     peer->search_str_matched = GNUNET_YES;
873
874     if (NULL != data_file)
875     {
876       size =
877         GNUNET_snprintf (output_buffer,
878                          sizeof (output_buffer),
879                          "%p Peer: %u\n%p Host: %s\n%p Policy file: %s\n"
880                          "%p Search string: %s\n%p Search duration: %s\n\n",
881                          peer, peer->id,
882                          peer,
883                          GNUNET_TESTBED_host_get_hostname (peer->host_handle),
884                          peer, peer->policy_file,
885                          peer, peer->search_str,
886                          peer,
887                          GNUNET_STRINGS_relative_time_to_string (prof_time,
888                                                                  GNUNET_NO));
889
890       if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
891         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
892     }
893   }
894
895   GNUNET_TESTBED_operation_done (peer->op_handle);
896   peer->op_handle = NULL;
897
898   if (peers_found == num_search_strings)
899   {
900     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
901     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
902                 "All strings successfully matched in %s\n",
903                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
904     printf ("All strings successfully matched.\n");
905     fflush (stdout);
906
907     if (GNUNET_SCHEDULER_NO_TASK != search_timeout_task)
908       GNUNET_SCHEDULER_cancel (search_timeout_task);
909
910     printf ("Collecting stats and shutting down.\n");
911     GNUNET_SCHEDULER_add_now (&do_collect_stats, NULL);
912   }
913 }
914
915
916 /**
917  * Connect by string timeout task. This will cancel the profiler after the
918  * specified timeout 'search_timeout'.
919  *
920  * @param cls NULL
921  * @param tc the task context
922  */
923 static void
924 do_connect_by_string_timeout (void *cls,
925                               const struct GNUNET_SCHEDULER_TaskContext * tc)
926 {
927   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
928               "Finding matches to all strings did not succeed after %s.\n",
929               GNUNET_STRINGS_relative_time_to_string (search_timeout, GNUNET_NO));
930   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
931               "Found %i of %i strings\n", peers_found, num_search_strings);
932
933   printf ("Search timed out after %s. Collecting stats and shutting down.\n", 
934           GNUNET_STRINGS_relative_time_to_string (search_timeout, GNUNET_NO));
935   fflush (stdout);
936
937   GNUNET_SCHEDULER_add_now (&do_collect_stats, NULL);
938 }
939
940
941 /**
942  * Connect by string task that is run to search for a string in the
943  * NFA. It first connects to the mesh service and when a connection is
944  * established it starts to search for the string.
945  *
946  * @param cls NULL
947  * @param tc the task context
948  */
949 static void
950 do_connect_by_string (void *cls,
951                       const struct GNUNET_SCHEDULER_TaskContext * tc)
952 {
953   unsigned int i;
954
955   printf ("Starting string search.\n");
956   fflush (stdout);
957
958   search_timeout_task = GNUNET_SCHEDULER_add_delayed (search_timeout,
959                                                       &do_connect_by_string_timeout, NULL);
960   for (i = 0; i < SEARCHES_IN_PARALLEL; i++)
961     GNUNET_SCHEDULER_add_now (&find_next_string, NULL);
962 }
963
964
965 /**
966  * Search timed out. It might still complete in the future,
967  * but we should start another one.
968  *
969  * @param cls Index of the next peer in the peers array.
970  * @param tc TaskContext.
971  */
972 static void
973 find_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
974 {
975   struct RegexPeer *p = cls;
976
977   p->timeout = GNUNET_SCHEDULER_NO_TASK;
978
979   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
980     return;
981   GNUNET_SCHEDULER_add_now (&find_next_string, NULL);
982 }
983
984
985 /**
986  * Start searching for the next string in the DHT.
987  *
988  * @param cls Index of the next peer in the peers array.
989  * @param tc TaskContext.
990  */
991 static void
992 find_next_string (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
993 {
994   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) ||
995       peer_cnt >= (num_search_strings - 1))
996     return;
997
998   peers[peer_cnt].search_str = search_strings[peer_cnt];
999   peers[peer_cnt].search_str_matched = GNUNET_NO;
1000   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1001               "Searching for string \"%s\" on peer %d with file %s\n",
1002               peers[peer_cnt].search_str,
1003               peer_cnt,
1004               peers[peer_cnt].policy_file);
1005
1006   peers[peer_cnt].op_handle =
1007     GNUNET_TESTBED_service_connect (NULL,
1008                                     peers[peer_cnt].peer_handle,
1009                                     "dht",
1010                                     &dht_connect_cb,
1011                                     &peers[peer_cnt],
1012                                     &dht_ca,
1013                                     &dht_da,
1014                                     &peers[peer_cnt]);
1015   peers[peer_cnt].timeout = GNUNET_SCHEDULER_add_delayed (FIND_TIMEOUT,
1016                                                           &find_timeout,
1017                                                           &peers[peer_cnt]);
1018   peer_cnt++;
1019 }
1020
1021
1022 /**
1023  * ARM connect adapter. Opens a connection to the ARM service.
1024  *
1025  * @param cls Closure (peer).
1026  * @param cfg Configuration handle.
1027  *
1028  * @return
1029  */
1030 static void *
1031 arm_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1032 {
1033   struct RegexPeer *peer = cls;
1034
1035   peer->arm_handle = GNUNET_ARM_connect (cfg, NULL);
1036
1037   return peer->arm_handle;
1038 }
1039
1040
1041 /**
1042  * Adapter function called to destroy a connection to the ARM service.
1043  *
1044  * @param cls Closure (peer).
1045  * @param op_result Service handle returned from the connect adapter.
1046  */
1047 static void
1048 arm_da (void *cls, void *op_result)
1049 {
1050   struct RegexPeer *peer = (struct RegexPeer *) cls;
1051
1052   GNUNET_assert (peer->arm_handle == op_result);
1053
1054   if (NULL != peer->arm_handle)
1055   {
1056     GNUNET_ARM_disconnect (peer->arm_handle);
1057     peer->arm_handle = NULL;
1058   }
1059 }
1060
1061
1062 /**
1063  * Start announcing the next regex in the DHT.
1064  *
1065  * @param cls Index of the next peer in the peers array.
1066  * @param tc TaskContext.
1067  */
1068 void
1069 announce_next_regex (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1070
1071
1072 /**
1073  * Callback function invoked when ARM peration is complete: deamon is started.
1074  *
1075  * @param cls Closure (RegexPeer).
1076  * @param result Outcome of the operation.
1077  */
1078 static void
1079 arm_start_cb (void *cls, enum GNUNET_ARM_ProcessStatus result)
1080 {
1081   struct RegexPeer *peer = (struct RegexPeer *) cls;
1082   static unsigned int peer_cnt;
1083   unsigned int next_p;
1084
1085   switch (result)
1086   {
1087       /**
1088        * Service is currently being started (due to client request).
1089        */
1090     case GNUNET_ARM_PROCESS_STARTING:
1091       GNUNET_TESTBED_operation_done (peer->op_handle);
1092       peer->op_handle = NULL;
1093
1094       if (peer_cnt < (num_peers - 1))
1095       {
1096         next_p = (++peer_cnt % num_peers);
1097         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(
1098                                         GNUNET_TIME_UNIT_MILLISECONDS,
1099                                         200),
1100                                       &announce_next_regex,
1101                                       (void *) (long) next_p);
1102       }
1103       else
1104       {
1105         printf ("All daemons started. Waiting %s to start string searches\n",
1106                 GNUNET_STRINGS_relative_time_to_string (search_delay,
1107                                                         GNUNET_NO));
1108         fflush (stdout);
1109         GNUNET_SCHEDULER_add_delayed (search_delay,
1110                                       do_connect_by_string, 
1111                                       NULL);
1112       }
1113       break;
1114
1115       /**
1116        * Service name is unknown to ARM.
1117        */
1118     case GNUNET_ARM_PROCESS_UNKNOWN:
1119       /**
1120        * Service is now down (due to client request).
1121        */
1122     case GNUNET_ARM_PROCESS_DOWN:
1123       /**
1124        * Service is already running.
1125        */
1126     case GNUNET_ARM_PROCESS_ALREADY_RUNNING:
1127       /**
1128        * Service is already being stopped by some other client.
1129        */
1130     case GNUNET_ARM_PROCESS_ALREADY_STOPPING:
1131       /**
1132        * Service is already down (no action taken)
1133        */
1134     case GNUNET_ARM_PROCESS_ALREADY_DOWN: 
1135       /**
1136        * ARM is currently being shut down (no more process starts)
1137        */
1138     case GNUNET_ARM_PROCESS_SHUTDOWN:
1139       /**
1140        * Error in communication with ARM
1141        */
1142     case GNUNET_ARM_PROCESS_COMMUNICATION_ERROR:
1143       /**
1144        * Timeout in communication with ARM
1145        */
1146     case GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT:
1147       /**
1148       * Failure to perform operation
1149       */
1150     case GNUNET_ARM_PROCESS_FAILURE:
1151     default:
1152       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ARM returned %d\n", result);
1153       GNUNET_abort ();
1154   }
1155 }
1156
1157
1158 /**
1159  * ARM connect callback. Called when we are connected to the arm service for
1160  * the peer in 'cls'. If successfull we start the regex deamon to start
1161  * announcing the regex of this peer.
1162  *
1163  * @param cls internal peer id.
1164  * @param op operation handle.
1165  * @param ca_result connect adapter result.
1166  * @param emsg error message.
1167  */
1168 static void
1169 arm_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
1170                 void *ca_result, const char *emsg)
1171 {
1172   struct RegexPeer *peer = (struct RegexPeer *) cls;
1173
1174   if (NULL != emsg || NULL == op || NULL == ca_result)
1175   {
1176     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ARM connect failed: %s\n", emsg);
1177     GNUNET_abort ();
1178   }
1179
1180   GNUNET_assert (NULL != peer->arm_handle);
1181   GNUNET_assert (peer->op_handle == op);
1182   GNUNET_assert (peer->arm_handle == ca_result);
1183
1184   GNUNET_ARM_start_service (ca_result, "regexprofiler",
1185                             GNUNET_OS_INHERIT_STD_NONE,
1186                             GNUNET_TIME_UNIT_FOREVER_REL,
1187                             &arm_start_cb,
1188                             peer);
1189 }
1190
1191
1192 /**
1193  * Task to start the daemons on each peer so that the regexes are announced
1194  * into the DHT.
1195  *
1196  * @param cls NULL
1197  * @param tc the task context
1198  */
1199 static void
1200 do_announce (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1201 {
1202   printf ("Starting announce.\n");
1203   fflush (stdout);
1204
1205     /* First connect to arm service, then announce. Next
1206        announce will be in arm_connect_cb */
1207     peers[0].op_handle =
1208       GNUNET_TESTBED_service_connect (NULL,
1209                                       peers[0].peer_handle,
1210                                       "arm",
1211                                       &arm_connect_cb,
1212                                       &peers[0],
1213                                       &arm_ca,
1214                                       &arm_da,
1215                                       &peers[0]);
1216
1217 }
1218
1219
1220 /**
1221  * Start announcing the next regex in the DHT.
1222  *
1223  * @param cls Index of the next peer in the peers array.
1224  * @param tc TaskContext.
1225  */
1226 void
1227 announce_next_regex (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1228 {
1229   long next_p = (long) cls;
1230
1231   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1232     return;
1233
1234   printf ("Starting daemon %ld\n", next_p);
1235   fflush (stdout);
1236
1237   peers[next_p].op_handle =
1238     GNUNET_TESTBED_service_connect (NULL,
1239                                     peers[next_p].peer_handle,
1240                                     "arm",
1241                                     &arm_connect_cb,
1242                                     &peers[next_p],
1243                                     &arm_ca,
1244                                     &arm_da,
1245                                     &peers[next_p]);
1246 }
1247
1248 /**
1249  * DHT connect callback. Called when we are connected to the dht service for
1250  * the peer in 'cls'. If successfull we connect to the stats service of this
1251  * peer and then try to match the search string of this peer.
1252  *
1253  * @param cls internal peer id.
1254  * @param op operation handle.
1255  * @param ca_result connect adapter result.
1256  * @param emsg error message.
1257  */
1258 static void
1259 dht_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
1260                 void *ca_result, const char *emsg)
1261 {
1262   struct RegexPeer *peer = (struct RegexPeer *) cls;
1263
1264   if (NULL != emsg || NULL == op || NULL == ca_result)
1265   {
1266     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "DHT connect failed: %s\n", emsg);
1267     GNUNET_abort ();
1268   }
1269
1270   GNUNET_assert (NULL != peer->dht_handle);
1271   GNUNET_assert (peer->op_handle == op);
1272   GNUNET_assert (peer->dht_handle == ca_result);
1273
1274   peer->search_str_matched = GNUNET_NO;
1275   peer->search_handle = GNUNET_REGEX_search (peer->dht_handle,
1276                                              peer->search_str,
1277                                              &regex_found_handler, peer,
1278                                              NULL);
1279   peer->prof_start_time = GNUNET_TIME_absolute_get ();
1280 }
1281
1282
1283 /**
1284  * DHT connect adapter. Opens a connection to the dht service.
1285  *
1286  * @param cls Closure (peer).
1287  * @param cfg Configuration handle.
1288  *
1289  * @return
1290  */
1291 static void *
1292 dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1293 {
1294   struct RegexPeer *peer = cls;
1295
1296   peer->dht_handle = GNUNET_DHT_connect (cfg, 32);
1297
1298   return peer->dht_handle;
1299 }
1300
1301
1302 /**
1303  * Adapter function called to destroy a connection to the dht service.
1304  *
1305  * @param cls Closure (peer).
1306  * @param op_result Service handle returned from the connect adapter.
1307  */
1308 static void
1309 dht_da (void *cls, void *op_result)
1310 {
1311   struct RegexPeer *peer = (struct RegexPeer *) cls;
1312
1313   GNUNET_assert (peer->dht_handle == op_result);
1314
1315   if (NULL != peer->search_handle)
1316   {
1317     GNUNET_REGEX_search_cancel (peer->search_handle);
1318     peer->search_handle = NULL;
1319   }
1320
1321   if (NULL != peer->dht_handle)
1322   {
1323     GNUNET_DHT_disconnect (peer->dht_handle);
1324     peer->dht_handle = NULL;
1325   }
1326 }
1327
1328
1329 /******************************************************************************/
1330 /***************************  TESTBED PEER SETUP  *****************************/
1331 /******************************************************************************/
1332
1333
1334 /**
1335  * Configure the peer overlay topology.
1336  *
1337  * @param cls NULL
1338  * @param tc the task context
1339  */
1340 static void
1341 do_configure_topology (void *cls,
1342                        const struct GNUNET_SCHEDULER_TaskContext * tc)
1343 {
1344   /*
1345     if (0 == linking_factor)
1346     linking_factor = 1;
1347     num_links = linking_factor * num_peers;
1348   */
1349   /* num_links = num_peers - 1; */
1350   num_links = linking_factor;
1351
1352   /* Do overlay connect */
1353   prof_start_time = GNUNET_TIME_absolute_get ();
1354   topology_op =
1355     GNUNET_TESTBED_overlay_configure_topology (NULL, num_peers, peer_handles,
1356                                                NULL,
1357                                                NULL,
1358                                                NULL,
1359                                                GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI,
1360                                                num_links,
1361                                                GNUNET_TESTBED_TOPOLOGY_RETRY_CNT,
1362                                                (unsigned int) 0,
1363                                                GNUNET_TESTBED_TOPOLOGY_OPTION_END);
1364   if (NULL == topology_op)
1365   {
1366     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1367                 "Cannot create topology, op handle was NULL\n");
1368     GNUNET_assert (0);
1369   }
1370 }
1371
1372
1373 /**
1374  * Functions of this signature are called when a peer has been successfully
1375  * started or stopped.
1376  *
1377  * @param cls the closure from GNUNET_TESTBED_peer_start/stop()
1378  * @param emsg NULL on success; otherwise an error description
1379  */
1380 static void
1381 peer_churn_cb (void *cls, const char *emsg)
1382 {
1383   struct DLLOperation *dll_op = cls;
1384   struct GNUNET_TESTBED_Operation *op;
1385   static unsigned int started_peers;
1386   unsigned int peer_cnt;
1387
1388   op = dll_op->op;
1389   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1390   GNUNET_free (dll_op);
1391   if (NULL != emsg)
1392   {
1393     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1394          _("An operation has failed while starting peers: %s\n"), emsg);
1395     GNUNET_TESTBED_operation_done (op);
1396     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1397       GNUNET_SCHEDULER_cancel (abort_task);
1398     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1399     return;
1400   }
1401   GNUNET_TESTBED_operation_done (op);
1402   if (++started_peers == num_peers)
1403   {
1404     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1405     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1406                 "All peers started successfully in %s\n",
1407                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1408     result = GNUNET_OK;
1409
1410     peer_handles = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *) * num_peers);
1411     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1412       peer_handles[peer_cnt] = peers[peer_cnt].peer_handle;
1413
1414     state = STATE_PEERS_LINKING;
1415     GNUNET_SCHEDULER_add_now (&do_configure_topology, NULL);
1416   }
1417 }
1418
1419
1420 /**
1421  * Functions of this signature are called when a peer has been successfully
1422  * created
1423  *
1424  * @param cls the closure from GNUNET_TESTBED_peer_create()
1425  * @param peer the handle for the created peer; NULL on any error during
1426  *          creation
1427  * @param emsg NULL if peer is not NULL; else MAY contain the error description
1428  */
1429 static void
1430 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
1431 {
1432   struct DLLOperation *dll_op = cls;
1433   struct RegexPeer *peer_ptr;
1434   static unsigned int created_peers;
1435   unsigned int peer_cnt;
1436
1437   if (NULL != emsg)
1438   {
1439     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1440          _("Creating a peer failed. Error: %s\n"), emsg);
1441     GNUNET_TESTBED_operation_done (dll_op->op);
1442     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1443     GNUNET_free (dll_op);
1444     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1445       GNUNET_SCHEDULER_cancel (abort_task);
1446     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1447     return;
1448   }
1449
1450   peer_ptr = dll_op->cls;
1451   GNUNET_assert (NULL == peer_ptr->peer_handle);
1452   GNUNET_CONFIGURATION_destroy (peer_ptr->cfg);
1453   peer_ptr->cfg = NULL;
1454   peer_ptr->peer_handle = peer;
1455   GNUNET_TESTBED_operation_done (dll_op->op);
1456   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1457   GNUNET_free (dll_op);
1458
1459   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %i created on host %s\n",
1460               peer_ptr->id,
1461               GNUNET_TESTBED_host_get_hostname (peer_ptr->host_handle));
1462
1463   if (++created_peers == num_peers)
1464   {
1465     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1466     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1467                 "All peers created successfully in %s\n",
1468                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1469     /* Now peers are to be started */
1470     state = STATE_PEERS_STARTING;
1471     prof_start_time = GNUNET_TIME_absolute_get ();
1472     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1473     {
1474       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1475       dll_op->op = GNUNET_TESTBED_peer_start (dll_op,
1476                                               peers[peer_cnt].peer_handle,
1477                                               &peer_churn_cb, dll_op);
1478       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1479     }
1480   }
1481 }
1482
1483
1484 /**
1485  * Function called with a filename for each file in the policy directory. Create
1486  * a peer for each filename and update the peer's configuration to include the
1487  * max_path_compression specified as a command line argument as well as the
1488  * policy_file for this peer. The gnunet-service-regexprofiler service is
1489  * automatically started on this peer. The service reads the configurration and
1490  * announces the regexes stored in the policy file 'filename'.
1491  *
1492  * @param cls closure
1493  * @param filename complete filename (absolute path)
1494  * @return GNUNET_OK to continue to iterate,
1495  *  GNUNET_SYSERR to abort iteration with error!
1496  */
1497 static int
1498 policy_filename_cb (void *cls, const char *filename)
1499 {
1500   static unsigned int peer_cnt;
1501   struct DLLOperation *dll_op;
1502   struct RegexPeer *peer = &peers[peer_cnt];
1503
1504   GNUNET_assert (NULL != peer);
1505
1506   peer->policy_file = GNUNET_strdup (filename);
1507
1508   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Creating peer %i on host %s for policy file %s\n",
1509               peer->id,
1510               GNUNET_TESTBED_host_get_hostname (peer->host_handle),
1511               filename);
1512
1513   /* Set configuration options specific for this peer
1514      (max_path_compression and policy_file */
1515   peer->cfg = GNUNET_CONFIGURATION_dup (cfg);
1516   GNUNET_CONFIGURATION_set_value_number (peer->cfg, "REGEXPROFILER",
1517                                          "MAX_PATH_COMPRESSION",
1518                                          (unsigned long long)max_path_compression);
1519   GNUNET_CONFIGURATION_set_value_string (peer->cfg, "REGEXPROFILER",
1520                                          "POLICY_FILE", filename);
1521
1522   dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1523   dll_op->cls = &peers[peer_cnt];
1524   dll_op->op = GNUNET_TESTBED_peer_create (mc,
1525                                            peer->host_handle,
1526                                            peer->cfg,
1527                                            &peer_create_cb,
1528                                            dll_op);
1529   GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1530
1531   peer_cnt++;
1532
1533   return GNUNET_OK;
1534 }
1535
1536
1537 /**
1538  * Controller event callback.
1539  *
1540  * @param cls NULL
1541  * @param event the controller event
1542  */
1543 static void
1544 controller_event_cb (void *cls,
1545                      const struct GNUNET_TESTBED_EventInformation *event)
1546 {
1547   struct DLLOperation *dll_op;
1548   struct GNUNET_TESTBED_Operation *op;
1549   int ret;
1550
1551   switch (state)
1552   {
1553   case STATE_SLAVES_STARTING:
1554     switch (event->type)
1555     {
1556     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1557       {
1558         static unsigned int slaves_started;
1559         unsigned int peer_cnt;
1560
1561         dll_op = event->details.operation_finished.op_cls;
1562         GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1563         GNUNET_free (dll_op);
1564         op = event->details.operation_finished.operation;
1565         if (NULL != event->details.operation_finished.emsg)
1566         {
1567           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1568                _("An operation has failed while starting slaves\n"));
1569           GNUNET_TESTBED_operation_done (op);
1570           if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1571             GNUNET_SCHEDULER_cancel (abort_task);
1572           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1573           return;
1574         }
1575         GNUNET_TESTBED_operation_done (op);
1576         /* Proceed to start peers */
1577         if (++slaves_started == num_hosts - 1)
1578         {
1579           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1580                       "All slaves started successfully\n");
1581
1582           state = STATE_PEERS_CREATING;
1583           prof_start_time = GNUNET_TIME_absolute_get ();
1584
1585           if (-1 == (ret = GNUNET_DISK_directory_scan (policy_dir,
1586                                                        NULL,
1587                                                        NULL)))
1588           {
1589             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1590                         _("No files found in `%s'\n"),
1591                         policy_dir);
1592             GNUNET_SCHEDULER_shutdown ();
1593             return;
1594           }
1595           num_peers = (unsigned int) ret;
1596           peers = GNUNET_malloc (sizeof (struct RegexPeer) * num_peers);
1597
1598           /* Initialize peers */
1599           for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1600           {
1601             struct RegexPeer *peer = &peers[peer_cnt];
1602             peer->id = peer_cnt;
1603             peer->policy_file = NULL;
1604             /* Do not start peers on hosts[0] (master controller) */
1605             peer->host_handle = hosts[1 + (peer_cnt % (num_hosts -1))];
1606             peer->dht_handle = NULL;
1607             peer->search_handle = NULL;
1608             peer->stats_handle = NULL;
1609             peer->stats_op_handle = NULL;
1610             peer->search_str = NULL;
1611             peer->search_str_matched = GNUNET_NO;
1612           }
1613
1614           GNUNET_DISK_directory_scan (policy_dir,
1615                                       &policy_filename_cb,
1616                                       NULL);
1617         }
1618       }
1619       break;
1620     default:
1621       GNUNET_assert (0);
1622     }
1623     break;
1624   case STATE_PEERS_STARTING:
1625     switch (event->type)
1626     {
1627     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1628       /* Control reaches here when peer start fails */
1629     case GNUNET_TESTBED_ET_PEER_START:
1630       /* we handle peer starts in peer_churn_cb */
1631       break;
1632     default:
1633       GNUNET_assert (0);
1634     }
1635     break;
1636   case STATE_PEERS_LINKING:
1637    switch (event->type)
1638    {
1639      static unsigned int established_links;
1640    case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1641      /* Control reaches here when a peer linking operation fails */
1642      if (NULL != event->details.operation_finished.emsg)
1643      {
1644        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1645                    _("An operation has failed while linking\n"));
1646        printf ("F%u ", retry_links);
1647        fflush (stdout);
1648        retry_links++;
1649      }
1650      /* We do no retries, consider this link as established */
1651      /* break; */
1652    case GNUNET_TESTBED_ET_CONNECT:
1653    {
1654      char output_buffer[512];
1655      size_t size;
1656
1657      if (0 == established_links)
1658        printf ("Establishing links .");
1659      else
1660      {
1661        printf (".");
1662        fflush (stdout);
1663      }
1664      if (++established_links == num_links)
1665      {
1666        fflush (stdout);
1667        prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1668        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1669                    "%u links established in %s\n",
1670                    num_links,
1671                    GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1672        prof_time = GNUNET_TIME_relative_divide(prof_time, num_links);
1673        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1674                    "Average of %s per connection\n",
1675                    GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1676        result = GNUNET_OK;
1677        GNUNET_free (peer_handles);
1678
1679        if (NULL != data_file)
1680        {
1681          size =
1682            GNUNET_snprintf (output_buffer,
1683                             sizeof (output_buffer),
1684                             "# of peers: %u\n# of links established: %u\n"
1685                             "Time to establish links: %s\nLinking failures: %u\n"
1686                             "path compression length: %u\n# of search strings: %u\n",
1687                             num_peers,
1688                             (established_links - cont_fails),
1689                             GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
1690                             cont_fails,
1691                             max_path_compression,
1692                             num_search_strings);
1693
1694          if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
1695            GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
1696        }
1697
1698        printf ("\nWaiting %s before starting to announce.\n",
1699                GNUNET_STRINGS_relative_time_to_string (search_delay, GNUNET_NO));
1700        fflush (stdout);
1701
1702        state = STATE_SEARCH_REGEX;
1703
1704        search_task = GNUNET_SCHEDULER_add_delayed (search_delay,
1705                                                    &do_announce, NULL);
1706      }
1707    }
1708    break;
1709    default:
1710      GNUNET_assert (0);
1711    }
1712    break;
1713   case STATE_SEARCH_REGEX:
1714   {
1715     /* Handled in service connect callback */
1716     break;
1717   }
1718   default:
1719     switch (state)
1720     {
1721     case STATE_PEERS_CREATING:
1722       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create peer\n");
1723       break;
1724     default:
1725       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1726                   "Unexpected controller_cb with state %i!\n", state);
1727     }
1728     GNUNET_assert (0);
1729   }
1730 }
1731
1732
1733 /**
1734  * Task to register all hosts available in the global host list.
1735  *
1736  * @param cls NULL
1737  * @param tc the scheduler task context
1738  */
1739 static void
1740 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1741
1742
1743 /**
1744  * Callback which will be called to after a host registration succeeded or failed
1745  *
1746  * @param cls the closure
1747  * @param emsg the error message; NULL if host registration is successful
1748  */
1749 static void
1750 host_registration_completion (void *cls, const char *emsg)
1751 {
1752   reg_handle = NULL;
1753   if (NULL != emsg)
1754   {
1755     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1756                 _("Host registration failed for a host. Error: %s\n"), emsg);
1757     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1758       GNUNET_SCHEDULER_cancel (abort_task);
1759     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1760     return;
1761   }
1762   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1763 }
1764
1765
1766 /**
1767  * Task to register all hosts available in the global host list.
1768  *
1769  * @param cls NULL
1770  * @param tc the scheduler task context
1771  */
1772 static void
1773 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1774 {
1775   struct DLLOperation *dll_op;
1776   static unsigned int reg_host;
1777   unsigned int slave;
1778
1779   register_hosts_task = GNUNET_SCHEDULER_NO_TASK;
1780   if (reg_host == num_hosts - 1)
1781   {
1782     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1783                 "All hosts successfully registered\n");
1784     /* Start slaves */
1785     state = STATE_SLAVES_STARTING;
1786     for (slave = 1; slave < num_hosts; slave++)
1787     {
1788       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1789       dll_op->op = GNUNET_TESTBED_controller_link (dll_op,
1790                                                    mc,
1791                                                    hosts[slave],
1792                                                    hosts[0],
1793                                                    cfg,
1794                                                    GNUNET_YES);
1795       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1796     }
1797     return;
1798   }
1799   reg_handle = GNUNET_TESTBED_register_host (mc, hosts[++reg_host],
1800                                              host_registration_completion,
1801                                              NULL);
1802 }
1803
1804
1805 /**
1806  * Callback to signal successfull startup of the controller process.
1807  *
1808  * @param cls the closure from GNUNET_TESTBED_controller_start()
1809  * @param config the configuration with which the controller has been started;
1810  *          NULL if status is not GNUNET_OK
1811  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
1812  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
1813  */
1814 static void
1815 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
1816 {
1817   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1818     GNUNET_SCHEDULER_cancel (abort_task);
1819   if (GNUNET_OK != status)
1820   {
1821     mc_proc = NULL;
1822     printf("Oh, dear!\n");
1823     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1824     return;
1825   }
1826   event_mask = 0;
1827   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
1828   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
1829   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1830   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
1831   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
1832   mc = GNUNET_TESTBED_controller_connect (config, hosts[0], event_mask,
1833                                           &controller_event_cb, NULL);
1834   if (NULL == mc)
1835   {
1836     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1837                 _("Unable to connect to master controller -- Check config\n"));
1838     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1839     return;
1840   }
1841   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1842   abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1843                                              &do_abort, (void*) __LINE__);
1844 }
1845
1846
1847 /**
1848  * Load search strings from given filename. One search string per line.
1849  *
1850  * @param filename filename of the file containing the search strings.
1851  * @param strings set of strings loaded from file. Caller needs to free this
1852  *                if number returned is greater than zero.
1853  * @param limit upper limit on the number of strings read from the file
1854  * @return number of strings found in the file. GNUNET_SYSERR on error.
1855  */
1856 static int
1857 load_search_strings (const char *filename, char ***strings, unsigned int limit)
1858 {
1859   char *data;
1860   char *buf;
1861   uint64_t filesize;
1862   unsigned int offset;
1863   int str_cnt;
1864   unsigned int i;
1865
1866   if (NULL == filename)
1867   {
1868     return GNUNET_SYSERR;
1869   }
1870
1871   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
1872   {
1873     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1874                 "Could not find search strings file %s\n", filename);
1875     return GNUNET_SYSERR;
1876   }
1877   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &filesize, GNUNET_YES, GNUNET_YES))
1878     filesize = 0;
1879   if (0 == filesize)
1880   {
1881     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Search strings file %s is empty.\n", filename);
1882     return GNUNET_SYSERR;
1883   }
1884   data = GNUNET_malloc (filesize);
1885   if (filesize != GNUNET_DISK_fn_read (filename, data, filesize))
1886   {
1887     GNUNET_free (data);
1888     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read search strings file %s.\n",
1889          filename);
1890     return GNUNET_SYSERR;
1891   }
1892   buf = data;
1893   offset = 0;
1894   str_cnt = 0;
1895   while (offset < (filesize - 1) && str_cnt < limit)
1896   {
1897     offset++;
1898     if (((data[offset] == '\n')) && (buf != &data[offset]))
1899     {
1900       data[offset] = '\0';
1901       str_cnt++;
1902       buf = &data[offset + 1];
1903     }
1904     else if ((data[offset] == '\n') || (data[offset] == '\0'))
1905       buf = &data[offset + 1];
1906   }
1907   *strings = GNUNET_malloc (sizeof (char *) * str_cnt);
1908   offset = 0;
1909   for (i = 0; i < str_cnt; i++)
1910   {
1911     GNUNET_asprintf (&(*strings)[i], "%s%s", regex_prefix, &data[offset]);
1912     offset += strlen (&data[offset]) + 1;
1913   }
1914   GNUNET_free (data);
1915   return str_cnt;
1916 }
1917
1918
1919 /**
1920  * Callbacks of this type are called by GNUNET_TESTBED_is_host_habitable to
1921  * inform whether the given host is habitable or not. The Handle returned by
1922  * GNUNET_TESTBED_is_host_habitable() is invalid after this callback is called
1923  *
1924  * @param cls NULL
1925  * @param host the host whose status is being reported; will be NULL if the host
1926  *          given to GNUNET_TESTBED_is_host_habitable() is NULL
1927  * @param status GNUNET_YES if it is habitable; GNUNET_NO if not
1928  */
1929 static void 
1930 host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *host, int status)
1931 {
1932   struct GNUNET_TESTBED_HostHabitableCheckHandle **hc_handle = cls;
1933   static unsigned int hosts_checked;
1934
1935   *hc_handle = NULL;
1936   if (GNUNET_NO == status)
1937   {
1938     if ((NULL != host) && (NULL != GNUNET_TESTBED_host_get_hostname (host)))
1939       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Host %s cannot start testbed\n"),
1940                   GNUNET_TESTBED_host_get_hostname (host));
1941     else
1942       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Testbed cannot be started on localhost\n"));
1943     GNUNET_SCHEDULER_cancel (abort_task);
1944     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1945     return;
1946   }
1947   hosts_checked++;
1948   /* printf (_("\rChecked %u hosts"), hosts_checked); */
1949   /* fflush (stdout); */
1950   if (hosts_checked < num_hosts)
1951     return;
1952   /* printf (_("\nAll hosts can start testbed. Creating peers\n")); */
1953   GNUNET_free (hc_handles);
1954   hc_handles = NULL;
1955   mc_proc = 
1956       GNUNET_TESTBED_controller_start (GNUNET_TESTBED_host_get_hostname
1957                                        (hosts[0]),
1958                                        hosts[0],
1959                                        cfg,
1960                                        status_cb,
1961                                        NULL);
1962 }
1963
1964
1965 /**
1966  * Main function that will be run by the scheduler.
1967  *
1968  * @param cls closure
1969  * @param args remaining command-line arguments
1970  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1971  * @param config configuration
1972  */
1973 static void
1974 run (void *cls, char *const *args, const char *cfgfile,
1975      const struct GNUNET_CONFIGURATION_Handle *config)
1976 {
1977   unsigned int nhost;
1978   unsigned int nsearchstrs;
1979
1980   if (NULL == args[0])
1981   {
1982     fprintf (stderr, _("No hosts-file specified on command line. Exiting.\n"));
1983     return;
1984   }
1985   if (NULL == args[1])
1986   {
1987     fprintf (stderr, _("No policy directory specified on command line. Exiting.\n"));
1988     return;
1989   }
1990   num_hosts = GNUNET_TESTBED_hosts_load_from_file (args[0], config, &hosts);
1991   if (0 == num_hosts)
1992   {
1993     fprintf (stderr, _("No hosts loaded. Need at least one host\n"));
1994     return;
1995   }
1996   printf (_("Checking whether given hosts can start testbed. Please wait\n"));
1997   hc_handles = GNUNET_malloc (sizeof (struct
1998                                       GNUNET_TESTBED_HostHabitableCheckHandle *) 
1999                               * num_hosts);
2000   for (nhost = 0; nhost < num_hosts; nhost++)
2001   {
2002     hc_handles[nhost] = GNUNET_TESTBED_is_host_habitable (hosts[nhost], config,
2003                                                           &host_habitable_cb,
2004                                                           &hc_handles[nhost]);
2005     if (NULL == hc_handles[nhost])
2006     {
2007       int i;
2008
2009       GNUNET_break (0);
2010       for (i = 0; i <= nhost; i++)
2011         if (NULL != hc_handles[i])
2012           GNUNET_TESTBED_is_host_habitable_cancel (hc_handles[i]);
2013       GNUNET_free (hc_handles);
2014       hc_handles = NULL;
2015       break;
2016     }
2017   }
2018   if (num_hosts != nhost)
2019   {
2020     fprintf (stderr, _("Exiting\n"));
2021     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2022     return;
2023   }
2024   if (NULL == config)
2025   {
2026     fprintf (stderr, _("No configuration file given. Exiting\n"));
2027     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2028     return;
2029   }
2030
2031   if (GNUNET_OK !=
2032       GNUNET_CONFIGURATION_get_value_string (config, "REGEXPROFILER", "REGEX_PREFIX",
2033                                              &regex_prefix))
2034   {
2035     fprintf (stderr, _("Configuration option (regex_prefix) missing. Exiting\n"));
2036     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2037     return;
2038   }
2039
2040   if ( (NULL != data_filename) &&
2041        (NULL == (data_file =
2042                  GNUNET_DISK_file_open (data_filename,
2043                                         GNUNET_DISK_OPEN_READWRITE |
2044                                         GNUNET_DISK_OPEN_TRUNCATE |
2045                                         GNUNET_DISK_OPEN_CREATE,
2046                                         GNUNET_DISK_PERM_USER_READ |
2047                                         GNUNET_DISK_PERM_USER_WRITE))) )
2048     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2049                               "open",
2050                               data_filename);
2051   if (GNUNET_YES != GNUNET_DISK_directory_test (args[1], GNUNET_YES))
2052   {
2053     fprintf (stderr, _("Specified policies directory does not exist. Exiting.\n"));
2054     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2055     return;
2056   }
2057   policy_dir = args[1];
2058   if (GNUNET_YES != GNUNET_DISK_file_test (args[2]))
2059   {
2060     fprintf (stderr, _("No search strings file given. Exiting.\n"));
2061     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2062     return;
2063   }
2064   nsearchstrs = load_search_strings (args[2], &search_strings, num_search_strings);
2065   if (num_search_strings != nsearchstrs)
2066   {
2067     num_search_strings = nsearchstrs;
2068     fprintf (stderr, _("Error loading search strings. Given file does not contain enough strings. Exiting.\n"));
2069     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2070     return;
2071   }
2072   if (0 >= num_search_strings || NULL == search_strings)
2073   {
2074     fprintf (stderr, _("Error loading search strings. Exiting.\n"));
2075     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2076     return;
2077   }
2078   unsigned int i;
2079   for (i = 0; i < num_search_strings; i++)
2080     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "search string: %s\n", search_strings[i]);
2081   cfg = GNUNET_CONFIGURATION_dup (config);
2082   abort_task =
2083       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
2084                                     (GNUNET_TIME_UNIT_SECONDS, 5), &do_abort,
2085                                     (void*) __LINE__);
2086 }
2087
2088
2089 /**
2090  * Main function.
2091  *
2092  * @param argc argument count
2093  * @param argv argument values
2094  * @return 0 on success
2095  */
2096 int
2097 main (int argc, char *const *argv)
2098 {
2099   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
2100     {'d', "details", "FILENAME",
2101      gettext_noop ("name of the file for writing statistics"),
2102      1, &GNUNET_GETOPT_set_string, &data_filename},
2103     {'n', "num-links", "COUNT",
2104       gettext_noop ("create COUNT number of random links between peers"),
2105       GNUNET_YES, &GNUNET_GETOPT_set_uint, &linking_factor },
2106     {'t', "matching-timeout", "TIMEOUT",
2107       gettext_noop ("wait TIMEOUT before considering a string match as failed"),
2108       GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &search_timeout },
2109     {'s', "search-delay", "DELAY",
2110       gettext_noop ("wait DELAY before starting string search"),
2111       GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &search_delay },
2112     {'a', "num-search-strings", "COUNT",
2113       gettext_noop ("number of search strings to read from search strings file"),
2114       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_search_strings },
2115     {'p', "max-path-compression", "MAX_PATH_COMPRESSION",
2116      gettext_noop ("maximum path compression length"),
2117      1, &GNUNET_GETOPT_set_uint, &max_path_compression},
2118     GNUNET_GETOPT_OPTION_END
2119   };
2120   int ret;
2121
2122   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
2123     return 2;
2124
2125   result = GNUNET_SYSERR;
2126   ret =
2127       GNUNET_PROGRAM_run (argc, argv,
2128                           "gnunet-regex-profiler [OPTIONS] hosts-file policy-dir search-strings-file",
2129                           _("Profiler for regex"),
2130                           options, &run, NULL);
2131
2132   if (GNUNET_OK != ret)
2133     return ret;
2134   if (GNUNET_OK != result)
2135     return 1;
2136   return 0;
2137 }