Merge branch 'master' of ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / regex / gnunet-regex-profiler.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011 - 2017 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file regex/gnunet-regex-profiler.c
21  * @brief Regex profiler for testing distributed regex use.
22  * @author Bartlomiej Polot
23  * @author Maximilian Szengel
24  *
25  */
26
27 #include <string.h>
28
29 #include "platform.h"
30 #include "gnunet_applications.h"
31 #include "gnunet_util_lib.h"
32 #include "regex_internal_lib.h"
33 #include "gnunet_arm_service.h"
34 #include "gnunet_dht_service.h"
35 #include "gnunet_testbed_service.h"
36
37 #define FIND_TIMEOUT \
38         GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
39
40 /**
41  * DLL of operations
42  */
43 struct DLLOperation
44 {
45   /**
46    * The testbed operation handle
47    */
48   struct GNUNET_TESTBED_Operation *op;
49
50   /**
51    * Closure
52    */
53   void *cls;
54
55   /**
56    * The next pointer for DLL
57    */
58   struct DLLOperation *next;
59
60   /**
61    * The prev pointer for DLL
62    */
63   struct DLLOperation *prev;
64 };
65
66
67 /**
68  * Available states during profiling
69  */
70 enum State
71 {
72   /**
73    * Initial state
74    */
75   STATE_INIT = 0,
76
77   /**
78    * Starting slaves
79    */
80   STATE_SLAVES_STARTING,
81
82   /**
83    * Creating peers
84    */
85   STATE_PEERS_CREATING,
86
87   /**
88    * Starting peers
89    */
90   STATE_PEERS_STARTING,
91
92   /**
93    * Linking peers
94    */
95   STATE_PEERS_LINKING,
96
97   /**
98    * Matching strings against announced regexes
99    */
100   STATE_SEARCH_REGEX,
101
102   /**
103    * Destroying peers; we can do this as the controller takes care of stopping a
104    * peer if it is running
105    */
106   STATE_PEERS_DESTROYING
107 };
108
109
110 /**
111  * Peer handles.
112  */
113 struct RegexPeer
114 {
115   /**
116    * Peer id.
117    */
118   unsigned int id;
119
120   /**
121    * Peer configuration handle.
122    */
123   struct GNUNET_CONFIGURATION_Handle *cfg;
124
125   /**
126    * The actual testbed peer handle.
127    */
128   struct GNUNET_TESTBED_Peer *peer_handle;
129
130   /**
131    * Peer's search string.
132    */
133   const char *search_str;
134
135   /**
136    * Set to GNUNET_YES if the peer successfully matched the above
137    * search string. GNUNET_NO if the string could not be matched
138    * during the profiler run. GNUNET_SYSERR if the string matching
139    * timed out. Undefined if search_str is NULL
140    */
141   int search_str_matched;
142
143   /**
144    * Peer's DHT handle.
145    */
146   struct GNUNET_DHT_Handle *dht_handle;
147
148   /**
149    * Handle to a running regex search.
150    */
151    struct REGEX_INTERNAL_Search *search_handle;
152
153   /**
154    * Testbed operation handle for DHT.
155    */
156   struct GNUNET_TESTBED_Operation *op_handle;
157
158   /**
159    * Peers's statistics handle.
160    */
161   struct GNUNET_STATISTICS_Handle *stats_handle;
162
163   /**
164    * The starting time of a profiling step.
165    */
166   struct GNUNET_TIME_Absolute prof_start_time;
167
168   /**
169    * Operation timeout
170    */
171   struct GNUNET_SCHEDULER_Task * timeout;
172
173   /**
174    * Deamon start
175    */
176   struct GNUNET_TESTBED_Operation *daemon_op;
177 };
178
179 /**
180  * Set when shutting down to avoid making more queries.
181  */
182 static int in_shutdown;
183
184 /**
185  * The array of peers; we fill this as the peers are given to us by the testbed
186  */
187 static struct RegexPeer *peers;
188
189 /**
190  * Host registration handle
191  */
192 static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
193
194 /**
195  * Handle to the master controller process
196  */
197 static struct GNUNET_TESTBED_ControllerProc *mc_proc;
198
199 /**
200  * Handle to the master controller
201  */
202 static struct GNUNET_TESTBED_Controller *mc;
203
204 /**
205  * Handle to global configuration
206  */
207 static struct GNUNET_CONFIGURATION_Handle *cfg;
208
209 /**
210  * Abort task identifier
211  */
212 static struct GNUNET_SCHEDULER_Task * abort_task;
213
214 /**
215  * Host registration task identifier
216  */
217 static struct GNUNET_SCHEDULER_Task * register_hosts_task;
218
219 /**
220  * Global event mask for all testbed events
221  */
222 static uint64_t event_mask;
223
224 /**
225  * The starting time of a profiling step
226  */
227 static struct GNUNET_TIME_Absolute prof_start_time;
228
229 /**
230  * Duration profiling step has taken
231  */
232 static struct GNUNET_TIME_Relative prof_time;
233
234 /**
235  * Number of peers to be started by the profiler
236  */
237 static unsigned int num_peers;
238
239 /**
240  * Global testing status
241  */
242 static int result;
243
244 /**
245  * current state of profiling
246  */
247 enum State state;
248
249 /**
250  * Folder where policy files are stored.
251  */
252 static char * policy_dir;
253
254 /**
255  * File with hostnames where to execute the test.
256  */
257 static char *hosts_file;
258
259 /**
260  * File with the strings to look for.
261  */
262 static char *strings_file;
263
264 /**
265  * Search strings (num_peers of them).
266  */
267 static char **search_strings;
268
269 /**
270  * How many searches are we going to start in parallel
271  */
272 static long long unsigned int init_parallel_searches;
273
274 /**
275  * How many searches are running in parallel
276  */
277 static unsigned int parallel_searches;
278
279 /**
280  * Number of strings found in the published regexes.
281  */
282 static unsigned int strings_found;
283
284 /**
285  * Index of peer to start next announce/search.
286  */
287 static unsigned int next_search;
288
289 /**
290  * Search timeout task identifier.
291  */
292 static struct GNUNET_SCHEDULER_Task * search_timeout_task;
293
294 /**
295  * Search timeout in seconds.
296  */
297 static struct GNUNET_TIME_Relative search_timeout_time = { 60000 };
298
299 /**
300  * File to log statistics to.
301  */
302 static struct GNUNET_DISK_FileHandle *data_file;
303
304 /**
305  * Filename to log statistics to.
306  */
307 static char *data_filename;
308
309 /**
310  * Prefix used for regex announcing. We need to prefix the search
311  * strings with it, in order to find something.
312  */
313 static char * regex_prefix;
314
315 /**
316  * What's the maximum regex reannounce period.
317  */
318 static struct GNUNET_TIME_Relative reannounce_period_max;
319
320
321 /******************************************************************************/
322 /******************************  DECLARATIONS  ********************************/
323 /******************************************************************************/
324
325 /**
326  * DHT connect callback.
327  *
328  * @param cls internal peer id.
329  * @param op operation handle.
330  * @param ca_result connect adapter result.
331  * @param emsg error message.
332  */
333 static void
334 dht_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
335                 void *ca_result, const char *emsg);
336
337 /**
338  * DHT connect adapter.
339  *
340  * @param cls not used.
341  * @param cfg configuration handle.
342  *
343  * @return
344  */
345 static void *
346 dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg);
347
348
349 /**
350  * Adapter function called to destroy a connection to
351  * the DHT service
352  *
353  * @param cls closure
354  * @param op_result service handle returned from the connect adapter
355  */
356 static void
357 dht_da (void *cls, void *op_result);
358
359
360 /**
361  * Function called by testbed once we are connected to stats
362  * service. Get the statistics for the services of interest.
363  *
364  * @param cls the 'struct RegexPeer' for which we connected to stats
365  * @param op connect operation handle
366  * @param ca_result handle to stats service
367  * @param emsg error message on failure
368  */
369 static void
370 stats_connect_cb (void *cls,
371                   struct GNUNET_TESTBED_Operation *op,
372                   void *ca_result,
373                   const char *emsg);
374
375
376 /**
377  * Start announcing the next regex in the DHT.
378  *
379  * @param cls Index of the next peer in the peers array.
380  */
381 static void
382 announce_next_regex (void *cls);
383
384
385 /******************************************************************************/
386 /********************************  SHUTDOWN  **********************************/
387 /******************************************************************************/
388
389
390 /**
391  * Shutdown nicely
392  *
393  * @param cls NULL
394  */
395 static void
396 do_shutdown (void *cls)
397 {
398   struct RegexPeer *peer;
399   unsigned int peer_cnt;
400   unsigned int search_str_cnt;
401   char output_buffer[512];
402   size_t size;
403
404   if (NULL != abort_task)
405   {
406     GNUNET_SCHEDULER_cancel (abort_task);
407     abort_task = NULL;
408   }
409   if (NULL != register_hosts_task)
410   {
411     GNUNET_SCHEDULER_cancel (register_hosts_task);
412     register_hosts_task = NULL;
413   }
414   for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
415   {
416     peer = &peers[peer_cnt];
417
418     if (GNUNET_YES != peer->search_str_matched && NULL != data_file)
419     {
420       prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time);
421       size =
422         GNUNET_snprintf (output_buffer,
423                          sizeof (output_buffer),
424                          "%p Search string not found: %s (%d)\n"
425                          "%p On peer: %u (%p)\n"
426                          "%p After: %s\n",
427                          peer, peer->search_str, peer->search_str_matched,
428                          peer, peer->id, peer,
429                          peer,
430                          GNUNET_STRINGS_relative_time_to_string (prof_time,
431                                                                  GNUNET_NO));
432       if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
433         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
434     }
435
436     if (NULL != peers[peer_cnt].op_handle)
437       GNUNET_TESTBED_operation_done (peers[peer_cnt].op_handle);
438   }
439
440   if (NULL != data_file)
441   {
442     GNUNET_DISK_file_close (data_file);
443     data_file = NULL;
444   }
445   for (search_str_cnt = 0;
446        search_str_cnt < num_peers && NULL != search_strings;
447        search_str_cnt++)
448   {
449     GNUNET_free_non_null (search_strings[search_str_cnt]);
450   }
451   GNUNET_free_non_null (search_strings);
452   search_strings = NULL;
453
454   if (NULL != reg_handle)
455   {
456     GNUNET_TESTBED_cancel_registration (reg_handle);
457     reg_handle = NULL;
458   }
459   if (NULL != mc)
460   {
461     GNUNET_TESTBED_controller_disconnect (mc);
462     mc = NULL;
463   }
464   if (NULL != mc_proc)
465   {
466     GNUNET_TESTBED_controller_stop (mc_proc);
467     mc_proc = NULL;
468   }
469   if (NULL != cfg)
470   {
471     GNUNET_CONFIGURATION_destroy (cfg);
472     cfg = NULL;
473   }
474 }
475
476
477 /**
478  * abort task to run on test timed out
479  *
480  * @param cls NULL
481  */
482 static void
483 do_abort (void *cls)
484 {
485   unsigned long i = (unsigned long) cls;
486
487   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
488               "Aborting from line %lu...\n", i);
489   abort_task = NULL;
490   result = GNUNET_SYSERR;
491   GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
492 }
493
494
495 /******************************************************************************/
496 /*********************  STATISTICS SERVICE CONNECTIONS  ***********************/
497 /******************************************************************************/
498
499 /**
500  * Adapter function called to establish a connection to
501  * statistics service.
502  *
503  * @param cls closure
504  * @param cfg configuration of the peer to connect to; will be available until
505  *          GNUNET_TESTBED_operation_done() is called on the operation returned
506  *          from GNUNET_TESTBED_service_connect()
507  * @return service handle to return in 'op_result', NULL on error
508  */
509 static void *
510 stats_ca (void *cls,
511           const struct GNUNET_CONFIGURATION_Handle *cfg)
512 {
513   return GNUNET_STATISTICS_create ("<driver>", cfg);
514 }
515
516
517 /**
518  * Adapter function called to destroy a connection to
519  * statistics service.
520  *
521  * @param cls closure
522  * @param op_result service handle returned from the connect adapter
523  */
524 static void
525 stats_da (void *cls, void *op_result)
526 {
527   struct RegexPeer *peer = cls;
528
529   GNUNET_assert (op_result == peer->stats_handle);
530
531   GNUNET_STATISTICS_destroy (peer->stats_handle, GNUNET_NO);
532   peer->stats_handle = NULL;
533 }
534
535
536 /**
537  * Process statistic values. Write all values to global 'data_file', if present.
538  *
539  * @param cls closure
540  * @param subsystem name of subsystem that created the statistic
541  * @param name the name of the datum
542  * @param value the current value
543  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
544  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
545  */
546 static int
547 stats_iterator (void *cls,
548                 const char *subsystem,
549                 const char *name,
550                 uint64_t value, int is_persistent)
551 {
552   struct RegexPeer *peer = cls;
553   char output_buffer[512];
554   size_t size;
555
556   if (NULL == data_file)
557   {
558     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
559                 "%p -> %s [%s]: %llu\n",
560                 peer,
561                 subsystem,
562                 name,
563                 (unsigned long long) value);
564     return GNUNET_OK;
565   }
566   size =
567     GNUNET_snprintf (output_buffer,
568                      sizeof (output_buffer),
569                      "%p [%s] %llu %s\n",
570                      peer,
571                      subsystem, value, name);
572   if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
573     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
574                 "Unable to write to file!\n");
575
576   return GNUNET_OK;
577 }
578
579
580 /**
581  * Stats callback. Finish the stats testbed operation and when all stats have
582  * been iterated, shutdown the profiler.
583  *
584  * @param cls closure
585  * @param success GNUNET_OK if statistics were
586  *        successfully obtained, GNUNET_SYSERR if not.
587  */
588 static void
589 stats_cb (void *cls,
590           int success)
591 {
592   static unsigned int peer_cnt;
593   struct RegexPeer *peer = cls;
594
595   if (GNUNET_OK != success)
596   {
597     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
598                 "Getting statistics for peer %u failed!\n",
599                 peer->id);
600     return;
601   }
602
603   GNUNET_assert (NULL != peer->op_handle);
604
605   GNUNET_TESTBED_operation_done (peer->op_handle);
606   peer->op_handle = NULL;
607
608   peer_cnt++;
609   peer = &peers[peer_cnt];
610
611   fprintf (stderr, "s");
612   if (peer_cnt == num_peers)
613   {
614     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
615                 "\nCollecting stats finished. Shutting down.\n");
616     GNUNET_SCHEDULER_shutdown ();
617     result = GNUNET_OK;
618   }
619   else
620   {
621     peer->op_handle =
622       GNUNET_TESTBED_service_connect (NULL,
623                                       peer->peer_handle,
624                                       "statistics",
625                                       &stats_connect_cb,
626                                       peer,
627                                       &stats_ca,
628                                       &stats_da,
629                                       peer);
630   }
631 }
632
633
634 /**
635  * Function called by testbed once we are connected to stats
636  * service. Get the statistics for the services of interest.
637  *
638  * @param cls the 'struct RegexPeer' for which we connected to stats
639  * @param op connect operation handle
640  * @param ca_result handle to stats service
641  * @param emsg error message on failure
642  */
643 static void
644 stats_connect_cb (void *cls,
645                   struct GNUNET_TESTBED_Operation *op,
646                   void *ca_result,
647                   const char *emsg)
648 {
649   struct RegexPeer *peer = cls;
650
651   if (NULL == ca_result || NULL != emsg)
652   {
653     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
654                 "Failed to connect to statistics service on peer %u: %s\n",
655                 peer->id, emsg);
656
657     peer->stats_handle = NULL;
658     return;
659   }
660
661   peer->stats_handle = ca_result;
662
663   if (NULL == GNUNET_STATISTICS_get (peer->stats_handle, NULL, NULL,
664                                      &stats_cb,
665                                      &stats_iterator, peer))
666   {
667     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
668                 "Could not get statistics of peer %u!\n", peer->id);
669   }
670 }
671
672
673 /**
674  * Task to collect all statistics from all peers, will shutdown the
675  * profiler, when done.
676  *
677  * @param cls NULL
678  */
679 static void
680 do_collect_stats (void *cls)
681 {
682   struct RegexPeer *peer = &peers[0];
683
684   GNUNET_assert (NULL != peer->peer_handle);
685
686   peer->op_handle =
687     GNUNET_TESTBED_service_connect (NULL,
688                                     peer->peer_handle,
689                                     "statistics",
690                                     &stats_connect_cb,
691                                     peer,
692                                     &stats_ca,
693                                     &stats_da,
694                                     peer);
695 }
696
697
698 /******************************************************************************/
699 /************************   REGEX FIND CONNECTIONS   **************************/
700 /******************************************************************************/
701
702
703 /**
704  * Start searching for the next string in the DHT.
705  *
706  * @param cls Index of the next peer in the peers array.
707  */
708 static void
709 find_string (void *cls);
710
711
712 /**
713  * Method called when we've found a peer that announced a regex
714  * that matches our search string. Now get the statistics.
715  *
716  * @param cls Closure provided in REGEX_INTERNAL_search.
717  * @param id Peer providing a regex that matches the string.
718  * @param get_path Path of the get request.
719  * @param get_path_length Lenght of get_path.
720  * @param put_path Path of the put request.
721  * @param put_path_length Length of the put_path.
722  */
723 static void
724 regex_found_handler (void *cls,
725                      const struct GNUNET_PeerIdentity *id,
726                      const struct GNUNET_PeerIdentity *get_path,
727                      unsigned int get_path_length,
728                      const struct GNUNET_PeerIdentity *put_path,
729                      unsigned int put_path_length)
730 {
731   struct RegexPeer *peer = cls;
732   char output_buffer[512];
733   size_t size;
734
735   if (GNUNET_YES == peer->search_str_matched)
736   {
737     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
738                 "String %s on peer %u already matched!\n",
739                 peer->search_str, peer->id);
740     return;
741   }
742
743   strings_found++;
744   parallel_searches--;
745
746   if (NULL != peer->timeout)
747   {
748     GNUNET_SCHEDULER_cancel (peer->timeout);
749     peer->timeout = NULL;
750     if (GNUNET_NO == in_shutdown)
751       GNUNET_SCHEDULER_add_now (&announce_next_regex, NULL);
752   }
753
754   if (NULL == id)
755   {
756     // FIXME not possible right now
757     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
758                 "String matching timed out for string %s on peer %u (%i/%i)\n",
759                 peer->search_str, peer->id, strings_found, num_peers);
760     peer->search_str_matched = GNUNET_SYSERR;
761   }
762   else
763   {
764     prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time);
765
766     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
767                 "String %s found on peer %u after %s (%i/%i) (%u||)\n",
768                 peer->search_str, peer->id,
769                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
770                 strings_found, num_peers, parallel_searches);
771
772     peer->search_str_matched = GNUNET_YES;
773
774     if (NULL != data_file)
775     {
776       size =
777         GNUNET_snprintf (output_buffer,
778                          sizeof (output_buffer),
779                          "%p Peer: %u\n"
780                          "%p Search string: %s\n"
781                          "%p Search duration: %s\n\n",
782                          peer, peer->id,
783                          peer, peer->search_str,
784                          peer,
785                          GNUNET_STRINGS_relative_time_to_string (prof_time,
786                                                                  GNUNET_NO));
787
788       if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
789         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
790     }
791   }
792
793   GNUNET_TESTBED_operation_done (peer->op_handle);
794   peer->op_handle = NULL;
795
796   if (strings_found == num_peers)
797   {
798     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
799     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
800                 "All strings successfully matched in %s\n",
801                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
802
803     if (NULL != search_timeout_task)
804     {
805       GNUNET_SCHEDULER_cancel (search_timeout_task);
806       search_timeout_task = NULL;
807     }
808
809     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Collecting stats.\n");
810     GNUNET_SCHEDULER_add_now (&do_collect_stats, NULL);
811   }
812 }
813
814
815 /**
816  * Connect by string timeout task. This will cancel the profiler after the
817  * specified timeout 'search_timeout'.
818  *
819  * @param cls NULL
820  */
821 static void
822 search_timed_out (void *cls)
823 {
824   unsigned int i;
825
826   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
827               "Finding matches to all strings did not succeed after %s.\n",
828               GNUNET_STRINGS_relative_time_to_string (search_timeout_time,
829                                                       GNUNET_NO));
830   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
831               "Found %i of %i strings\n", strings_found, num_peers);
832
833   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
834               "Search timed out after %s."
835               "Collecting stats and shutting down.\n",
836               GNUNET_STRINGS_relative_time_to_string (search_timeout_time,
837                                                       GNUNET_NO));
838
839   in_shutdown = GNUNET_YES;
840   for (i = 0; i < num_peers; i++)
841   {
842     if (NULL != peers[i].op_handle)
843     {
844       GNUNET_TESTBED_operation_done (peers[i].op_handle);
845       peers[i].op_handle = NULL;
846     }
847   }
848   GNUNET_SCHEDULER_add_now (&do_collect_stats, NULL);
849 }
850
851
852 /**
853  * Search timed out. It might still complete in the future,
854  * but we should start another one.
855  *
856  * @param cls Index of the next peer in the peers array.
857  */
858 static void
859 find_timed_out (void *cls)
860 {
861   struct RegexPeer *p = cls;
862
863   p->timeout = NULL;
864   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
865               "Searching for string \"%s\" on peer %d timed out.\n",
866               p->search_str,
867               p->id);
868   if (GNUNET_NO == in_shutdown)
869     GNUNET_SCHEDULER_add_now (&announce_next_regex, NULL);
870 }
871
872
873 /**
874  * Start searching for a string in the DHT.
875  *
876  * @param cls Index of the next peer in the peers array.
877  */
878 static void
879 find_string (void *cls)
880 {
881   unsigned int search_peer = (unsigned int) (long) cls;
882
883   if ( (search_peer >= num_peers) ||
884        (GNUNET_YES == in_shutdown) )
885     return;
886
887   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
888               "Searching for string \"%s\" on peer %d (%u||)\n",
889               peers[search_peer].search_str,
890               search_peer,
891               parallel_searches);
892
893   peers[search_peer].op_handle =
894     GNUNET_TESTBED_service_connect (NULL,
895                                     peers[search_peer].peer_handle,
896                                     "dht",
897                                     &dht_connect_cb,
898                                     &peers[search_peer],
899                                     &dht_ca,
900                                     &dht_da,
901                                     &peers[search_peer]);
902   GNUNET_assert (NULL != peers[search_peer].op_handle);
903   peers[search_peer].timeout
904     = GNUNET_SCHEDULER_add_delayed (FIND_TIMEOUT,
905                                     &find_timed_out,
906                                     &peers[search_peer]);
907 }
908
909
910 /**
911  * Callback called when testbed has started the daemon we asked for.
912  *
913  * @param cls NULL
914  * @param op the operation handle
915  * @param emsg NULL on success; otherwise an error description
916  */
917 static void
918 daemon_started (void *cls,
919                 struct GNUNET_TESTBED_Operation *op,
920                 const char *emsg)
921 {
922   struct RegexPeer *peer = (struct RegexPeer *) cls;
923   unsigned long search_peer;
924   unsigned int i;
925
926   GNUNET_TESTBED_operation_done (peer->daemon_op);
927   peer->daemon_op = NULL;
928   if (NULL != emsg)
929   {
930     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
931                 "Failed to start/stop daemon at peer %u: %s\n", peer->id, emsg);
932     GNUNET_assert (0);
933   }
934   else
935   {
936     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
937                 "Deamon %u started successfully\n", peer->id);
938   }
939
940   /* Find a peer to look for a string matching the regex announced */
941   search_peer = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
942                                           num_peers);
943   for (i = 0; peers[search_peer].search_str != NULL; i++)
944   {
945     search_peer = (search_peer + 1) % num_peers;
946     if (i > num_peers)
947       GNUNET_assert (0); /* we ran out of peers, must be a bug */
948   }
949   peers[search_peer].search_str = search_strings[peer->id];
950   peers[search_peer].search_str_matched = GNUNET_NO;
951   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_saturating_multiply(
952                                   reannounce_period_max,
953                                   2),
954                                 &find_string,
955                                 (void *) search_peer);
956 }
957
958
959 /**
960  * Task to start the daemons on each peer so that the regexes are announced
961  * into the DHT.
962  *
963  * @param cls NULL
964  * @param tc the task context
965  */
966 static void
967 do_announce (void *cls)
968 {
969   unsigned int i;
970
971   if (GNUNET_YES == in_shutdown)
972     return;
973   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
974               "Starting announce.\n");
975   for (i = 0; i < init_parallel_searches; i++)
976   {
977     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
978                 "  scheduling announce %u\n",
979                 i);
980     (void) GNUNET_SCHEDULER_add_now (&announce_next_regex, NULL);
981   }
982 }
983
984
985 /**
986  * Start announcing the next regex in the DHT.
987  *
988  * @param cls Closure (unused).
989  */
990 static void
991 announce_next_regex (void *cls)
992 {
993   struct RegexPeer *peer;
994
995   if (GNUNET_YES == in_shutdown)
996     return;
997   if (next_search >= num_peers)
998   {
999     if (strings_found != num_peers)
1000     {
1001       struct GNUNET_TIME_Relative new_delay;
1002       if (NULL != search_timeout_task)
1003         GNUNET_SCHEDULER_cancel (search_timeout_task);
1004       new_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15);
1005       search_timeout_task = GNUNET_SCHEDULER_add_delayed (new_delay,
1006                                                           &search_timed_out,
1007                                                           NULL);
1008     }
1009     return;
1010   }
1011
1012   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting daemon %u\n", next_search);
1013   peer = &peers[next_search];
1014   peer->daemon_op =
1015   GNUNET_TESTBED_peer_manage_service (NULL,
1016                                       peer->peer_handle,
1017                                       "regexprofiler",
1018                                       &daemon_started,
1019                                       peer,
1020                                       1);
1021   next_search++;
1022   parallel_searches++;
1023 }
1024
1025
1026 /**
1027  * DHT connect callback. Called when we are connected to the dht service for
1028  * the peer in 'cls'. If successfull we connect to the stats service of this
1029  * peer and then try to match the search string of this peer.
1030  *
1031  * @param cls internal peer id.
1032  * @param op operation handle.
1033  * @param ca_result connect adapter result.
1034  * @param emsg error message.
1035  */
1036 static void
1037 dht_connect_cb (void *cls,
1038                 struct GNUNET_TESTBED_Operation *op,
1039                 void *ca_result,
1040                 const char *emsg)
1041 {
1042   struct RegexPeer *peer = (struct RegexPeer *) cls;
1043
1044   if (NULL != emsg || NULL == op || NULL == ca_result)
1045   {
1046     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "DHT connect failed: %s\n", emsg);
1047     GNUNET_assert (0);
1048   }
1049
1050   GNUNET_assert (NULL != peer->dht_handle);
1051   GNUNET_assert (peer->op_handle == op);
1052   GNUNET_assert (peer->dht_handle == ca_result);
1053
1054   peer->search_str_matched = GNUNET_NO;
1055   peer->search_handle = REGEX_INTERNAL_search (peer->dht_handle,
1056                                              peer->search_str,
1057                                              &regex_found_handler, peer,
1058                                              NULL);
1059   peer->prof_start_time = GNUNET_TIME_absolute_get ();
1060 }
1061
1062
1063 /**
1064  * DHT connect adapter. Opens a connection to the dht service.
1065  *
1066  * @param cls Closure (peer).
1067  * @param cfg Configuration handle.
1068  *
1069  * @return
1070  */
1071 static void *
1072 dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1073 {
1074   struct RegexPeer *peer = cls;
1075
1076   peer->dht_handle = GNUNET_DHT_connect (cfg, 32);
1077
1078   return peer->dht_handle;
1079 }
1080
1081
1082 /**
1083  * Adapter function called to destroy a connection to the dht service.
1084  *
1085  * @param cls Closure (peer).
1086  * @param op_result Service handle returned from the connect adapter.
1087  */
1088 static void
1089 dht_da (void *cls, void *op_result)
1090 {
1091   struct RegexPeer *peer = (struct RegexPeer *) cls;
1092
1093   GNUNET_assert (peer->dht_handle == op_result);
1094
1095   if (NULL != peer->search_handle)
1096   {
1097     REGEX_INTERNAL_search_cancel (peer->search_handle);
1098     peer->search_handle = NULL;
1099   }
1100
1101   if (NULL != peer->dht_handle)
1102   {
1103     GNUNET_DHT_disconnect (peer->dht_handle);
1104     peer->dht_handle = NULL;
1105   }
1106 }
1107
1108
1109 /**
1110  * Signature of a main function for a testcase.
1111  *
1112  * @param cls NULL
1113  * @param h the run handle
1114  * @param num_peers_ number of peers in 'peers'
1115  * @param testbed_peers handle to peers run in the testbed.  NULL upon timeout (see
1116  *          GNUNET_TESTBED_test_run()).
1117  * @param links_succeeded the number of overlay link connection attempts that
1118  *          succeeded
1119  * @param links_failed the number of overlay link connection attempts that
1120  *          failed
1121  */
1122 static void
1123 test_master (void *cls,
1124              struct GNUNET_TESTBED_RunHandle *h,
1125              unsigned int num_peers_,
1126              struct GNUNET_TESTBED_Peer **testbed_peers,
1127              unsigned int links_succeeded,
1128              unsigned int links_failed)
1129 {
1130   unsigned int i;
1131
1132   GNUNET_assert (num_peers_ == num_peers);
1133
1134   prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1135   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1136               "Testbed started in %s\n",
1137               GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1138
1139   if (NULL != abort_task)
1140   {
1141     GNUNET_SCHEDULER_cancel (abort_task);
1142     abort_task = NULL;
1143   }
1144
1145   for (i = 0; i < num_peers; i++)
1146   {
1147     peers[i].peer_handle = testbed_peers[i];
1148   }
1149   if (GNUNET_NO ==
1150       GNUNET_CONFIGURATION_get_value_yesno (cfg, "DHT", "DISABLE_TRY_CONNECT"))
1151   {
1152     struct GNUNET_TIME_Relative settle_time;
1153
1154     settle_time =
1155       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
1156                                      10 * num_peers);
1157     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1158                 "Waiting for DHT for %s to settle new connections.\n\n",
1159                 GNUNET_STRINGS_relative_time_to_string(settle_time, GNUNET_NO));
1160     GNUNET_SCHEDULER_add_delayed (settle_time, &do_announce, NULL);
1161   }
1162   else
1163   {
1164     GNUNET_SCHEDULER_add_now (&do_announce, NULL);
1165   }
1166   search_timeout_task =
1167       GNUNET_SCHEDULER_add_delayed (search_timeout_time, &search_timed_out, NULL);
1168 }
1169
1170 /**
1171  * Function that will be called whenever something in the testbed changes.
1172  *
1173  * @param cls closure, NULL
1174  * @param event information on what is happening
1175  */
1176 static void
1177 master_controller_cb (void *cls,
1178                       const struct GNUNET_TESTBED_EventInformation *event)
1179 {
1180   switch (event->type)
1181   {
1182   case GNUNET_TESTBED_ET_CONNECT:
1183     printf(".");
1184     break;
1185   case GNUNET_TESTBED_ET_PEER_START:
1186     printf("#");
1187     break;
1188   default:
1189     break;
1190   }
1191   fflush(stdout);
1192 }
1193
1194
1195 /******************************************************************************/
1196 /***************************  TESTBED PEER SETUP  *****************************/
1197 /******************************************************************************/
1198
1199 /**
1200  * Process the text buffer counting the non-empty lines and separating them
1201  * with NULL characters, for later ease of copy using (as)printf.
1202  *
1203  * @param data Memory buffer with strings.
1204  * @param data_size Size of the @a data buffer in bytes.
1205  * @param str_max Maximum number of strings to return.
1206  * @return Positive number of lines found in the buffer,
1207  *         #GNUNET_SYSERR otherwise.
1208  */
1209 static int
1210 count_and_separate_strings (char *data,
1211                             uint64_t data_size,
1212                             unsigned int str_max)
1213 {
1214   char *buf;            // Keep track of last string to skip blank lines
1215   unsigned int offset;
1216   unsigned int str_cnt;
1217
1218   buf = data;
1219   offset = 0;
1220   str_cnt = 0;
1221   while ( (offset < (data_size - 1)) && (str_cnt < str_max) )
1222   {
1223     offset++;
1224     if ( ((data[offset] == '\n')) &&
1225          (buf != &data[offset]) )
1226     {
1227       data[offset] = '\0';
1228       str_cnt++;
1229       buf = &data[offset + 1];
1230     }
1231     else if ( (data[offset] == '\n') ||
1232               (data[offset] == '\0') )
1233       buf = &data[offset + 1];
1234   }
1235   return str_cnt;
1236 }
1237
1238
1239 /**
1240  * Allocate a string array and fill it with the prefixed strings
1241  * from a pre-processed, NULL-separated memory region.
1242  *
1243  * @param data Preprocessed memory with strings
1244  * @param data_size Size of the @a data buffer in bytes.
1245  * @param strings Address of the string array to be created.
1246  *                Must be freed by caller if function end in success.
1247  * @param str_cnt String count. The @a data buffer should contain
1248  *                at least this many NULL-separated strings.
1249  * @return #GNUNET_OK in ase of success, #GNUNET_SYSERR otherwise.
1250  *         In case of error @a strings must not be freed.
1251  */
1252 static int
1253 create_string_array (char *data, uint64_t data_size,
1254                      char ***strings, unsigned int str_cnt)
1255 {
1256   uint64_t offset;
1257   uint64_t len;
1258   unsigned int i;
1259
1260   *strings = GNUNET_malloc (sizeof (char *) * str_cnt);
1261   offset = 0;
1262   for (i = 0; i < str_cnt; i++)
1263   {
1264     len = strlen (&data[offset]);
1265     if (offset + len >= data_size)
1266     {
1267       GNUNET_free (*strings);
1268       *strings = NULL;
1269       return GNUNET_SYSERR;
1270     }
1271     if (0 == len) // empty line
1272     {
1273       offset++;
1274       i--;
1275       continue;
1276     }
1277
1278     GNUNET_asprintf (&(*strings)[i],
1279                      "%s%s",
1280                      regex_prefix,
1281                      &data[offset]);
1282     offset += len + 1;
1283   }
1284   return GNUNET_OK;
1285 }
1286
1287
1288 /**
1289  * Load search strings from given filename. One search string per line.
1290  *
1291  * @param filename filename of the file containing the search strings.
1292  * @param strings set of strings loaded from file. Caller needs to free this
1293  *                if number returned is greater than zero.
1294  * @param limit upper limit on the number of strings read from the file
1295  * @return number of strings found in the file. #GNUNET_SYSERR on error.
1296  */
1297 static int
1298 load_search_strings (const char *filename,
1299                      char ***strings,
1300                      unsigned int limit)
1301 {
1302   char *data;
1303   uint64_t filesize;
1304   int str_cnt;
1305
1306   /* Sanity checks */
1307   if (NULL == filename)
1308   {
1309     return GNUNET_SYSERR;
1310   }
1311   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
1312   {
1313     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1314                 "Could not find search strings file %s\n", filename);
1315     return GNUNET_SYSERR;
1316   }
1317   if (GNUNET_OK !=
1318       GNUNET_DISK_file_size (filename,
1319                              &filesize,
1320                              GNUNET_YES,
1321                              GNUNET_YES))
1322   {
1323     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1324                 "Search strings file %s cannot be read.\n",
1325                 filename);
1326     return GNUNET_SYSERR;
1327   }
1328   if (0 == filesize)
1329   {
1330     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1331                 "Search strings file %s is empty.\n",
1332                 filename);
1333     return GNUNET_SYSERR;
1334   }
1335
1336   /* Read data into memory */
1337   data = GNUNET_malloc (filesize + 1);
1338   if (filesize != GNUNET_DISK_fn_read (filename,
1339                                        data,
1340                                        filesize))
1341   {
1342     GNUNET_free (data);
1343     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1344                 "Could not read search strings file %s.\n",
1345          filename);
1346     return GNUNET_SYSERR;
1347   }
1348
1349   /* Process buffer and build array */
1350   str_cnt = count_and_separate_strings (data, filesize, limit);
1351   if (GNUNET_OK != create_string_array (data, filesize, strings, str_cnt))
1352   {
1353     str_cnt = GNUNET_SYSERR;
1354   }
1355   GNUNET_free (data);
1356   return str_cnt;
1357 }
1358
1359
1360 /**
1361  * Main function that will be run by the scheduler.
1362  *
1363  * @param cls closure
1364  * @param args remaining command-line arguments
1365  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1366  * @param config configuration
1367  */
1368 static void
1369 run (void *cls,
1370      char *const *args,
1371      const char *cfgfile,
1372      const struct GNUNET_CONFIGURATION_Handle *config)
1373 {
1374   unsigned int nsearchstrs;
1375   unsigned int i;
1376   struct GNUNET_TIME_Relative abort_time;
1377
1378   in_shutdown = GNUNET_NO;
1379
1380   /* Check config */
1381   if (NULL == config)
1382   {
1383     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1384                 _("No configuration file given. Exiting\n"));
1385     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1386     return;
1387   }
1388   cfg = GNUNET_CONFIGURATION_dup (config);
1389   if (GNUNET_OK !=
1390       GNUNET_CONFIGURATION_get_value_string (cfg, "REGEXPROFILER",
1391                                              "REGEX_PREFIX",
1392                                              &regex_prefix))
1393   {
1394     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1395                                "regexprofiler",
1396                                "regex_prefix");
1397     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1398     return;
1399   }
1400   if (GNUNET_OK !=
1401       GNUNET_CONFIGURATION_get_value_number (cfg, "REGEXPROFILER",
1402                                              "PARALLEL_SEARCHES",
1403                                              &init_parallel_searches))
1404   {
1405     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1406                 "Configuration option \"PARALLEL_SEARCHES\" missing."
1407                 " Using default (%d)\n", 10);
1408     init_parallel_searches = 10;
1409   }
1410   if (GNUNET_OK !=
1411       GNUNET_CONFIGURATION_get_value_time (cfg, "REGEXPROFILER",
1412                                            "REANNOUNCE_PERIOD_MAX",
1413                                            &reannounce_period_max))
1414   {
1415     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1416                 "reannounce_period_max not given. Using 10 minutes.\n");
1417     reannounce_period_max =
1418       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 10);
1419   }
1420
1421   /* Check arguments */
1422   if (NULL == policy_dir)
1423   {
1424     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1425                 _("No policy directory specified on command line. Exiting.\n"));
1426     return;
1427   }
1428   if (GNUNET_YES != GNUNET_DISK_directory_test (policy_dir, GNUNET_YES))
1429   {
1430     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1431                 _("Specified policies directory does not exist. Exiting.\n"));
1432     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1433     return;
1434   }
1435   if (0 >= (int) (num_peers = GNUNET_DISK_directory_scan (policy_dir, NULL, NULL)))
1436   {
1437     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1438                 _("No files found in `%s'\n"),
1439                 policy_dir);
1440     return;
1441   }
1442   GNUNET_CONFIGURATION_set_value_string (cfg, "REGEXPROFILER",
1443                                          "POLICY_DIR", policy_dir);
1444   if (GNUNET_YES != GNUNET_DISK_file_test (strings_file))
1445   {
1446     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1447                 _("No search strings file given. Exiting.\n"));
1448     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1449     return;
1450   }
1451   nsearchstrs = load_search_strings (strings_file,
1452                                      &search_strings,
1453                                      num_peers);
1454   if (num_peers != nsearchstrs)
1455   {
1456     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1457                 "Error loading search strings.\n");
1458     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1459                 "File (%s) does not contain enough strings (%u/%u).\n",
1460                 strings_file, nsearchstrs, num_peers);
1461     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1462     return;
1463   }
1464   if ( (0 == num_peers) || (NULL == search_strings))
1465   {
1466     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1467                 _("Error loading search strings. Exiting.\n"));
1468     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1469     return;
1470   }
1471   for (i = 0; i < num_peers; i++)
1472     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1473                 "search string: %s\n",
1474                 search_strings[i]);
1475
1476   /* Check logfile */
1477   if ( (NULL != data_filename) &&
1478        (NULL == (data_file =
1479                  GNUNET_DISK_file_open (data_filename,
1480                                         GNUNET_DISK_OPEN_READWRITE |
1481                                         GNUNET_DISK_OPEN_TRUNCATE |
1482                                         GNUNET_DISK_OPEN_CREATE,
1483                                         GNUNET_DISK_PERM_USER_READ |
1484                                         GNUNET_DISK_PERM_USER_WRITE))) )
1485   {
1486     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1487                               "open",
1488                               data_filename);
1489     return;
1490   }
1491
1492   /* Initialize peers */
1493   peers = GNUNET_malloc (sizeof (struct RegexPeer) * num_peers);
1494   for (i = 0; i < num_peers; i++)
1495     peers[i].id = i;
1496
1497   GNUNET_CONFIGURATION_set_value_number (cfg,
1498                                          "TESTBED", "OVERLAY_RANDOM_LINKS",
1499                                          num_peers * 20);
1500   GNUNET_CONFIGURATION_set_value_number (cfg,
1501                                          "DHT", "FORCE_NSE",
1502                                          (long long unsigned)
1503                                          (log (num_peers) / log (2.0)));
1504   event_mask = 0LL;
1505 /* For feedback about the start process activate these and pass master_cb */
1506   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
1507 //   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
1508   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1509 //   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
1510   prof_start_time = GNUNET_TIME_absolute_get ();
1511   GNUNET_TESTBED_run (hosts_file,
1512                       cfg,
1513                       num_peers,
1514                       event_mask,
1515                       &master_controller_cb,
1516                       NULL,     /* master_controller_cb cls */
1517                       &test_master,
1518                       NULL);    /* test_master cls */
1519   if (GNUNET_OK !=
1520       GNUNET_CONFIGURATION_get_value_time (cfg, "TESTBED",
1521                                            "SETUP_TIMEOUT",
1522                                            &abort_time))
1523   {
1524     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1525                 "SETUP_TIMEOUT not given. Using 15 minutes.\n");
1526     abort_time =
1527       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15);
1528   }
1529   abort_time = GNUNET_TIME_relative_add (abort_time, GNUNET_TIME_UNIT_MINUTES);
1530   abort_task =
1531       GNUNET_SCHEDULER_add_delayed (abort_time,
1532                                     &do_abort,
1533                                     (void*) __LINE__);
1534   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1535               "setup_timeout: %s\n",
1536               GNUNET_STRINGS_relative_time_to_string (abort_time, GNUNET_YES));
1537 }
1538
1539
1540 /**
1541  * Main function.
1542  *
1543  * @param argc argument count
1544  * @param argv argument values
1545  * @return 0 on success
1546  */
1547 int
1548 main (int argc, char *const *argv)
1549 {
1550   struct GNUNET_GETOPT_CommandLineOption options[] = {
1551
1552     GNUNET_GETOPT_option_filename ('o',
1553                                    "output-file",
1554                                    "FILENAME",
1555                                    gettext_noop ("name of the file for writing statistics"),
1556                                    &data_filename),
1557
1558     GNUNET_GETOPT_option_relative_time ('t',
1559                                             "matching-timeout",
1560                                             "TIMEOUT",
1561                                             gettext_noop ("wait TIMEOUT before ending the experiment"),
1562                                             &search_timeout_time), 
1563
1564     GNUNET_GETOPT_option_filename ('p',
1565                                    "policy-dir",
1566                                    "DIRECTORY",
1567                                    gettext_noop ("directory with policy files"),
1568                                    &policy_dir),
1569
1570
1571     GNUNET_GETOPT_option_filename ('s',
1572                                    "strings-file",
1573                                    "FILENAME",
1574                                    gettext_noop ("name of file with input strings"),
1575                                    &strings_file),
1576
1577     GNUNET_GETOPT_option_filename ('H',
1578                                    "hosts-file",
1579                                    "FILENAME",
1580                                    gettext_noop ("name of file with hosts' names"),
1581                                    &hosts_file),
1582
1583     GNUNET_GETOPT_OPTION_END
1584   };
1585   int ret;
1586
1587   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1588     return 2;
1589   result = GNUNET_SYSERR;
1590   ret =
1591       GNUNET_PROGRAM_run (argc, argv,
1592                           "gnunet-regex-profiler",
1593                           _("Profiler for regex"),
1594                           options, &run, NULL);
1595   if (GNUNET_OK != ret)
1596     return ret;
1597   if (GNUNET_OK != result)
1598     return 1;
1599   return 0;
1600 }