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