- not a bug
[oweals/gnunet.git] / src / mesh / gnunet-regex-profiler.c
1 /**
2      This file is part of GNUnet.
3      (C) 2011, 2012 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 mesh/gnunet-regex-profiler.c
23  * @brief Regex profiler for testing distributed regex use.
24  * @author Bart Polot
25  * @author Max 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_mesh_service.h"
35 #include "gnunet_stream_lib.h"
36 #include "gnunet_testbed_service.h"
37
38 /**
39  * DLL of operations
40  */
41 struct DLLOperation
42 {
43   /**
44    * The testbed operation handle
45    */
46   struct GNUNET_TESTBED_Operation *op;
47
48   /**
49    * Closure
50    */
51   void *cls;
52
53   /**
54    * The next pointer for DLL
55    */
56   struct DLLOperation *next;
57
58   /**
59    * The prev pointer for DLL
60    */
61   struct DLLOperation *prev;
62 };
63
64
65 /**
66  * Available states during profiling
67  */
68 enum State
69 {
70   /**
71    * Initial state
72    */
73   STATE_INIT = 0,
74
75   /**
76    * Starting slaves
77    */
78   STATE_SLAVES_STARTING,
79
80   /**
81    * Creating peers
82    */
83   STATE_PEERS_CREATING,
84
85   /**
86    * Starting peers
87    */
88   STATE_PEERS_STARTING,
89
90   /**
91    * Linking peers
92    */
93   STATE_PEERS_LINKING,
94
95   /**
96    * Matching strings against announced regexes
97    */
98   STATE_SEARCH_REGEX,
99
100   /**
101    * Destroying peers; we can do this as the controller takes care of stopping a
102    * peer if it is running
103    */
104   STATE_PEERS_DESTROYING
105 };
106
107
108 /**
109  * Peer handles.
110  */
111 struct RegexPeer
112 {
113   /**
114    * Peer id.
115    */
116   unsigned int id;
117
118   /**
119    * Peer configuration handle.
120    */
121   struct GNUNET_CONFIGURATION_Handle *cfg;
122
123   /**
124    * The actual testbed peer handle.
125    */
126   struct GNUNET_TESTBED_Peer *peer_handle;
127
128   /**
129    * Host on which the peer is running.
130    */
131   struct GNUNET_TESTBED_Host *host_handle;
132
133   /**
134    * Filename of the peer's policy file.
135    */
136   char *policy_file;
137
138   /**
139    * Peers search string.
140    */
141   const char *search_str;
142
143   /**
144    * Set to GNUNET_YES if the peer successfully matched the above
145    * search string. GNUNET_NO if the string could not be matched
146    * during the profiler run. GNUNET_SYSERR if the string matching
147    * timed out. Undefined if search_str is NULL
148    */
149   int search_str_matched;
150
151   /**
152    * Peer's mesh handle.
153    */
154   struct GNUNET_MESH_Handle *mesh_handle;
155
156   /**
157    * Peer's mesh tunnel handle.
158    */
159   struct GNUNET_MESH_Tunnel *mesh_tunnel_handle;
160
161   /**
162    * Testbed operation handle for the mesh service.
163    */
164   struct GNUNET_TESTBED_Operation *mesh_op_handle;
165
166   /**
167    * Peers's statistics handle.
168    */
169   struct GNUNET_STATISTICS_Handle *stats_handle;
170
171   /**
172    * Testbed operation handle for the statistics service.
173    */
174   struct GNUNET_TESTBED_Operation *stats_op_handle;
175
176   /**
177    * The starting time of a profiling step.
178    */
179   struct GNUNET_TIME_Absolute prof_start_time;
180 };
181
182
183 /**
184  * An array of hosts loaded from the hostkeys file
185  */
186 static struct GNUNET_TESTBED_Host **hosts;
187
188 /**
189  * Array of peer handles used to pass to
190  * GNUNET_TESTBED_overlay_configure_topology
191  */
192 static struct GNUNET_TESTBED_Peer **peer_handles;
193
194 /**
195  * The array of peers; we fill this as the peers are given to us by the testbed
196  */
197 static struct RegexPeer *peers;
198
199 /**
200  * Host registration handle
201  */
202 static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
203
204 /**
205  * Handle to the master controller process
206  */
207 static struct GNUNET_TESTBED_ControllerProc *mc_proc;
208
209 /**
210  * Handle to the master controller
211  */
212 static struct GNUNET_TESTBED_Controller *mc;
213
214 /**
215  * Handle to global configuration
216  */
217 static struct GNUNET_CONFIGURATION_Handle *cfg;
218
219 /**
220  * Head of the operations list
221  */
222 static struct DLLOperation *dll_op_head;
223
224 /**
225  * Tail of the operations list
226  */
227 static struct DLLOperation *dll_op_tail;
228
229 /**
230  * Peer linking - topology operation
231  */
232 static struct GNUNET_TESTBED_Operation *topology_op;
233
234 /**
235  * The handle for whether a host is habitable or not
236  */
237 struct GNUNET_TESTBED_HostHabitableCheckHandle **hc_handles;
238
239 /**
240  * Abort task identifier
241  */
242 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
243
244 /**
245  * Shutdown task identifier
246  */
247 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
248
249 /**
250  * Host registration task identifier
251  */
252 static GNUNET_SCHEDULER_TaskIdentifier register_hosts_task;
253
254 /**
255  * Global event mask for all testbed events
256  */
257 static uint64_t event_mask;
258
259 /**
260  * The starting time of a profiling step
261  */
262 static struct GNUNET_TIME_Absolute prof_start_time;
263
264 /**
265  * Duration profiling step has taken
266  */
267 static struct GNUNET_TIME_Relative prof_time;
268
269 /**
270  * Number of peers to be started by the profiler
271  */
272 static unsigned int num_peers;
273
274 /**
275  * Number of hosts in the hosts array
276  */
277 static unsigned int num_hosts;
278
279 /**
280  * Factor of number of links. num_links = num_peers * linking_factor.
281  */
282 static unsigned int linking_factor;
283
284 /**
285  * Number of random links to be established between peers
286  */
287 static unsigned int num_links;
288
289 /**
290  * Number of times we try overlay connect operations
291  */
292 static unsigned int retry_links;
293
294 /**
295  * Continuous failures during overlay connect operations
296  */
297 static unsigned int cont_fails;
298
299 /**
300  * Global testing status
301  */
302 static int result;
303
304 /**
305  * current state of profiling
306  */
307 enum State state;
308
309 /**
310  * Folder where policy files are stored.
311  */
312 static char * policy_dir;
313
314 /**
315  * Search strings.
316  */
317 static char **search_strings;
318
319 /**
320  * Number of search strings.
321  */
322 static int num_search_strings;
323
324 /**
325  * Number of peers found with search strings.
326  */
327 static unsigned int peers_found;
328
329 /**
330  * Search task identifier
331  */
332 static GNUNET_SCHEDULER_TaskIdentifier search_task;
333
334 /**
335  * Search timeout task identifier.
336  */
337 static GNUNET_SCHEDULER_TaskIdentifier search_timeout_task;
338
339 /**
340  * Search timeout in seconds.
341  */
342 static struct GNUNET_TIME_Relative search_timeout = { 60000 };
343
344 /**
345  * How long do we wait before starting the search?
346  * Default: 1 m.
347  */
348 static struct GNUNET_TIME_Relative search_delay = { 60000 };
349
350 /**
351  * File to log statistics to.
352  */
353 static struct GNUNET_DISK_FileHandle *data_file;
354
355 /**
356  * Filename to log statistics to.
357  */
358 static char *data_filename;
359
360 /**
361  * Maximal path compression length.
362  */
363 static unsigned int max_path_compression;
364
365 /**
366  * If we should distribute the search evenly throught all peers (each
367  * peer searches for a string) or if only one peer should search for
368  * all strings.
369  */
370 static int no_distributed_search;
371
372 /**
373  * Prefix used for regex announcing. We need to prefix the search
374  * strings with it, in order to find something.
375  */
376 static char * regex_prefix;
377
378
379 /******************************************************************************/
380 /******************************  DECLARATIONS  ********************************/
381 /******************************************************************************/
382
383
384 /**
385  * Method called whenever a peer has connected to the tunnel.
386  *
387  * @param cls closure
388  * @param peer_id peer identity the tunnel was created to, NULL on timeout
389  * @param atsi performance data for the connection
390  *
391  */
392 static void
393 mesh_peer_connect_handler (void *cls,
394                            const struct GNUNET_PeerIdentity* peer_id,
395                            const struct GNUNET_ATS_Information * atsi);
396
397
398 /**
399  * Method called whenever a peer has disconnected from the tunnel.
400  * Implementations of this callback must NOT call
401  * GNUNET_MESH_tunnel_destroy immediately, but instead schedule those
402  * to run in some other task later.  However, calling
403  * "GNUNET_MESH_notify_transmit_ready_cancel" is allowed.
404  *
405  * @param cls closure
406  * @param peer_id peer identity the tunnel stopped working with
407  */
408 static void
409 mesh_peer_disconnect_handler (void *cls,
410                               const struct GNUNET_PeerIdentity * peer_id);
411
412 /**
413  * Mesh connect callback.
414  *
415  * @param cls internal peer id.
416  * @param op operation handle.
417  * @param ca_result connect adapter result.
418  * @param emsg error message.
419  */
420 static void
421 mesh_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
422                  void *ca_result, const char *emsg);
423
424 /**
425  * Mesh connect adapter.
426  *
427  * @param cls not used.
428  * @param cfg configuration handle.
429  *
430  * @return
431  */
432 static void *
433 mesh_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg);
434
435
436 /**
437  * Adapter function called to destroy a connection to
438  * the mesh service
439  *
440  * @param cls closure
441  * @param op_result service handle returned from the connect adapter
442  */
443 static void
444 mesh_da (void *cls, void *op_result);
445
446
447 /**
448  * Function called by testbed once we are connected to stats
449  * service. Get the statistics for the services of interest.
450  *
451  * @param cls the 'struct RegexPeer' for which we connected to stats
452  * @param op connect operation handle
453  * @param ca_result handle to stats service
454  * @param emsg error message on failure
455  */
456 static void
457 stats_connect_cb (void *cls,
458                   struct GNUNET_TESTBED_Operation *op,
459                   void *ca_result,
460                   const char *emsg);
461
462
463 /**
464  * Task to collect all statistics from all peers, will shutdown the
465  * profiler, when done.
466  *
467  * @param cls NULL
468  * @param tc the task context
469  */
470 static void
471 do_collect_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
472
473
474 /******************************************************************************/
475 /********************************  SHUTDOWN  **********************************/
476 /******************************************************************************/
477
478
479 /**
480  * Shutdown nicely
481  *
482  * @param cls NULL
483  * @param tc the task context
484  */
485 static void
486 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
487 {
488   struct DLLOperation *dll_op;
489   struct RegexPeer *peer;
490   unsigned int nhost;
491   unsigned int peer_cnt;
492   unsigned int search_str_cnt;
493   char output_buffer[512];
494   size_t size;
495
496   shutdown_task = GNUNET_SCHEDULER_NO_TASK;
497   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
498     GNUNET_SCHEDULER_cancel (abort_task);
499   if (NULL != hc_handles)
500   {
501     for (nhost = 0; nhost < num_hosts; nhost++)
502       if (NULL != hc_handles[nhost])
503         GNUNET_TESTBED_is_host_habitable_cancel (hc_handles[nhost]);
504     GNUNET_free (hc_handles);
505     hc_handles = NULL;
506   }
507   if (GNUNET_SCHEDULER_NO_TASK != register_hosts_task)
508     GNUNET_SCHEDULER_cancel (register_hosts_task);
509
510   for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
511   {
512     peer = &peers[peer_cnt];
513
514     if (GNUNET_YES != peer->search_str_matched && NULL != data_file)
515     {
516       prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time);
517       size =
518         GNUNET_snprintf (output_buffer,
519                          sizeof (output_buffer),
520                          "%p Search string not found: %s (%d)\n%p On peer: %u (%p)\n%p With policy file: %s\n%p After: %s\n",
521                          peer,
522                          peer->search_str,
523                          peer->search_str_matched,
524                          peer,
525                          peer->id,
526                          peer,
527                          peer,
528                          peer->policy_file,
529                          peer,
530                          GNUNET_STRINGS_relative_time_to_string (prof_time, 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].mesh_op_handle)
536       GNUNET_TESTBED_operation_done (peers[peer_cnt].mesh_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 /************************  MESH SERVICE CONNECTIONS  **************************/
795 /******************************************************************************/
796
797 /**
798  * Method called whenever a peer has disconnected from the tunnel.
799  * Implementations of this callback must NOT call
800  * GNUNET_MESH_tunnel_destroy immediately, but instead schedule those
801  * to run in some other task later.  However, calling
802  * "GNUNET_MESH_notify_transmit_ready_cancel" is allowed.
803  *
804  * @param cls closure
805  * @param peer_id peer identity the tunnel stopped working with
806  */
807 static void
808 mesh_peer_disconnect_handler (void *cls,
809                               const struct GNUNET_PeerIdentity * peer_id)
810 {
811   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh peer disconnect handler.\n");
812 }
813
814
815 /**
816  * Method called when the mesh connection succeeded (or timed out), which means
817  * we've found a peer that announced a regex that matches our search string. Now
818  * get the statistics.
819  *
820  * @param cls closure
821  * @param peer_id peer identity the tunnel was created to, NULL on timeout
822  * @param atsi performance data for the connection
823  *
824  */
825 static void
826 mesh_peer_connect_handler (void *cls,
827                            const struct GNUNET_PeerIdentity* peer_id,
828                            const struct GNUNET_ATS_Information * atsi)
829 {
830   struct RegexPeer *peer = cls;
831   char output_buffer[512];
832   size_t size;
833
834   if (GNUNET_YES == peer->search_str_matched)
835   {
836     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
837                 "String %s on peer %u already matched!\n",
838                 peer->search_str, peer->id);
839     return;
840   }
841
842   peers_found++;
843
844   if (NULL == peer_id)
845   {
846     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
847                 "String matching timed out for string %s on peer %u (%i/%i)\n",
848                 peer->search_str, peer->id, peers_found, num_search_strings);
849
850     printf ("String matching timed out for string %s on peer %u (%i/%i)\n",
851             peer->search_str, peer->id, peers_found, num_search_strings);
852
853     peer->search_str_matched = GNUNET_SYSERR;
854   }
855   else
856   {
857     prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time);
858     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
859                 "String %s successfully matched on peer %u after %s (%i/%i)\n",
860                 peer->search_str, peer->id, GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
861                 peers_found, num_search_strings);
862
863     printf ("String %s successfully matched on peer %u after %s (%i/%i)\n",
864             peer->search_str, peer->id, GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
865             peers_found, num_search_strings);
866     fflush (stdout);
867
868     peer->search_str_matched = GNUNET_YES;
869
870     if (NULL != data_file)
871     {
872       size =
873         GNUNET_snprintf (output_buffer,
874                          sizeof (output_buffer),
875                          "%p Peer: %u\n%p Host: %s\n%p Policy file: %s\n%p Search string: %s\n%p Search duration: %s\n\n",
876                          peer,
877                          peer->id,
878                          peer,
879                          GNUNET_TESTBED_host_get_hostname (peer->host_handle),
880                          peer,
881                          peer->policy_file,
882                          peer,
883                          peer->search_str,
884                          peer,
885                          GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
886
887       if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
888         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
889     }
890   }
891
892   GNUNET_TESTBED_operation_done (peer->mesh_op_handle);
893   peer->mesh_op_handle = NULL;
894
895   if (peers_found == num_search_strings)
896   {
897     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
898     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
899                 "All strings successfully matched in %s\n",
900                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
901     printf ("All strings successfully matched.\n");
902     fflush (stdout);
903
904     if (GNUNET_SCHEDULER_NO_TASK != search_timeout_task)
905       GNUNET_SCHEDULER_cancel (search_timeout_task);
906
907     printf ("Collecting stats and shutting down.\n");
908     GNUNET_SCHEDULER_add_now (&do_collect_stats, NULL);
909   }
910 }
911
912
913 /**
914  * Connect by string timeout task. This will cancel the profiler after the
915  * specified timeout 'search_timeout'.
916  *
917  * @param cls NULL
918  * @param tc the task context
919  */
920 static void
921 do_connect_by_string_timeout (void *cls,
922                               const struct GNUNET_SCHEDULER_TaskContext * tc)
923 {
924   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
925               "Finding matches to all strings did not succeed after %s.\n",
926               GNUNET_STRINGS_relative_time_to_string (search_timeout, GNUNET_NO));
927   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
928               "Found %i of %i strings\n", peers_found, num_search_strings);
929
930   printf ("Search timed out after %s. Collecting stats and shutting down.\n", 
931           GNUNET_STRINGS_relative_time_to_string (search_timeout, GNUNET_NO));
932   fflush (stdout);
933
934   GNUNET_SCHEDULER_add_now (&do_collect_stats, NULL);
935 }
936
937
938 /**
939  * Connect by string task that is run to search for a string in the
940  * NFA. It first connects to the mesh service and when a connection is
941  * established it starts to search for the string.
942  *
943  * @param cls NULL
944  * @param tc the task context
945  */
946 static void
947 do_connect_by_string (void *cls,
948                       const struct GNUNET_SCHEDULER_TaskContext * tc)
949 {
950   printf ("Starting string search.\n");
951   fflush (stdout);
952
953   peers[0].search_str = search_strings[0];
954   peers[0].search_str_matched = GNUNET_NO;
955
956   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
957               "Searching for string \"%s\" on peer %d with file %s\n",
958               peers[0].search_str, 0, peers[0].policy_file);
959
960     /* First connect to mesh service, then search for string. Next
961        connect will be in mesh_connect_cb */
962     peers[0].mesh_op_handle =
963       GNUNET_TESTBED_service_connect (NULL,
964                                       peers[0].peer_handle,
965                                       "mesh",
966                                       &mesh_connect_cb,
967                                       &peers[0],
968                                       &mesh_ca,
969                                       &mesh_da,
970                                       &peers[0]);
971
972   search_timeout_task = GNUNET_SCHEDULER_add_delayed (search_timeout,
973                                                       &do_connect_by_string_timeout, NULL);
974 }
975
976
977 /**
978  * Mesh connect callback. Called when we are connected to the mesh service for
979  * the peer in 'cls'. If successfull we connect to the stats service of this
980  * peer and then try to match the search string of this peer.
981  *
982  * @param cls internal peer id.
983  * @param op operation handle.
984  * @param ca_result connect adapter result.
985  * @param emsg error message.
986  */
987 static void
988 mesh_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
989                  void *ca_result, const char *emsg)
990 {
991   struct RegexPeer *peer = (struct RegexPeer *) cls;
992   static unsigned int peer_cnt;
993   unsigned int next_p;
994
995   if (NULL != emsg || NULL == op || NULL == ca_result)
996   {
997     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Mesh connect failed: %s\n", emsg);
998     GNUNET_assert (0);
999   }
1000
1001   GNUNET_assert (NULL != peer->mesh_handle);
1002   GNUNET_assert (peer->mesh_op_handle == op);
1003   GNUNET_assert (peer->mesh_handle == ca_result);
1004
1005   peer->mesh_tunnel_handle = 
1006     GNUNET_MESH_tunnel_create (peer->mesh_handle,
1007                                NULL,
1008                                &mesh_peer_connect_handler,
1009                                &mesh_peer_disconnect_handler,
1010                                peer);
1011
1012   peer->prof_start_time = GNUNET_TIME_absolute_get ();
1013
1014   peer->search_str_matched = GNUNET_NO;
1015   GNUNET_MESH_peer_request_connect_by_string (peer->mesh_tunnel_handle,
1016                                               peer->search_str);
1017
1018   if (peer_cnt < (num_search_strings - 1))
1019   {
1020     if (GNUNET_YES == no_distributed_search)
1021       next_p = 0;
1022     else
1023       next_p = (++peer_cnt % num_peers);
1024
1025     peers[next_p].search_str = search_strings[next_p];
1026     peers[next_p].search_str_matched = GNUNET_NO;
1027     
1028     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1029                 "Searching for string \"%s\" on peer %d with file %s\n",
1030                 peers[next_p].search_str, next_p, peers[next_p].policy_file);
1031
1032     peers[next_p].mesh_op_handle =
1033       GNUNET_TESTBED_service_connect (NULL,
1034                                       peers[next_p].peer_handle,
1035                                       "mesh",
1036                                       &mesh_connect_cb,
1037                                       &peers[next_p],
1038                                       &mesh_ca,
1039                                       &mesh_da,
1040                                       &peers[next_p]);
1041   }
1042 }
1043
1044
1045 /**
1046  * Mesh connect adapter. Opens a connection to the mesh service.
1047  *
1048  * @param cls not used.
1049  * @param cfg configuration handle.
1050  *
1051  * @return
1052  */
1053 static void *
1054 mesh_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1055 {
1056   GNUNET_MESH_ApplicationType app;
1057   struct RegexPeer *peer = cls;
1058
1059   static struct GNUNET_MESH_MessageHandler handlers[] = {
1060     {NULL, 0, 0}
1061   };
1062
1063   app = (GNUNET_MESH_ApplicationType)0;
1064
1065   peer->mesh_handle =
1066     GNUNET_MESH_connect (cfg, peer, NULL, NULL, handlers, &app);
1067
1068   return peer->mesh_handle;
1069 }
1070
1071
1072 /**
1073  * Adapter function called to destroy a connection to
1074  * the mesh service
1075  *
1076  * @param cls closure
1077  * @param op_result service handle returned from the connect adapter
1078  */
1079 static void
1080 mesh_da (void *cls, void *op_result)
1081 {
1082   struct RegexPeer *peer = (struct RegexPeer *) cls;
1083
1084   GNUNET_assert (peer->mesh_handle == op_result);
1085
1086   if (NULL != peer->mesh_tunnel_handle)
1087   {
1088     GNUNET_MESH_tunnel_destroy (peer->mesh_tunnel_handle);
1089     peer->mesh_tunnel_handle = NULL;
1090   }
1091
1092   if (NULL != peer->mesh_handle)
1093   {
1094     GNUNET_MESH_disconnect (peer->mesh_handle);
1095     peer->mesh_handle = NULL;
1096   }
1097 }
1098
1099
1100 /******************************************************************************/
1101 /***************************  TESTBED PEER SETUP  *****************************/
1102 /******************************************************************************/
1103
1104
1105 /**
1106  * Configure the peer overlay topology.
1107  *
1108  * @param cls NULL
1109  * @param tc the task context
1110  */
1111 static void
1112 do_configure_topology (void *cls,
1113                        const struct GNUNET_SCHEDULER_TaskContext * tc)
1114 {
1115   /*
1116     if (0 == linking_factor)
1117     linking_factor = 1;
1118     num_links = linking_factor * num_peers;
1119   */
1120   /* num_links = num_peers - 1; */
1121   num_links = linking_factor;
1122
1123   /* Do overlay connect */
1124   prof_start_time = GNUNET_TIME_absolute_get ();
1125   topology_op =
1126     GNUNET_TESTBED_overlay_configure_topology (NULL, num_peers, peer_handles,
1127                                                NULL,
1128                                                GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI,
1129                                                num_links,
1130                                                GNUNET_TESTBED_TOPOLOGY_DISABLE_AUTO_RETRY,
1131                                                GNUNET_TESTBED_TOPOLOGY_OPTION_END);
1132   if (NULL == topology_op)
1133   {
1134     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1135                 "Cannot create topology, op handle was NULL\n");
1136     GNUNET_assert (0);
1137   }
1138 }
1139
1140
1141 /**
1142  * Functions of this signature are called when a peer has been successfully
1143  * started or stopped.
1144  *
1145  * @param cls the closure from GNUNET_TESTBED_peer_start/stop()
1146  * @param emsg NULL on success; otherwise an error description
1147  */
1148 static void
1149 peer_churn_cb (void *cls, const char *emsg)
1150 {
1151   struct DLLOperation *dll_op = cls;
1152   struct GNUNET_TESTBED_Operation *op;
1153   static unsigned int started_peers;
1154   unsigned int peer_cnt;
1155
1156   op = dll_op->op;
1157   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1158   GNUNET_free (dll_op);
1159   if (NULL != emsg)
1160   {
1161     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1162          _("An operation has failed while starting peers\n"));
1163     GNUNET_TESTBED_operation_done (op);
1164     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1165       GNUNET_SCHEDULER_cancel (abort_task);
1166     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1167     return;
1168   }
1169   GNUNET_TESTBED_operation_done (op);
1170   if (++started_peers == num_peers)
1171   {
1172     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1173     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1174                 "All peers started successfully in %s\n",
1175                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1176     result = GNUNET_OK;
1177
1178     peer_handles = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *) * num_peers);
1179     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1180       peer_handles[peer_cnt] = peers[peer_cnt].peer_handle;
1181
1182     state = STATE_PEERS_LINKING;
1183     GNUNET_SCHEDULER_add_now (&do_configure_topology, NULL);
1184   }
1185 }
1186
1187
1188 /**
1189  * Functions of this signature are called when a peer has been successfully
1190  * created
1191  *
1192  * @param cls the closure from GNUNET_TESTBED_peer_create()
1193  * @param peer the handle for the created peer; NULL on any error during
1194  *          creation
1195  * @param emsg NULL if peer is not NULL; else MAY contain the error description
1196  */
1197 static void
1198 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
1199 {
1200   struct DLLOperation *dll_op = cls;
1201   struct RegexPeer *peer_ptr;
1202   static unsigned int created_peers;
1203   unsigned int peer_cnt;
1204
1205   if (NULL != emsg)
1206   {
1207     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1208          _("Creating a peer failed. Error: %s\n"), emsg);
1209     GNUNET_TESTBED_operation_done (dll_op->op);
1210     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1211     GNUNET_free (dll_op);
1212     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1213       GNUNET_SCHEDULER_cancel (abort_task);
1214     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1215     return;
1216   }
1217
1218   peer_ptr = dll_op->cls;
1219   GNUNET_assert (NULL == peer_ptr->peer_handle);
1220   GNUNET_CONFIGURATION_destroy (peer_ptr->cfg);
1221   peer_ptr->cfg = NULL;
1222   peer_ptr->peer_handle = peer;
1223   GNUNET_TESTBED_operation_done (dll_op->op);
1224   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1225   GNUNET_free (dll_op);
1226
1227   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %i created on host %s\n",
1228               peer_ptr->id,
1229               GNUNET_TESTBED_host_get_hostname (peer_ptr->host_handle));
1230
1231   if (++created_peers == num_peers)
1232   {
1233     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1234     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1235                 "All peers created successfully in %s\n",
1236                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1237     /* Now peers are to be started */
1238     state = STATE_PEERS_STARTING;
1239     prof_start_time = GNUNET_TIME_absolute_get ();
1240     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1241     {
1242       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1243       dll_op->op = GNUNET_TESTBED_peer_start (dll_op, peers[peer_cnt].peer_handle,
1244                                               &peer_churn_cb, dll_op);
1245       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1246     }
1247   }
1248 }
1249
1250
1251 /**
1252  * Function called with a filename for each file in the policy directory. Create
1253  * a peer for each filename and update the peer's configuration to include the
1254  * max_path_compression specified as a command line argument as well as the
1255  * policy_file for this peer. The gnunet-service-regexprofiler service is
1256  * automatically started on this peer. The service reads the configurration and
1257  * announces the regexes stored in the policy file 'filename'.
1258  *
1259  * @param cls closure
1260  * @param filename complete filename (absolute path)
1261  * @return GNUNET_OK to continue to iterate,
1262  *  GNUNET_SYSERR to abort iteration with error!
1263  */
1264 static int
1265 policy_filename_cb (void *cls, const char *filename)
1266 {
1267   static unsigned int peer_cnt;
1268   struct DLLOperation *dll_op;
1269   struct RegexPeer *peer = &peers[peer_cnt];
1270
1271   GNUNET_assert (NULL != peer);
1272
1273   peer->policy_file = GNUNET_strdup (filename);
1274
1275   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Creating peer %i on host %s for policy file %s\n",
1276               peer->id,
1277               GNUNET_TESTBED_host_get_hostname (peer->host_handle),
1278               filename);
1279
1280   /* Set configuration options specific for this peer
1281      (max_path_compression and policy_file */
1282   peer->cfg = GNUNET_CONFIGURATION_dup (cfg);
1283   GNUNET_CONFIGURATION_set_value_number (peer->cfg, "REGEXPROFILER",
1284                                          "MAX_PATH_COMPRESSION",
1285                                          (unsigned long long)max_path_compression);
1286   GNUNET_CONFIGURATION_set_value_string (peer->cfg, "REGEXPROFILER",
1287                                          "POLICY_FILE", filename);
1288
1289   dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1290   dll_op->cls = &peers[peer_cnt];
1291   dll_op->op = GNUNET_TESTBED_peer_create (mc,
1292                                            peer->host_handle,
1293                                            peer->cfg,
1294                                            &peer_create_cb,
1295                                            dll_op);
1296   GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1297
1298   peer_cnt++;
1299
1300   return GNUNET_OK;
1301 }
1302
1303
1304 /**
1305  * Controller event callback.
1306  *
1307  * @param cls NULL
1308  * @param event the controller event
1309  */
1310 static void
1311 controller_event_cb (void *cls,
1312                      const struct GNUNET_TESTBED_EventInformation *event)
1313 {
1314   struct DLLOperation *dll_op;
1315   struct GNUNET_TESTBED_Operation *op;
1316   int ret;
1317
1318   switch (state)
1319   {
1320   case STATE_SLAVES_STARTING:
1321     switch (event->type)
1322     {
1323     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1324       {
1325         static unsigned int slaves_started;
1326         unsigned int peer_cnt;
1327
1328         dll_op = event->details.operation_finished.op_cls;
1329         GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1330         GNUNET_free (dll_op);
1331         op = event->details.operation_finished.operation;
1332         if (NULL != event->details.operation_finished.emsg)
1333         {
1334           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1335                _("An operation has failed while starting slaves\n"));
1336           GNUNET_TESTBED_operation_done (op);
1337           if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1338             GNUNET_SCHEDULER_cancel (abort_task);
1339           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1340           return;
1341         }
1342         GNUNET_TESTBED_operation_done (op);
1343         /* Proceed to start peers */
1344         if (++slaves_started == num_hosts - 1)
1345         {
1346           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1347                       "All slaves started successfully\n");
1348
1349           state = STATE_PEERS_CREATING;
1350           prof_start_time = GNUNET_TIME_absolute_get ();
1351
1352           if (-1 == (ret = GNUNET_DISK_directory_scan (policy_dir,
1353                                                        NULL,
1354                                                        NULL)))
1355           {
1356             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1357                         _("No files found in `%s'\n"),
1358                         policy_dir);
1359             GNUNET_SCHEDULER_shutdown ();
1360             return;
1361           }
1362           num_peers = (unsigned int) ret;
1363           peers = GNUNET_malloc (sizeof (struct RegexPeer) * num_peers);
1364
1365           /* Initialize peers */
1366           for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1367           {
1368             struct RegexPeer *peer = &peers[peer_cnt];
1369             peer->id = peer_cnt;
1370             peer->policy_file = NULL;
1371             /* Do not start peers on hosts[0] (master controller) */
1372             peer->host_handle = hosts[1 + (peer_cnt % (num_hosts -1))];
1373             peer->mesh_handle = NULL;
1374             peer->mesh_tunnel_handle = NULL;
1375             peer->stats_handle = NULL;
1376             peer->stats_op_handle = NULL;
1377             peer->search_str = NULL;
1378             peer->search_str_matched = GNUNET_NO;
1379           }
1380
1381           GNUNET_DISK_directory_scan (policy_dir,
1382                                       &policy_filename_cb,
1383                                       NULL);
1384         }
1385       }
1386       break;
1387     default:
1388       GNUNET_assert (0);
1389     }
1390     break;
1391   case STATE_PEERS_STARTING:
1392     switch (event->type)
1393     {
1394     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1395       /* Control reaches here when peer start fails */
1396     case GNUNET_TESTBED_ET_PEER_START:
1397       /* we handle peer starts in peer_churn_cb */
1398       break;
1399     default:
1400       GNUNET_assert (0);
1401     }
1402     break;
1403   case STATE_PEERS_LINKING:
1404    switch (event->type)
1405    {
1406      static unsigned int established_links;
1407    case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1408      /* Control reaches here when a peer linking operation fails */
1409      if (NULL != event->details.operation_finished.emsg)
1410      {
1411        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1412                    _("An operation has failed while linking\n"));
1413        printf ("F");
1414        fflush (stdout);
1415        retry_links++;
1416      }
1417      /* We do no retries, consider this link as established */
1418      /* break; */
1419    case GNUNET_TESTBED_ET_CONNECT:
1420    {
1421      char output_buffer[512];
1422      size_t size;
1423
1424      if (0 == established_links)
1425        printf ("Establishing links .");
1426      else
1427      {
1428        printf (".");
1429        fflush (stdout);
1430      }
1431      if (++established_links == num_links)
1432      {
1433        fflush (stdout);
1434        prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1435        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1436                    "%u links established in %s\n",
1437                    num_links,
1438                    GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1439        result = GNUNET_OK;
1440        GNUNET_free (peer_handles);
1441
1442        if (NULL != data_file)
1443        {
1444          size =
1445            GNUNET_snprintf (output_buffer,
1446                             sizeof (output_buffer),
1447                             "# of peers: %u\n# of links established: %u\n"
1448                             "Time to establish links: %s\nLinking failures: %u\n"
1449                             "path compression length: %u\n# of search strings: %u\n",
1450                             num_peers,
1451                             (established_links - cont_fails),
1452                             GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
1453                             cont_fails,
1454                             max_path_compression,
1455                             num_search_strings);
1456
1457          if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
1458            GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
1459        }
1460
1461        printf ("\nWaiting %s before starting to search.\n",
1462                GNUNET_STRINGS_relative_time_to_string (search_delay, GNUNET_YES));
1463        fflush (stdout);
1464
1465        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1466                    "Waiting %s before starting to search.\n",
1467                    GNUNET_STRINGS_relative_time_to_string (search_delay, GNUNET_NO));
1468
1469        state = STATE_SEARCH_REGEX;
1470
1471        search_task = GNUNET_SCHEDULER_add_delayed (search_delay,
1472                                                    &do_connect_by_string, NULL);
1473      }
1474    }
1475    break;
1476    default:
1477      GNUNET_assert (0);
1478    }
1479    break;
1480   case STATE_SEARCH_REGEX:
1481   {
1482     /* Handled in service connect callback */
1483     break;
1484   }
1485   default:
1486     switch (state)
1487     {
1488     case STATE_PEERS_CREATING:
1489       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create peer\n");
1490       break;
1491     default:
1492       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1493                   "Unexpected controller_cb with state %i!\n", state);
1494     }
1495     GNUNET_assert (0);
1496   }
1497 }
1498
1499
1500 /**
1501  * Task to register all hosts available in the global host list.
1502  *
1503  * @param cls NULL
1504  * @param tc the scheduler task context
1505  */
1506 static void
1507 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1508
1509
1510 /**
1511  * Callback which will be called to after a host registration succeeded or failed
1512  *
1513  * @param cls the closure
1514  * @param emsg the error message; NULL if host registration is successful
1515  */
1516 static void
1517 host_registration_completion (void *cls, const char *emsg)
1518 {
1519   reg_handle = NULL;
1520   if (NULL != emsg)
1521   {
1522     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1523                 _("Host registration failed for a host. Error: %s\n"), emsg);
1524     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1525       GNUNET_SCHEDULER_cancel (abort_task);
1526     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1527     return;
1528   }
1529   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1530 }
1531
1532
1533 /**
1534  * Task to register all hosts available in the global host list.
1535  *
1536  * @param cls NULL
1537  * @param tc the scheduler task context
1538  */
1539 static void
1540 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1541 {
1542   struct DLLOperation *dll_op;
1543   static unsigned int reg_host;
1544   unsigned int slave;
1545
1546   register_hosts_task = GNUNET_SCHEDULER_NO_TASK;
1547   if (reg_host == num_hosts - 1)
1548   {
1549     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1550                 "All hosts successfully registered\n");
1551     /* Start slaves */
1552     state = STATE_SLAVES_STARTING;
1553     for (slave = 1; slave < num_hosts; slave++)
1554     {
1555       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1556       dll_op->op = GNUNET_TESTBED_controller_link (dll_op,
1557                                                    mc,
1558                                                    hosts[slave],
1559                                                    hosts[0],
1560                                                    cfg,
1561                                                    GNUNET_YES);
1562       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1563     }
1564     return;
1565   }
1566   reg_handle = GNUNET_TESTBED_register_host (mc, hosts[++reg_host],
1567                                              host_registration_completion,
1568                                              NULL);
1569 }
1570
1571
1572 /**
1573  * Callback to signal successfull startup of the controller process.
1574  *
1575  * @param cls the closure from GNUNET_TESTBED_controller_start()
1576  * @param config the configuration with which the controller has been started;
1577  *          NULL if status is not GNUNET_OK
1578  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
1579  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
1580  */
1581 static void
1582 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
1583 {
1584   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1585     GNUNET_SCHEDULER_cancel (abort_task);
1586   if (GNUNET_OK != status)
1587   {
1588     mc_proc = NULL;
1589     printf("CRAPPP\n");
1590     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1591     return;
1592   }
1593   event_mask = 0;
1594   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
1595   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
1596   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1597   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
1598   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
1599   mc = GNUNET_TESTBED_controller_connect (config, hosts[0], event_mask,
1600                                           &controller_event_cb, NULL);
1601   if (NULL == mc)
1602   {
1603     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1604                 _("Unable to connect to master controller -- Check config\n"));
1605     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1606     return;
1607   }
1608   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1609   abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1610                                              &do_abort, (void*) __LINE__);
1611 }
1612
1613
1614 /**
1615  * Load search strings from given filename. One search string per line.
1616  *
1617  * @param filename filename of the file containing the search strings.
1618  * @param strings set of strings loaded from file. Caller needs to free this
1619  *                if number returned is greater than zero.
1620  * @param limit upper limit on the number of strings read from the file
1621  * @return number of strings found in the file. GNUNET_SYSERR on error.
1622  */
1623 static int
1624 load_search_strings (const char *filename, char ***strings, unsigned int limit)
1625 {
1626   char *data;
1627   char *buf;
1628   uint64_t filesize;
1629   unsigned int offset;
1630   int str_cnt;
1631   unsigned int i;
1632
1633   if (NULL == filename)
1634   {
1635     return GNUNET_SYSERR;
1636   }
1637
1638   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
1639   {
1640     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1641                 "Could not find search strings file %s\n", filename);
1642     return GNUNET_SYSERR;
1643   }
1644   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &filesize, GNUNET_YES, GNUNET_YES))
1645     filesize = 0;
1646   if (0 == filesize)
1647   {
1648     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Search strings file %s is empty.\n", filename);
1649     return GNUNET_SYSERR;
1650   }
1651   data = GNUNET_malloc (filesize);
1652   if (filesize != GNUNET_DISK_fn_read (filename, data, filesize))
1653   {
1654     GNUNET_free (data);
1655     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read search strings file %s.\n",
1656          filename);
1657     return GNUNET_SYSERR;
1658   }
1659   buf = data;
1660   offset = 0;
1661   str_cnt = 0;
1662   while (offset < (filesize - 1) && str_cnt < limit)
1663   {
1664     offset++;
1665     if (((data[offset] == '\n')) && (buf != &data[offset]))
1666     {
1667       data[offset] = '\0';
1668       str_cnt++;
1669       buf = &data[offset + 1];
1670     }
1671     else if ((data[offset] == '\n') || (data[offset] == '\0'))
1672       buf = &data[offset + 1];
1673   }
1674   *strings = GNUNET_malloc (sizeof (char *) * str_cnt);
1675   offset = 0;
1676   for (i = 0; i < str_cnt; i++)
1677   {
1678     GNUNET_asprintf (&(*strings)[i], "%s%s", regex_prefix, &data[offset]);
1679     offset += strlen (&data[offset]) + 1;
1680   }
1681   GNUNET_free (data);
1682   return str_cnt;
1683 }
1684
1685
1686 /**
1687  * Callbacks of this type are called by GNUNET_TESTBED_is_host_habitable to
1688  * inform whether the given host is habitable or not. The Handle returned by
1689  * GNUNET_TESTBED_is_host_habitable() is invalid after this callback is called
1690  *
1691  * @param cls NULL
1692  * @param host the host whose status is being reported; will be NULL if the host
1693  *          given to GNUNET_TESTBED_is_host_habitable() is NULL
1694  * @param status GNUNET_YES if it is habitable; GNUNET_NO if not
1695  */
1696 static void 
1697 host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *host, int status)
1698 {
1699   struct GNUNET_TESTBED_HostHabitableCheckHandle **hc_handle = cls;
1700   static unsigned int hosts_checked;
1701
1702   *hc_handle = NULL;
1703   if (GNUNET_NO == status)
1704   {
1705     if ((NULL != host) && (NULL != GNUNET_TESTBED_host_get_hostname (host)))
1706       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Host %s cannot start testbed\n"),
1707                   GNUNET_TESTBED_host_get_hostname (host));
1708     else
1709       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Testbed cannot be started on localhost\n"));
1710     GNUNET_SCHEDULER_cancel (abort_task);
1711     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, (void*) __LINE__);
1712     return;
1713   }
1714   hosts_checked++;
1715   /* printf (_("\rChecked %u hosts"), hosts_checked); */
1716   /* fflush (stdout); */
1717   if (hosts_checked < num_hosts)
1718     return;
1719   /* printf (_("\nAll hosts can start testbed. Creating peers\n")); */
1720   GNUNET_free (hc_handles);
1721   hc_handles = NULL;
1722   mc_proc = 
1723       GNUNET_TESTBED_controller_start (GNUNET_TESTBED_host_get_hostname
1724                                        (hosts[0]),
1725                                        hosts[0],
1726                                        cfg,
1727                                        status_cb,
1728                                        NULL);
1729 }
1730
1731
1732 /**
1733  * Main function that will be run by the scheduler.
1734  *
1735  * @param cls closure
1736  * @param args remaining command-line arguments
1737  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1738  * @param config configuration
1739  */
1740 static void
1741 run (void *cls, char *const *args, const char *cfgfile,
1742      const struct GNUNET_CONFIGURATION_Handle *config)
1743 {
1744   unsigned int nhost;
1745   unsigned int nsearchstrs;
1746
1747   if (NULL == args[0])
1748   {
1749     fprintf (stderr, _("No hosts-file specified on command line. Exiting.\n"));
1750     return;
1751   }
1752   if (NULL == args[1])
1753   {
1754     fprintf (stderr, _("No policy directory specified on command line. Exiting.\n"));
1755     return;
1756   }
1757   num_hosts = GNUNET_TESTBED_hosts_load_from_file (args[0], &hosts);
1758   if (0 == num_hosts)
1759   {
1760     fprintf (stderr, _("No hosts loaded. Need at least one host\n"));
1761     return;
1762   }
1763   printf (_("Checking whether given hosts can start testbed. Please wait\n"));
1764   hc_handles = GNUNET_malloc (sizeof (struct
1765                                       GNUNET_TESTBED_HostHabitableCheckHandle *) 
1766                               * num_hosts);
1767   for (nhost = 0; nhost < num_hosts; nhost++)
1768   {    
1769     if (NULL == (hc_handles[nhost] = GNUNET_TESTBED_is_host_habitable (hosts[nhost], config,
1770                                                                        &host_habitable_cb,
1771                                                                        &hc_handles[nhost])))
1772     {
1773       GNUNET_break (0);
1774       for (nhost = 0; nhost < num_hosts; nhost++)
1775         if (NULL != hc_handles[nhost])
1776           GNUNET_TESTBED_is_host_habitable_cancel (hc_handles[nhost]);
1777       GNUNET_free (hc_handles);
1778       hc_handles = NULL;
1779       break;
1780     }
1781   }
1782   if (num_hosts != nhost)
1783   {
1784     fprintf (stderr, _("Exiting\n"));
1785     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1786     return;
1787   }
1788   if (NULL == config)
1789   {
1790     fprintf (stderr, _("No configuration file given. Exiting\n"));
1791     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1792     return;
1793   }
1794
1795   if (GNUNET_OK !=
1796       GNUNET_CONFIGURATION_get_value_string (config, "REGEXPROFILER", "REGEX_PREFIX",
1797                                              &regex_prefix))
1798   {
1799     fprintf (stderr, _("Configuration option (regex_prefix) missing. Exiting\n"));
1800     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1801     return;
1802   }
1803
1804   if ( (NULL != data_filename) &&
1805        (NULL == (data_file =
1806                  GNUNET_DISK_file_open (data_filename,
1807                                         GNUNET_DISK_OPEN_READWRITE |
1808                                         GNUNET_DISK_OPEN_TRUNCATE |
1809                                         GNUNET_DISK_OPEN_CREATE,
1810                                         GNUNET_DISK_PERM_USER_READ |
1811                                         GNUNET_DISK_PERM_USER_WRITE))) )
1812     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1813                               "open",
1814                               data_filename);
1815   if (GNUNET_YES != GNUNET_DISK_directory_test (args[1], GNUNET_YES))
1816   {
1817     fprintf (stderr, _("Specified policies directory does not exist. Exiting.\n"));
1818     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1819     return;
1820   }
1821   policy_dir = args[1];
1822   if (GNUNET_YES != GNUNET_DISK_file_test (args[2]))
1823   {
1824     fprintf (stderr, _("No search strings file given. Exiting.\n"));
1825     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1826     return;
1827   }
1828   nsearchstrs = load_search_strings (args[2], &search_strings, num_search_strings);
1829   if (num_search_strings != nsearchstrs)
1830   {
1831     num_search_strings = nsearchstrs;
1832     fprintf (stderr, _("Error loading search strings. Given file does not contain enough strings. Exiting.\n"));
1833     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1834     return;
1835   }
1836   if (0 >= num_search_strings || NULL == search_strings)
1837   {
1838     fprintf (stderr, _("Error loading search strings. Exiting.\n"));
1839     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1840     return;
1841   }
1842   unsigned int i;
1843   for (i = 0; i < num_search_strings; i++)
1844     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "search string: %s\n", search_strings[i]);
1845   cfg = GNUNET_CONFIGURATION_dup (config);
1846   abort_task =
1847       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
1848                                     (GNUNET_TIME_UNIT_SECONDS, 5), &do_abort,
1849                                     (void*) __LINE__);
1850 }
1851
1852
1853 /**
1854  * Main function.
1855  *
1856  * @param argc argument count
1857  * @param argv argument values
1858  * @return 0 on success
1859  */
1860 int
1861 main (int argc, char *const *argv)
1862 {
1863   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1864     {'d', "details", "FILENAME",
1865      gettext_noop ("name of the file for writing statistics"),
1866      1, &GNUNET_GETOPT_set_string, &data_filename},
1867     {'n', "num-links", "COUNT",
1868       gettext_noop ("create COUNT number of random links between peers"),
1869       GNUNET_YES, &GNUNET_GETOPT_set_uint, &linking_factor },
1870     {'t', "matching-timeout", "TIMEOUT",
1871       gettext_noop ("wait TIMEOUT before considering a string match as failed"),
1872       GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &search_timeout },
1873     {'s', "search-delay", "DELAY",
1874       gettext_noop ("wait DELAY before starting string search"),
1875       GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &search_delay },
1876     {'a', "num-search-strings", "COUNT",
1877       gettext_noop ("number of search strings to read from search strings file"),
1878       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_search_strings },
1879     {'p', "max-path-compression", "MAX_PATH_COMPRESSION",
1880      gettext_noop ("maximum path compression length"),
1881      1, &GNUNET_GETOPT_set_uint, &max_path_compression},
1882     {'i', "no-distributed-search", "",
1883      gettext_noop ("if this option is set, only one peer is responsible for searching all strings"),
1884      0, &GNUNET_GETOPT_set_one, &no_distributed_search},
1885     GNUNET_GETOPT_OPTION_END
1886   };
1887   int ret;
1888
1889   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1890     return 2;
1891
1892   result = GNUNET_SYSERR;
1893   ret =
1894       GNUNET_PROGRAM_run (argc, argv, "gnunet-regex-profiler [OPTIONS] hosts-file policy-dir search-strings-file",
1895                           _("Profiler for regex/mesh"),
1896                           options, &run, NULL);
1897
1898   if (GNUNET_OK != ret)
1899     return ret;
1900   if (GNUNET_OK != result)
1901     return 1;
1902   return 0;
1903 }