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