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