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