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