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