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