057ab7056477cef133db84ef59ad6f108f405bc3
[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 next_search;
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   peers[peer_cnt].search_str = search_strings[peer_cnt];
972   peers[peer_cnt].search_str_matched = GNUNET_NO;
973   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
974               "Searching for string \"%s\" on peer %d with file %s (%u||)\n",
975               peers[peer_cnt].search_str,
976               peer_cnt,
977               peers[peer_cnt].policy_file,
978               parallel_searches);
979
980   peers[peer_cnt].op_handle =
981     GNUNET_TESTBED_service_connect (NULL,
982                                     peers[peer_cnt].peer_handle,
983                                     "dht",
984                                     &dht_connect_cb,
985                                     &peers[peer_cnt],
986                                     &dht_ca,
987                                     &dht_da,
988                                     &peers[peer_cnt]);
989   peers[peer_cnt].timeout = GNUNET_SCHEDULER_add_delayed (FIND_TIMEOUT,
990                                                           &find_timeout,
991                                                           &peers[peer_cnt]);
992   peer_cnt++;
993 }
994
995
996 /**
997  * ARM connect adapter. Opens a connection to the ARM service.
998  *
999  * @param cls Closure (peer).
1000  * @param cfg Configuration handle.
1001  *
1002  * @return
1003  */
1004 static void *
1005 arm_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1006 {
1007   struct RegexPeer *peer = cls;
1008
1009   peer->arm_handle = GNUNET_ARM_alloc (cfg);
1010   GNUNET_ARM_connect (peer->arm_handle, NULL, NULL);
1011
1012   return peer->arm_handle;
1013 }
1014
1015
1016 /**
1017  * Adapter function called to destroy a connection to the ARM service.
1018  *
1019  * @param cls Closure (peer).
1020  * @param op_result Service handle returned from the connect adapter.
1021  */
1022 static void
1023 arm_da (void *cls, void *op_result)
1024 {
1025   struct RegexPeer *peer = (struct RegexPeer *) cls;
1026
1027   GNUNET_assert (peer->arm_handle == op_result);
1028
1029   if (NULL != peer->arm_handle)
1030   {
1031     GNUNET_ARM_disconnect_and_free (peer->arm_handle);
1032     peer->arm_handle = NULL;
1033   }
1034 }
1035
1036 /**
1037  * Finish and free the operation used to start the regex daemon.
1038  * operation_done calls ARM_disconnect, which cannot happen inside an
1039  * ARM callback.
1040  *
1041  * @param cls Closure (Peer info)
1042  * @param tc TaskContext
1043  */
1044 static void
1045 arm_op_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1046 {
1047   struct RegexPeer *peer = (struct RegexPeer *) cls;
1048
1049   GNUNET_TESTBED_operation_done (peer->op_handle);
1050   peer->op_handle = NULL;
1051 }
1052
1053
1054 /**
1055  * Callback called when arm has started the daemon we asked for.
1056  * 
1057  * @param cls           Closure ().
1058  * @param arm           Arm handle.
1059  * @param rs            Status of the request.
1060  * @param service       Service we asked to start (deamon).
1061  * @param result        Result of the request.
1062  */
1063 static void
1064 arm_start_cb (void *cls, struct GNUNET_ARM_Handle *arm,
1065     enum GNUNET_ARM_RequestStatus rs, const char *service,
1066     enum GNUNET_ARM_Result result)
1067 {
1068   struct RegexPeer *peer = (struct RegexPeer *) cls;
1069
1070   if (rs != GNUNET_ARM_REQUEST_SENT_OK)
1071   {
1072     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ARM request was not sent: %u\n", rs);
1073     GNUNET_abort ();
1074   }
1075   switch (result)
1076   {
1077       /**
1078        * Asked to start it, but it's already starting.
1079        */
1080     case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
1081       GNUNET_break (0); /* Shouldn't be starting, however it's not fatal. */
1082       /* fallthrough */
1083
1084       /**
1085        * Service is currently being started (due to client request).
1086        */
1087     case GNUNET_ARM_RESULT_STARTING:
1088       GNUNET_SCHEDULER_add_now (&arm_op_done, peer);
1089
1090       {
1091         long search_peer;
1092         unsigned int i;
1093         unsigned int me;
1094
1095         me = peer - peers;
1096
1097         /* Find a peer to look for a string matching the regex announced */
1098         search_peer = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1099                                                 num_peers);
1100         for (i = 0; peers[search_peer].search_str != NULL; i++)
1101         {
1102           search_peer = (search_peer + 1) % num_peers;
1103           if (i > num_peers)
1104             GNUNET_abort (); /* we ran out of peers, must be a bug */
1105         }
1106         peers[search_peer].search_str = search_strings[me];
1107         GNUNET_SCHEDULER_add_delayed (ANNOUNCE_TIME,
1108                                       &find_string,
1109                                       (void *) search_peer);
1110       }
1111       if (next_search >= num_peers &&
1112           GNUNET_SCHEDULER_NO_TASK == search_timeout_task)
1113       {
1114         GNUNET_log (GNUNET_ERROR_TYPE_INFO, "All daemons started.\n");
1115         /* FIXME start GLOBAL timeout to abort experiment */
1116         search_timeout_task = GNUNET_SCHEDULER_add_delayed (search_timeout_time,
1117                                                             &search_timeout,
1118                                                             NULL);
1119       }
1120       break;
1121
1122     default:
1123       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ARM returned %d\n", result);
1124       GNUNET_abort ();
1125   }
1126 }
1127
1128 /**
1129  * ARM connect callback. Called when we are connected to the arm service for
1130  * the peer in 'cls'. If successfull we start the regex deamon to start
1131  * announcing the regex of this peer.
1132  *
1133  * @param cls internal peer id.
1134  * @param op operation handle.
1135  * @param ca_result connect adapter result.
1136  * @param emsg error message.
1137  */
1138 static void
1139 arm_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
1140                 void *ca_result, const char *emsg)
1141 {
1142   struct RegexPeer *peer = (struct RegexPeer *) cls;
1143
1144   if (NULL != emsg || NULL == op || NULL == ca_result)
1145   {
1146     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ARM connect failed: %s\n", emsg);
1147     GNUNET_abort ();
1148   }
1149
1150   GNUNET_assert (NULL != peer->arm_handle);
1151   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "[]%p - ()%p\n", peer->op_handle, op);
1152   GNUNET_assert (peer->op_handle == op);
1153   GNUNET_assert (peer->arm_handle == ca_result);
1154
1155   GNUNET_ARM_request_service_start (ca_result, "regexprofiler",
1156                                     GNUNET_OS_INHERIT_STD_NONE,
1157                                     GNUNET_TIME_UNIT_FOREVER_REL,
1158                                     arm_start_cb, cls);
1159 }
1160
1161
1162 /**
1163  * Task to start the daemons on each peer so that the regexes are announced
1164  * into the DHT.
1165  *
1166  * @param cls NULL
1167  * @param tc the task context
1168  */
1169 static void
1170 do_announce (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1171 {
1172   unsigned int i;
1173
1174   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting announce.\n");
1175
1176   for (i = 0; i < SEARCHES_IN_PARALLEL; i++)
1177   {
1178     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1179                 "  scheduling announce %u\n",
1180                 i);
1181     (void) GNUNET_SCHEDULER_add_now (&announce_next_regex, NULL);
1182   }
1183 }
1184
1185
1186 /**
1187  * Start announcing the next regex in the DHT.
1188  *
1189  * @param cls Closure (unused).
1190  * @param tc TaskContext.
1191  */
1192 static void
1193 announce_next_regex (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1194 {
1195   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) ||
1196             next_search >= num_peers)
1197     return;
1198
1199   /* First connect to arm service, then announce. Next
1200    * a nnounce will be in arm_connect_cb */
1201   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting daemon %u\n", next_search);
1202   peers[next_search].op_handle =
1203     GNUNET_TESTBED_service_connect (NULL,
1204                                     peers[next_search].peer_handle,
1205                                     "arm",
1206                                     &arm_connect_cb,
1207                                     &peers[next_search],
1208                                     &arm_ca,
1209                                     &arm_da,
1210                                     &peers[next_search]);
1211   next_search++;
1212   parallel_searches++;
1213 }
1214
1215 /**
1216  * DHT connect callback. Called when we are connected to the dht service for
1217  * the peer in 'cls'. If successfull we connect to the stats service of this
1218  * peer and then try to match the search string of this peer.
1219  *
1220  * @param cls internal peer id.
1221  * @param op operation handle.
1222  * @param ca_result connect adapter result.
1223  * @param emsg error message.
1224  */
1225 static void
1226 dht_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
1227                 void *ca_result, const char *emsg)
1228 {
1229   struct RegexPeer *peer = (struct RegexPeer *) cls;
1230
1231   if (NULL != emsg || NULL == op || NULL == ca_result)
1232   {
1233     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "DHT connect failed: %s\n", emsg);
1234     GNUNET_abort ();
1235   }
1236
1237   GNUNET_assert (NULL != peer->dht_handle);
1238   GNUNET_assert (peer->op_handle == op);
1239   GNUNET_assert (peer->dht_handle == ca_result);
1240
1241   peer->search_str_matched = GNUNET_NO;
1242   peer->search_handle = GNUNET_REGEX_search (peer->dht_handle,
1243                                              peer->search_str,
1244                                              &regex_found_handler, peer,
1245                                              NULL);
1246   peer->prof_start_time = GNUNET_TIME_absolute_get ();
1247 }
1248
1249
1250 /**
1251  * DHT connect adapter. Opens a connection to the dht service.
1252  *
1253  * @param cls Closure (peer).
1254  * @param cfg Configuration handle.
1255  *
1256  * @return
1257  */
1258 static void *
1259 dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1260 {
1261   struct RegexPeer *peer = cls;
1262
1263   peer->dht_handle = GNUNET_DHT_connect (cfg, 32);
1264
1265   return peer->dht_handle;
1266 }
1267
1268
1269 /**
1270  * Adapter function called to destroy a connection to the dht service.
1271  *
1272  * @param cls Closure (peer).
1273  * @param op_result Service handle returned from the connect adapter.
1274  */
1275 static void
1276 dht_da (void *cls, void *op_result)
1277 {
1278   struct RegexPeer *peer = (struct RegexPeer *) cls;
1279
1280   GNUNET_assert (peer->dht_handle == op_result);
1281
1282   if (NULL != peer->search_handle)
1283   {
1284     GNUNET_REGEX_search_cancel (peer->search_handle);
1285     peer->search_handle = NULL;
1286   }
1287
1288   if (NULL != peer->dht_handle)
1289   {
1290     GNUNET_DHT_disconnect (peer->dht_handle);
1291     peer->dht_handle = NULL;
1292   }
1293 }
1294
1295
1296 /******************************************************************************/
1297 /***************************  TESTBED PEER SETUP  *****************************/
1298 /******************************************************************************/
1299
1300
1301 /**
1302  * Configure the peer overlay topology.
1303  *
1304  * @param cls NULL
1305  * @param tc the task context
1306  */
1307 static void
1308 do_configure_topology (void *cls,
1309                        const struct GNUNET_SCHEDULER_TaskContext * tc)
1310 {
1311   /*
1312     if (0 == linking_factor)
1313     linking_factor = 1;
1314     num_links = linking_factor * num_peers;
1315   */
1316   /* num_links = num_peers - 1; */
1317   num_links = linking_factor;
1318
1319   /* Do overlay connect */
1320   prof_start_time = GNUNET_TIME_absolute_get ();
1321   topology_op =
1322     GNUNET_TESTBED_overlay_configure_topology (NULL, num_peers, peer_handles,
1323                                                NULL,
1324                                                NULL,
1325                                                NULL,
1326                                                GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI,
1327                                                num_links,
1328                                                GNUNET_TESTBED_TOPOLOGY_RETRY_CNT,
1329                                                (unsigned int) 0,
1330                                                GNUNET_TESTBED_TOPOLOGY_OPTION_END);
1331   if (NULL == topology_op)
1332   {
1333     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1334                 "Cannot create topology, op handle was NULL\n");
1335     GNUNET_assert (0);
1336   }
1337 }
1338
1339
1340 /**
1341  * Functions of this signature are called when a peer has been successfully
1342  * started or stopped.
1343  *
1344  * @param cls the closure from GNUNET_TESTBED_peer_start/stop()
1345  * @param emsg NULL on success; otherwise an error description
1346  */
1347 static void
1348 peer_churn_cb (void *cls, const char *emsg)
1349 {
1350   struct DLLOperation *dll_op = cls;
1351   struct GNUNET_TESTBED_Operation *op;
1352   static unsigned int started_peers;
1353   unsigned int peer_cnt;
1354
1355   op = dll_op->op;
1356   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1357   GNUNET_free (dll_op);
1358   if (NULL != emsg)
1359   {
1360     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1361          _("An operation has failed while starting peers: %s\n"), emsg);
1362     GNUNET_TESTBED_operation_done (op);
1363     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1364       GNUNET_SCHEDULER_cancel (abort_task);
1365     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1366     return;
1367   }
1368   GNUNET_TESTBED_operation_done (op);
1369   if (++started_peers == num_peers)
1370   {
1371     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1372     GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
1373                 "All peers started successfully in %s\n",
1374                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1375     result = GNUNET_OK;
1376
1377     peer_handles = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *) * num_peers);
1378     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1379       peer_handles[peer_cnt] = peers[peer_cnt].peer_handle;
1380
1381     state = STATE_PEERS_LINKING;
1382     GNUNET_SCHEDULER_add_now (&do_configure_topology, NULL);
1383   }
1384 }
1385
1386
1387 /**
1388  * Functions of this signature are called when a peer has been successfully
1389  * created
1390  *
1391  * @param cls the closure from GNUNET_TESTBED_peer_create()
1392  * @param peer the handle for the created peer; NULL on any error during
1393  *          creation
1394  * @param emsg NULL if peer is not NULL; else MAY contain the error description
1395  */
1396 static void
1397 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
1398 {
1399   struct DLLOperation *dll_op = cls;
1400   struct RegexPeer *peer_ptr;
1401   static unsigned int created_peers;
1402   unsigned int peer_cnt;
1403
1404   if (NULL != emsg)
1405   {
1406     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1407          _("Creating a peer failed. Error: %s\n"), emsg);
1408     GNUNET_TESTBED_operation_done (dll_op->op);
1409     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1410     GNUNET_free (dll_op);
1411     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1412       GNUNET_SCHEDULER_cancel (abort_task);
1413     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1414     return;
1415   }
1416
1417   peer_ptr = dll_op->cls;
1418   GNUNET_assert (NULL == peer_ptr->peer_handle);
1419   GNUNET_CONFIGURATION_destroy (peer_ptr->cfg);
1420   peer_ptr->cfg = NULL;
1421   peer_ptr->peer_handle = peer;
1422   GNUNET_TESTBED_operation_done (dll_op->op);
1423   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1424   GNUNET_free (dll_op);
1425
1426   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %i created on host %s\n",
1427               peer_ptr->id,
1428               GNUNET_TESTBED_host_get_hostname (peer_ptr->host_handle));
1429
1430   if (++created_peers == num_peers)
1431   {
1432     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1433     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1434                 "All peers created successfully in %s\n",
1435                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1436     /* Now peers are to be started */
1437     state = STATE_PEERS_STARTING;
1438     prof_start_time = GNUNET_TIME_absolute_get ();
1439     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1440     {
1441       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1442       dll_op->op = GNUNET_TESTBED_peer_start (dll_op,
1443                                               peers[peer_cnt].peer_handle,
1444                                               &peer_churn_cb, dll_op);
1445       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1446     }
1447   }
1448 }
1449
1450
1451 /**
1452  * Function called with a filename for each file in the policy directory. Create
1453  * a peer for each filename and update the peer's configuration to include the
1454  * max_path_compression specified as a command line argument as well as the
1455  * policy_file for this peer. The gnunet-service-regexprofiler service is
1456  * automatically started on this peer. The service reads the configurration and
1457  * announces the regexes stored in the policy file 'filename'.
1458  *
1459  * @param cls closure
1460  * @param filename complete filename (absolute path)
1461  * @return GNUNET_OK to continue to iterate,
1462  *  GNUNET_SYSERR to abort iteration with error!
1463  */
1464 static int
1465 policy_filename_cb (void *cls, const char *filename)
1466 {
1467   static unsigned int peer_cnt;
1468   struct DLLOperation *dll_op;
1469   struct RegexPeer *peer = &peers[peer_cnt];
1470
1471   GNUNET_assert (NULL != peer);
1472
1473   peer->policy_file = GNUNET_strdup (filename);
1474
1475   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1476               "Creating peer %i on host %s for policy file %s\n",
1477               peer->id, GNUNET_TESTBED_host_get_hostname (peer->host_handle),
1478               filename);
1479
1480   /* Set configuration options specific for this peer
1481      (max_path_compression and policy_file */
1482   peer->cfg = GNUNET_CONFIGURATION_dup (cfg);
1483   GNUNET_CONFIGURATION_set_value_number (peer->cfg, "REGEXPROFILER",
1484                                          "MAX_PATH_COMPRESSION",
1485                                          (unsigned long long)
1486                                          max_path_compression);
1487   GNUNET_CONFIGURATION_set_value_string (peer->cfg, "REGEXPROFILER",
1488                                          "POLICY_FILE", filename);
1489
1490   dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1491   dll_op->cls = &peers[peer_cnt];
1492   dll_op->op = GNUNET_TESTBED_peer_create (mc,
1493                                            peer->host_handle,
1494                                            peer->cfg,
1495                                            &peer_create_cb,
1496                                            dll_op);
1497   GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1498
1499   peer_cnt++;
1500
1501   return GNUNET_OK;
1502 }
1503
1504
1505 /**
1506  * Controller event callback.
1507  *
1508  * @param cls NULL
1509  * @param event the controller event
1510  */
1511 static void
1512 controller_event_cb (void *cls,
1513                      const struct GNUNET_TESTBED_EventInformation *event)
1514 {
1515   struct DLLOperation *dll_op;
1516   struct GNUNET_TESTBED_Operation *op;
1517   int ret;
1518
1519   switch (state)
1520   {
1521   case STATE_SLAVES_STARTING:
1522     switch (event->type)
1523     {
1524     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1525       {
1526         static unsigned int slaves_started;
1527         unsigned int peer_cnt;
1528
1529         dll_op = event->op_cls;
1530         GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1531         GNUNET_free (dll_op);
1532         op = event->op;
1533         if (NULL != event->details.operation_finished.emsg)
1534         {
1535           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1536                _("An operation has failed while starting slaves\n"));
1537           GNUNET_TESTBED_operation_done (op);
1538           if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1539             GNUNET_SCHEDULER_cancel (abort_task);
1540           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1541           return;
1542         }
1543         GNUNET_TESTBED_operation_done (op);
1544         /* Proceed to start peers */
1545         if (++slaves_started == num_hosts - 1)
1546         {
1547           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1548                       "All slaves started successfully\n");
1549
1550           state = STATE_PEERS_CREATING;
1551           prof_start_time = GNUNET_TIME_absolute_get ();
1552
1553           if (-1 == (ret = GNUNET_DISK_directory_scan (policy_dir,
1554                                                        NULL,
1555                                                        NULL)))
1556           {
1557             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1558                         _("No files found in `%s'\n"),
1559                         policy_dir);
1560             GNUNET_SCHEDULER_shutdown ();
1561             return;
1562           }
1563           num_peers = (unsigned int) ret;
1564           peers = GNUNET_malloc (sizeof (struct RegexPeer) * num_peers);
1565
1566           /* Initialize peers */
1567           for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1568           {
1569             struct RegexPeer *peer = &peers[peer_cnt];
1570             peer->id = peer_cnt;
1571             peer->policy_file = NULL;
1572             /* Do not start peers on hosts[0] (master controller) */
1573             peer->host_handle = hosts[1 + (peer_cnt % (num_hosts -1))];
1574             peer->dht_handle = NULL;
1575             peer->search_handle = NULL;
1576             peer->stats_handle = NULL;
1577             peer->stats_op_handle = NULL;
1578             peer->search_str = NULL;
1579             peer->search_str_matched = GNUNET_NO;
1580           }
1581
1582           GNUNET_DISK_directory_scan (policy_dir,
1583                                       &policy_filename_cb,
1584                                       NULL);
1585         }
1586       }
1587       break;
1588     default:
1589       GNUNET_assert (0);
1590     }
1591     break;
1592   case STATE_PEERS_STARTING:
1593     switch (event->type)
1594     {
1595     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1596       /* Control reaches here when peer start fails */
1597     case GNUNET_TESTBED_ET_PEER_START:
1598       /* we handle peer starts in peer_churn_cb */
1599       break;
1600     default:
1601       GNUNET_assert (0);
1602     }
1603     break;
1604   case STATE_PEERS_LINKING:
1605    switch (event->type)
1606    {
1607      static unsigned int established_links;
1608    case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1609      /* Control reaches here when a peer linking operation fails */
1610      if (NULL != event->details.operation_finished.emsg)
1611      {
1612        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1613                    _("An operation has failed while linking\n"));
1614        printf ("F%u/%u(%s)",
1615                retry_links + 1, established_links + 1, 
1616                event->details.operation_finished.emsg);
1617        fflush (stdout);
1618        retry_links++;
1619      }
1620      /* We do no retries, consider this link as established */
1621      /* break; */
1622    case GNUNET_TESTBED_ET_CONNECT:
1623    {
1624      char output_buffer[1024];
1625      size_t size;
1626
1627      if (0 == established_links)
1628        GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Establishing links .");
1629      else
1630      {
1631        printf (".");fflush (stdout);
1632      }
1633      if (++established_links == num_links)
1634      {
1635        prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1636        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1637                    "%u links established in %s\n",
1638                    num_links,
1639                    GNUNET_STRINGS_relative_time_to_string (prof_time,
1640                                                            GNUNET_NO));
1641        prof_time = GNUNET_TIME_relative_divide(prof_time, num_links);
1642        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1643                    "Average of %s per connection\n",
1644                    GNUNET_STRINGS_relative_time_to_string (prof_time,
1645                                                            GNUNET_NO));
1646        result = GNUNET_OK;
1647        GNUNET_free (peer_handles);
1648
1649        if (NULL != data_file)
1650        {
1651          size =
1652            GNUNET_snprintf (output_buffer,
1653                             sizeof (output_buffer),
1654                             "# of peers: %u\n# of links established: %u\n"
1655                             "Time to establish links: %s\n"
1656                             "Linking failures: %u\n"
1657                             "path compression length: %u\n"
1658                             "# of search strings: %u\n",
1659                             num_peers,
1660                             (established_links - retry_links),
1661                             GNUNET_STRINGS_relative_time_to_string (prof_time,
1662                                                                     GNUNET_NO),
1663                             retry_links,
1664                             max_path_compression,
1665                             num_search_strings);
1666
1667          if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
1668            GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
1669        }
1670
1671        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1672                    "\nWaiting %s before starting to announce.\n",
1673                    GNUNET_STRINGS_relative_time_to_string (search_delay,
1674                                                            GNUNET_NO));
1675        state = STATE_SEARCH_REGEX;
1676        search_task = GNUNET_SCHEDULER_add_delayed (search_delay,
1677                                                    &do_announce, NULL);
1678      }
1679    }
1680    break;
1681    default:
1682      GNUNET_assert (0);
1683    }
1684    break;
1685   case STATE_SEARCH_REGEX:
1686   {
1687     /* Handled in service connect callback */
1688     break;
1689   }
1690   default:
1691     switch (state)
1692     {
1693     case STATE_PEERS_CREATING:
1694       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create peer\n");
1695       break;
1696     default:
1697       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1698                   "Unexpected controller_cb with state %i!\n", state);
1699     }
1700     GNUNET_assert (0);
1701   }
1702 }
1703
1704
1705 /**
1706  * Task to register all hosts available in the global host list.
1707  *
1708  * @param cls NULL
1709  * @param tc the scheduler task context
1710  */
1711 static void
1712 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1713
1714
1715 /**
1716  * Callback which will be called to after a host registration succeeded or failed
1717  *
1718  * @param cls the closure
1719  * @param emsg the error message; NULL if host registration is successful
1720  */
1721 static void
1722 host_registration_completion (void *cls, const char *emsg)
1723 {
1724   reg_handle = NULL;
1725   if (NULL != emsg)
1726   {
1727     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1728                 _("Host registration failed for a host. Error: %s\n"), emsg);
1729     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1730       GNUNET_SCHEDULER_cancel (abort_task);
1731     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1732     return;
1733   }
1734   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1735 }
1736
1737
1738 /**
1739  * Task to register all hosts available in the global host list.
1740  *
1741  * @param cls NULL
1742  * @param tc the scheduler task context
1743  */
1744 static void
1745 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1746 {
1747   struct DLLOperation *dll_op;
1748   static unsigned int reg_host;
1749   unsigned int slave;
1750
1751   register_hosts_task = GNUNET_SCHEDULER_NO_TASK;
1752   if (reg_host == num_hosts - 1)
1753   {
1754     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1755                 "All hosts successfully registered\n");
1756     /* Start slaves */
1757     state = STATE_SLAVES_STARTING;
1758     for (slave = 1; slave < num_hosts; slave++)
1759     {
1760       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1761       dll_op->op = GNUNET_TESTBED_controller_link (dll_op,
1762                                                    mc,
1763                                                    hosts[slave],
1764                                                    hosts[0],
1765                                                    cfg,
1766                                                    GNUNET_YES);
1767       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1768     }
1769     return;
1770   }
1771   reg_handle = GNUNET_TESTBED_register_host (mc, hosts[++reg_host],
1772                                              host_registration_completion,
1773                                              NULL);
1774 }
1775
1776
1777 /**
1778  * Callback to signal successfull startup of the controller process.
1779  *
1780  * @param cls the closure from GNUNET_TESTBED_controller_start()
1781  * @param config the configuration with which the controller has been started;
1782  *          NULL if status is not GNUNET_OK
1783  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
1784  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
1785  */
1786 static void
1787 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
1788 {
1789   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1790     GNUNET_SCHEDULER_cancel (abort_task);
1791   if (GNUNET_OK != status)
1792   {
1793     mc_proc = NULL;
1794     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Oh, dear!\n");
1795     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1796     return;
1797   }
1798   event_mask = 0;
1799   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
1800   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
1801   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1802   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
1803   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
1804   mc = GNUNET_TESTBED_controller_connect (config, hosts[0], event_mask,
1805                                           &controller_event_cb, NULL);
1806   if (NULL == mc)
1807   {
1808     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1809                 _("Unable to connect to master controller -- Check config\n"));
1810     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1811     return;
1812   }
1813   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1814   abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1815                                              &do_abort, (void*) __LINE__);
1816 }
1817
1818
1819 /**
1820  * Load search strings from given filename. One search string per line.
1821  *
1822  * @param filename filename of the file containing the search strings.
1823  * @param strings set of strings loaded from file. Caller needs to free this
1824  *                if number returned is greater than zero.
1825  * @param limit upper limit on the number of strings read from the file
1826  * @return number of strings found in the file. GNUNET_SYSERR on error.
1827  */
1828 static int
1829 load_search_strings (const char *filename, char ***strings, unsigned int limit)
1830 {
1831   char *data;
1832   char *buf;
1833   uint64_t filesize;
1834   unsigned int offset;
1835   int str_cnt;
1836   unsigned int i;
1837
1838   if (NULL == filename)
1839   {
1840     return GNUNET_SYSERR;
1841   }
1842
1843   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
1844   {
1845     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1846                 "Could not find search strings file %s\n", filename);
1847     return GNUNET_SYSERR;
1848   }
1849   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &filesize, GNUNET_YES, GNUNET_YES))
1850     filesize = 0;
1851   if (0 == filesize)
1852   {
1853     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Search strings file %s is empty.\n", filename);
1854     return GNUNET_SYSERR;
1855   }
1856   data = GNUNET_malloc (filesize);
1857   if (filesize != GNUNET_DISK_fn_read (filename, data, filesize))
1858   {
1859     GNUNET_free (data);
1860     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read search strings file %s.\n",
1861          filename);
1862     return GNUNET_SYSERR;
1863   }
1864   buf = data;
1865   offset = 0;
1866   str_cnt = 0;
1867   while (offset < (filesize - 1) && str_cnt < limit)
1868   {
1869     offset++;
1870     if (((data[offset] == '\n')) && (buf != &data[offset]))
1871     {
1872       data[offset] = '\0';
1873       str_cnt++;
1874       buf = &data[offset + 1];
1875     }
1876     else if ((data[offset] == '\n') || (data[offset] == '\0'))
1877       buf = &data[offset + 1];
1878   }
1879   *strings = GNUNET_malloc (sizeof (char *) * str_cnt);
1880   offset = 0;
1881   for (i = 0; i < str_cnt; i++)
1882   {
1883     GNUNET_asprintf (&(*strings)[i], "%s%s", regex_prefix, &data[offset]);
1884     offset += strlen (&data[offset]) + 1;
1885   }
1886   GNUNET_free (data);
1887   return str_cnt;
1888 }
1889
1890
1891 /**
1892  * Callbacks of this type are called by GNUNET_TESTBED_is_host_habitable to
1893  * inform whether the given host is habitable or not. The Handle returned by
1894  * GNUNET_TESTBED_is_host_habitable() is invalid after this callback is called
1895  *
1896  * @param cls NULL
1897  * @param host the host whose status is being reported; will be NULL if the host
1898  *          given to GNUNET_TESTBED_is_host_habitable() is NULL
1899  * @param status GNUNET_YES if it is habitable; GNUNET_NO if not
1900  */
1901 static void 
1902 host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *host, int status)
1903 {
1904   struct GNUNET_TESTBED_HostHabitableCheckHandle **hc_handle = cls;
1905   static unsigned int hosts_checked;
1906
1907   *hc_handle = NULL;
1908   if (GNUNET_NO == status)
1909   {
1910     if ((NULL != host) && (NULL != GNUNET_TESTBED_host_get_hostname (host)))
1911       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Host %s cannot start testbed\n"),
1912                   GNUNET_TESTBED_host_get_hostname (host));
1913     else
1914       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Testbed cannot be started on localhost\n"));
1915     GNUNET_SCHEDULER_cancel (abort_task);
1916     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1917     return;
1918   }
1919   hosts_checked++;
1920   /* printf (_("\rChecked %u hosts"), hosts_checked); */
1921   /* fflush (stdout); */
1922   if (hosts_checked < num_hosts)
1923     return;
1924   /* printf (_("\nAll hosts can start testbed. Creating peers\n")); */
1925   GNUNET_free (hc_handles);
1926   hc_handles = NULL;
1927   mc_proc = 
1928       GNUNET_TESTBED_controller_start (GNUNET_TESTBED_host_get_hostname
1929                                        (hosts[0]),
1930                                        hosts[0],
1931                                        cfg,
1932                                        status_cb,
1933                                        NULL);
1934 }
1935
1936
1937 /**
1938  * Main function that will be run by the scheduler.
1939  *
1940  * @param cls closure
1941  * @param args remaining command-line arguments
1942  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1943  * @param config configuration
1944  */
1945 static void
1946 run (void *cls, char *const *args, const char *cfgfile,
1947      const struct GNUNET_CONFIGURATION_Handle *config)
1948 {
1949   unsigned int nhost;
1950   unsigned int nsearchstrs;
1951
1952   if (NULL == args[0])
1953   {
1954     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1955                 _("No hosts-file specified on command line. Exiting.\n"));
1956     return;
1957   }
1958   if (NULL == args[1])
1959   {
1960     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1961                 _("No policy directory specified on command line. Exiting.\n"));
1962     return;
1963   }
1964   num_hosts = GNUNET_TESTBED_hosts_load_from_file (args[0], config, &hosts);
1965   if (0 == num_hosts)
1966   {
1967     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1968                 _("No hosts loaded. Need at least one host\n"));
1969     return;
1970   }
1971   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
1972               _("Checking whether given hosts can start testbed."
1973                 "Please wait\n"));
1974   hc_handles = GNUNET_malloc (sizeof (struct
1975                                       GNUNET_TESTBED_HostHabitableCheckHandle *) 
1976                               * num_hosts);
1977   for (nhost = 0; nhost < num_hosts; nhost++)
1978   {
1979     hc_handles[nhost] = GNUNET_TESTBED_is_host_habitable (hosts[nhost], config,
1980                                                           &host_habitable_cb,
1981                                                           &hc_handles[nhost]);
1982     if (NULL == hc_handles[nhost])
1983     {
1984       int i;
1985
1986       GNUNET_break (0);
1987       for (i = 0; i <= nhost; i++)
1988         if (NULL != hc_handles[i])
1989           GNUNET_TESTBED_is_host_habitable_cancel (hc_handles[i]);
1990       GNUNET_free (hc_handles);
1991       hc_handles = NULL;
1992       break;
1993     }
1994   }
1995   if (num_hosts != nhost)
1996   {
1997     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Exiting\n"));
1998     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1999     return;
2000   }
2001   if (NULL == config)
2002   {
2003     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2004                 _("No configuration file given. Exiting\n"));
2005     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2006     return;
2007   }
2008
2009   if (GNUNET_OK !=
2010       GNUNET_CONFIGURATION_get_value_string (config, "REGEXPROFILER", "REGEX_PREFIX",
2011                                              &regex_prefix))
2012   {
2013     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2014                 _("Configuration option \"regex_prefix\" missing. Exiting\n"));
2015     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2016     return;
2017   }
2018
2019   if ( (NULL != data_filename) &&
2020        (NULL == (data_file =
2021                  GNUNET_DISK_file_open (data_filename,
2022                                         GNUNET_DISK_OPEN_READWRITE |
2023                                         GNUNET_DISK_OPEN_TRUNCATE |
2024                                         GNUNET_DISK_OPEN_CREATE,
2025                                         GNUNET_DISK_PERM_USER_READ |
2026                                         GNUNET_DISK_PERM_USER_WRITE))) )
2027     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2028                               "open",
2029                               data_filename);
2030   if (GNUNET_YES != GNUNET_DISK_directory_test (args[1], GNUNET_YES))
2031   {
2032     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2033                 _("Specified policies directory does not exist. Exiting.\n"));
2034     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2035     return;
2036   }
2037   policy_dir = args[1];
2038   if (GNUNET_YES != GNUNET_DISK_file_test (args[2]))
2039   {
2040     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2041                 _("No search strings file given. Exiting.\n"));
2042     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2043     return;
2044   }
2045   nsearchstrs = load_search_strings (args[2], &search_strings, num_search_strings);
2046   if (num_search_strings != nsearchstrs)
2047   {
2048     num_search_strings = nsearchstrs;
2049     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2050                 _("Error loading search strings."
2051                   "Given file does not contain enough strings. Exiting.\n"));
2052     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2053     return;
2054   }
2055   if (0 >= num_search_strings || NULL == search_strings)
2056   {
2057     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2058                 _("Error loading search strings. Exiting.\n"));
2059     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
2060     return;
2061   }
2062   unsigned int i;
2063   for (i = 0; i < num_search_strings; i++)
2064     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "search string: %s\n", search_strings[i]);
2065   cfg = GNUNET_CONFIGURATION_dup (config);
2066   abort_task =
2067       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
2068                                     (GNUNET_TIME_UNIT_SECONDS, 5), &do_abort,
2069                                     (void*) __LINE__);
2070 }
2071
2072
2073 /**
2074  * Main function.
2075  *
2076  * @param argc argument count
2077  * @param argv argument values
2078  * @return 0 on success
2079  */
2080 int
2081 main (int argc, char *const *argv)
2082 {
2083   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
2084     {'d', "details", "FILENAME",
2085      gettext_noop ("name of the file for writing statistics"),
2086      1, &GNUNET_GETOPT_set_string, &data_filename},
2087     {'n', "num-links", "COUNT",
2088       gettext_noop ("create COUNT number of random links between peers"),
2089       GNUNET_YES, &GNUNET_GETOPT_set_uint, &linking_factor },
2090     {'t', "matching-timeout", "TIMEOUT",
2091       gettext_noop ("wait TIMEOUT before considering a string match as failed"),
2092       GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &search_timeout_time
2093         },
2094     {'s', "search-delay", "DELAY",
2095       gettext_noop ("wait DELAY before starting string search"),
2096       GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &search_delay },
2097     {'a', "num-search-strings", "COUNT",
2098       gettext_noop ("number of search strings to read from search strings file"),
2099       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_search_strings },
2100     {'p', "max-path-compression", "MAX_PATH_COMPRESSION",
2101      gettext_noop ("maximum path compression length"),
2102      1, &GNUNET_GETOPT_set_uint, &max_path_compression},
2103     GNUNET_GETOPT_OPTION_END
2104   };
2105   int ret;
2106
2107   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
2108     return 2;
2109
2110   result = GNUNET_SYSERR;
2111   ret =
2112       GNUNET_PROGRAM_run (argc, argv,
2113                           "gnunet-regex-profiler [OPTIONS] hosts-file policy-dir search-strings-file",
2114                           _("Profiler for regex"),
2115                           options, &run, NULL);
2116
2117   if (GNUNET_OK != ret)
2118     return ret;
2119   if (GNUNET_OK != result)
2120     return 1;
2121   return 0;
2122 }