regex profiler fixes
[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    * Announcing regexes
97    */
98   STATE_ANNOUNCE_REGEX,
99
100   /**
101    * Matching strings against announced regexes
102    */
103   STATE_SEARCH_REGEX,
104
105   /**
106    * Destroying peers; we can do this as the controller takes care of stopping a
107    * peer if it is running
108    */
109   STATE_PEERS_DESTROYING
110 };
111
112
113 /**
114  * An array of hosts loaded from the hostkeys file
115  */
116 static struct GNUNET_TESTBED_Host **hosts;
117
118 /**
119  * Peer handles.
120  */
121 struct RegexPeer
122 {
123   /**
124    * Peer id.
125    */
126   unsigned int id;
127
128   /**
129    * The actual testbed peer handle.
130    */
131   struct GNUNET_TESTBED_Peer *peer_handle;
132
133   /**
134    * Host on which the peer is running.
135    */
136   struct GNUNET_TESTBED_Host *host_handle;
137
138   /**
139    * Filename of the peer's policy file.
140    */
141   char *policy_file;
142
143   /**
144    * Peers search string.
145    */
146   const char *search_str;
147
148   /**
149    * Peer's mesh handle.
150    */
151   struct GNUNET_MESH_Handle *mesh_handle;
152
153   /**
154    * Peer's mesh tunnel handle.
155    */
156   struct GNUNET_MESH_Tunnel *mesh_tunnel_handle;
157
158   /**
159    * Testbed operation handle for the mesh service.
160    */
161   struct GNUNET_TESTBED_Operation *mesh_op_handle;
162
163   /**
164    * Peers's statistics handle.
165    */
166   struct GNUNET_STATISTICS_Handle *stats_handle;
167
168   /**
169    * Testbed operation handle for the statistics service.
170    */
171   struct GNUNET_TESTBED_Operation *stats_op_handle;
172
173   /**
174    * The starting time of a profiling step.
175    */
176   struct GNUNET_TIME_Absolute prof_start_time;
177 };
178
179 /**
180  * Array of peer handles used to pass to
181  * GNUNET_TESTBED_overlay_configure_topology
182  */
183 struct GNUNET_TESTBED_Peer **peer_handles;
184
185 /**
186  * The array of peers; we fill this as the peers are given to us by the testbed
187  */
188 static struct RegexPeer *peers;
189
190 /**
191  * Host registration handle
192  */
193 static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
194
195 /**
196  * Handle to the master controller process
197  */
198 struct GNUNET_TESTBED_ControllerProc *mc_proc;
199
200 /**
201  * Handle to the master controller
202  */
203 struct GNUNET_TESTBED_Controller *mc;
204
205 /**
206  * Handle to global configuration
207  */
208 struct GNUNET_CONFIGURATION_Handle *cfg;
209
210 /**
211  * Head of the operations list
212  */
213 struct DLLOperation *dll_op_head;
214
215 /**
216  * Tail of the operations list
217  */
218 struct DLLOperation *dll_op_tail;
219
220 /**
221  * Peer linking - topology operation
222  */
223 struct GNUNET_TESTBED_Operation *topology_op;
224
225 /**
226  * Abort task identifier
227  */
228 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
229
230 /**
231  * Host registration task identifier
232  */
233 static GNUNET_SCHEDULER_TaskIdentifier register_hosts_task;
234
235 /**
236  * Global event mask for all testbed events
237  */
238 uint64_t event_mask;
239
240 /**
241  * The starting time of a profiling step
242  */
243 struct GNUNET_TIME_Absolute prof_start_time;
244
245 /**
246  * Duration profiling step has taken
247  */
248 struct GNUNET_TIME_Relative prof_time;
249
250 /**
251  * Current peer id
252  */
253 unsigned int peer_id;
254
255 /**
256  * Number of peers to be started by the profiler
257  */
258 static unsigned int num_peers;
259
260 /**
261  * Number of hosts in the hosts array
262  */
263 static unsigned int num_hosts;
264
265 /**
266  * Factor of number of links. num_links = num_peers * linking_factor.
267  */
268 static unsigned int linking_factor;
269
270 /**
271  * Number of random links to be established between peers
272  */
273 static unsigned int num_links;
274
275 /**
276  * Number of timeout failures to tolerate
277  */
278 static unsigned int num_cont_fails;
279
280 /**
281  * Number of times we try overlay connect operations
282  */
283 static unsigned int retry_links;
284
285 /**
286  * Continuous failures during overlay connect operations
287  */
288 static unsigned int cont_fails;
289
290 /**
291  * Global testing status
292  */
293 static int result;
294
295 /**
296  * current state of profiling
297  */
298 enum State state;
299
300 /**
301  * Folder where policy files are stored.
302  */
303 static char * policy_dir;
304
305 /**
306  * Search strings.
307  */
308 static char **search_strings;
309
310 /**
311  * Number of search strings.
312  */
313 static int num_search_strings;
314
315 /**
316  * Number of peers found with search strings.
317  */
318 static unsigned int peers_found;
319
320 /**
321  * Search task identifier
322  */
323 static GNUNET_SCHEDULER_TaskIdentifier search_task;
324
325 /**
326  * Search timeout task identifier.
327  */
328 static GNUNET_SCHEDULER_TaskIdentifier search_timeout_task;
329
330 /**
331  * Search timeout in seconds.
332  */
333 static struct GNUNET_TIME_Relative search_timeout = { 60000 };
334
335 /**
336  * How long do we wait before starting the search?
337  * Default: 1 m.
338  */
339 static struct GNUNET_TIME_Relative search_delay = { 60000 };
340
341 /**
342  * Delay before setting mesh service op as done.
343  */
344 static struct GNUNET_TIME_Relative mesh_done_delay = { 1000 };
345
346 /**
347  * Delay to wait before starting to configure the overlay topology
348  */
349 static struct GNUNET_TIME_Relative conf_topo_delay = { 10000 };
350
351 /**
352  * File to log statistics to.
353  */
354 static struct GNUNET_DISK_FileHandle *data_file;
355
356 /**
357  * Filename to log statistics to.
358  */
359 static char *data_filename;
360
361 /**
362  * Maximal path compression length.
363  */
364 static unsigned int max_path_compression;
365
366 /******************************************************************************/
367 /******************************  DECLARATIONS  ********************************/
368 /******************************************************************************/
369
370
371 /**
372  * Method called whenever a peer has connected to the tunnel.
373  *
374  * @param cls closure
375  * @param peer_id peer identity the tunnel was created to, NULL on timeout
376  * @param atsi performance data for the connection
377  *
378  */
379 void
380 mesh_peer_connect_handler (void *cls,
381                            const struct GNUNET_PeerIdentity* peer_id,
382                            const struct GNUNET_ATS_Information * atsi);
383
384
385 /**
386  * Method called whenever a peer has disconnected from the tunnel.
387  * Implementations of this callback must NOT call
388  * GNUNET_MESH_tunnel_destroy immediately, but instead schedule those
389  * to run in some other task later.  However, calling
390  * "GNUNET_MESH_notify_transmit_ready_cancel" is allowed.
391  *
392  * @param cls closure
393  * @param peer_id peer identity the tunnel stopped working with
394  */
395 void
396 mesh_peer_disconnect_handler (void *cls,
397                               const struct GNUNET_PeerIdentity * peer_id);
398
399 /**
400  * Mesh connect callback.
401  *
402  * @param cls internal peer id.
403  * @param op operation handle.
404  * @param ca_result connect adapter result.
405  * @param emsg error message.
406  */
407 void
408 mesh_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
409                  void *ca_result, const char *emsg);
410
411 /**
412  * Mesh connect adapter.
413  *
414  * @param cls not used.
415  * @param cfg configuration handle.
416  *
417  * @return
418  */
419 void *
420 mesh_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg);
421
422
423 /**
424  * Adapter function called to destroy a connection to
425  * the mesh service
426  *
427  * @param cls closure
428  * @param op_result service handle returned from the connect adapter
429  */
430 void
431 mesh_da (void *cls, void *op_result);
432
433
434 /******************************************************************************/
435 /********************************  SHUTDOWN  **********************************/
436 /******************************************************************************/
437
438
439 /**
440  * Shutdown nicely
441  *
442  * @param cls NULL
443  * @param tc the task context
444  */
445 static void
446 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
447 {
448   struct DLLOperation *dll_op;
449   unsigned int nhost;
450   unsigned int peer_cnt;
451   unsigned int search_str_cnt;
452
453   for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
454   {
455     if (NULL != peers[peer_cnt].mesh_op_handle)
456       GNUNET_TESTBED_operation_done (peers[peer_cnt].mesh_op_handle);
457     if (NULL != peers[peer_cnt].stats_op_handle)
458       GNUNET_TESTBED_operation_done (peers[peer_cnt].stats_op_handle);
459   }
460   for (search_str_cnt = 0; search_str_cnt < num_search_strings; search_str_cnt++)
461   {
462     GNUNET_free (search_strings[search_str_cnt]);
463   }
464   GNUNET_free (search_strings);
465   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
466     GNUNET_SCHEDULER_cancel (abort_task);
467   if (GNUNET_SCHEDULER_NO_TASK != register_hosts_task)
468     GNUNET_SCHEDULER_cancel (register_hosts_task);
469   if (NULL != reg_handle)
470     GNUNET_TESTBED_cancel_registration (reg_handle);
471   if (NULL != topology_op)
472     GNUNET_TESTBED_operation_done (topology_op);
473   for (nhost = 0; nhost < num_hosts; nhost++)
474     if (NULL != hosts[nhost])
475       GNUNET_TESTBED_host_destroy (hosts[nhost]);
476   GNUNET_free_non_null (hosts);
477   while (NULL != (dll_op = dll_op_head))
478   {
479     GNUNET_TESTBED_operation_done (dll_op->op);
480     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
481     GNUNET_free (dll_op);
482   }
483   if (NULL != mc)
484     GNUNET_TESTBED_controller_disconnect (mc);
485   if (NULL != mc_proc)
486     GNUNET_TESTBED_controller_stop (mc_proc);
487   if (NULL != cfg)
488     GNUNET_CONFIGURATION_destroy (cfg);
489   if (NULL != data_file)
490     GNUNET_DISK_file_close (data_file);
491
492   GNUNET_SCHEDULER_shutdown (); /* Stop scheduler to shutdown testbed run */
493 }
494
495
496 /**
497  * abort task to run on test timed out
498  *
499  * @param cls NULL
500  * @param tc the task context
501  */
502 static void
503 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
504 {
505   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Aborting\n");
506   abort_task = GNUNET_SCHEDULER_NO_TASK;
507   result = GNUNET_SYSERR;
508   GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
509 }
510
511
512 /******************************************************************************/
513 /*********************  STATISTICS SERVICE CONNECTIONS  ***********************/
514 /******************************************************************************/
515
516 /**
517  * Adapter function called to establish a connection to
518  * statistics service.
519  *
520  * @param cls closure
521  * @param cfg configuration of the peer to connect to; will be available until
522  *          GNUNET_TESTBED_operation_done() is called on the operation returned
523  *          from GNUNET_TESTBED_service_connect()
524  * @return service handle to return in 'op_result', NULL on error
525  */
526 static void *
527 stats_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
528 {
529   return GNUNET_STATISTICS_create ("<driver>", cfg);
530 }
531
532
533 /**
534  * Adapter function called to destroy a connection to
535  * statistics service.
536  *
537  * @param cls closure
538  * @param op_result service handle returned from the connect adapter
539  */
540 static void
541 stats_da (void *cls, void *op_result)
542 {
543   GNUNET_STATISTICS_destroy (op_result, GNUNET_NO);
544 }
545
546
547 /**
548  * Process statistic values.
549  *
550  * @param cls closure
551  * @param subsystem name of subsystem that created the statistic
552  * @param name the name of the datum
553  * @param value the current value
554  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
555  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
556  */
557 static int
558 stats_iterator (void *cls, const char *subsystem, const char *name,
559                 uint64_t value, int is_persistent)
560 {
561   struct RegexPeer *peer = cls;
562   char output_buffer[512];
563   size_t size;
564
565   if (NULL == data_file)
566   {
567     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
568                 "%p -> %s [%s]: %llu\n",
569                 peer, subsystem, name, value);
570     return GNUNET_OK;
571   }
572   size =
573     GNUNET_snprintf (output_buffer,
574                      sizeof (output_buffer),
575                      "%p [%s] %llu %s\n",
576                      peer,
577                      subsystem, value, name);
578   if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
579     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
580
581   return GNUNET_OK;
582 }
583
584
585 /**
586  * Stats callback.
587  *
588  * @param cls closure
589  * @param success GNUNET_OK if statistics were
590  *        successfully obtained, GNUNET_SYSERR if not.
591  */
592 static void
593 stats_cb (void *cls,
594           int success)
595 {
596   static unsigned int peer_cnt;
597   struct RegexPeer *peer = cls;
598
599   if (GNUNET_OK != success)
600   {
601     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
602                 "Getting statistics for peer %u failed!\n",
603                 peer->id);
604     return;
605   }
606
607   GNUNET_TESTBED_operation_done (peer->stats_op_handle);
608   peer->stats_op_handle = NULL;
609
610   if (++peer_cnt == num_search_strings)
611   {
612     struct GNUNET_TIME_Relative delay = { 100 };
613     GNUNET_SCHEDULER_add_delayed (delay, &do_shutdown, NULL);
614   }
615 }
616
617
618 /**
619  * Function called by testbed once we are connected to stats service.
620  *
621  * @param cls the 'struct RegexPeer' for which we connected to stats
622  * @param op connect operation handle
623  * @param ca_result handle to stats service
624  * @param emsg error message on failure
625  */
626 static void
627 stats_connect_cb (void *cls,
628                   struct GNUNET_TESTBED_Operation *op,
629                   void *ca_result,
630                   const char *emsg)
631 {
632   struct RegexPeer *peer = cls;
633
634   if (NULL == ca_result || NULL != emsg)
635   {
636     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
637                 "Failed to connect to statistics service on peer %u: %s\n",
638                 peer->id, emsg);
639
640     peer->stats_handle = NULL;
641     return;
642   }
643
644   GNUNET_assert (NULL != peer->mesh_handle);
645
646   peer->stats_handle = ca_result;
647
648   peer->mesh_tunnel_handle = GNUNET_MESH_tunnel_create (peer->mesh_handle,
649                                                         NULL,
650                                                         &mesh_peer_connect_handler,
651                                                         &mesh_peer_disconnect_handler,
652                                                         peer);
653
654   peer->prof_start_time = GNUNET_TIME_absolute_get ();
655
656   GNUNET_MESH_peer_request_connect_by_string (peer->mesh_tunnel_handle,
657                                               peer->search_str);
658 }
659
660
661 /******************************************************************************/
662 /************************  MESH SERVICE CONNECTIONS  **************************/
663 /******************************************************************************/
664
665 /**
666  * Method called whenever another peer has added us to a tunnel
667  * the other peer initiated.
668  * Only called (once) upon reception of data with a message type which was
669  * subscribed to in GNUNET_MESH_connect. A call to GNUNET_MESH_tunnel_destroy
670  * causes te tunnel to be ignored and no further notifications are sent about
671  * the same tunnel.
672  *
673  * @param cls closure
674  * @param tunnel new handle to the tunnel
675  * @param initiator peer that started the tunnel
676  * @param atsi performance information for the tunnel
677  * @return initial tunnel context for the tunnel
678  *         (can be NULL -- that's not an error)
679  */
680 void *
681 mesh_inbound_tunnel_handler (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
682                              const struct GNUNET_PeerIdentity *initiator,
683                              const struct GNUNET_ATS_Information *atsi)
684 {
685   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh inbound tunnel handler.\n");
686
687   return NULL;
688 }
689
690
691 /**
692  * Function called whenever an inbound tunnel is destroyed.  Should clean up
693  * any associated state.  This function is NOT called if the client has
694  * explicitly asked for the tunnel to be destroyed using
695  * GNUNET_MESH_tunnel_destroy. It must NOT call GNUNET_MESH_tunnel_destroy on
696  * the tunnel.
697  *
698  * @param cls closure (set from GNUNET_MESH_connect)
699  * @param tunnel connection to the other end (henceforth invalid)
700  * @param tunnel_ctx place where local state associated
701  *                   with the tunnel is stored
702  */
703 void
704 mesh_tunnel_end_handler (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
705                          void *tunnel_ctx)
706 {
707   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh tunnel end handler.\n");
708 }
709
710
711 /**
712  * Method called whenever a peer has disconnected from the tunnel.
713  * Implementations of this callback must NOT call
714  * GNUNET_MESH_tunnel_destroy immediately, but instead schedule those
715  * to run in some other task later.  However, calling
716  * "GNUNET_MESH_notify_transmit_ready_cancel" is allowed.
717  *
718  * @param cls closure
719  * @param peer_id peer identity the tunnel stopped working with
720  */
721 void
722 mesh_peer_disconnect_handler (void *cls,
723                               const struct GNUNET_PeerIdentity * peer_id)
724 {
725   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh peer disconnect handler.\n");
726 }
727
728
729 /**
730  * Method called whenever a peer has connected to the tunnel.
731  *
732  * @param cls closure
733  * @param peer_id peer identity the tunnel was created to, NULL on timeout
734  * @param atsi performance data for the connection
735  *
736  */
737 void
738 mesh_peer_connect_handler (void *cls,
739                            const struct GNUNET_PeerIdentity* peer_id,
740                            const struct GNUNET_ATS_Information * atsi)
741 {
742   struct RegexPeer *peer = cls;
743   char output_buffer[512];
744   size_t size;
745
746   peers_found++;
747
748   if (NULL == peer_id)
749   {
750     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
751                 "String matching timed out for string %s on peer %u (%i/%i)\n",
752                 peer->search_str, peer->id, peers_found, num_search_strings);
753
754     printf ("String matching timed out for string %s on peer %u (%i/%i)\n",
755             peer->search_str, peer->id, peers_found, num_search_strings);
756   }
757   else
758   {
759     prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time);
760     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
761                 "String %s successfully matched on peer %u after %s (%i/%i)\n",
762                 peer->search_str, peer->id, GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
763                 peers_found, num_search_strings);
764
765     printf ("String %s successfully matched on peer %u after %s (%i/%i)\n",
766             peer->search_str, peer->id, GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
767             peers_found, num_search_strings);
768     fflush (stdout);
769
770     if (NULL != data_file)
771     {
772       size =
773         GNUNET_snprintf (output_buffer,
774                          sizeof (output_buffer),
775                          "Peer: %u (%p)\nHost: %s\nPolicy file: %s\nSearch string: %s\nSearch duration: %s\n\n",
776                          peer->id,
777                          peer,
778                          GNUNET_TESTBED_host_get_hostname (peer->host_handle),
779                          peer->policy_file,
780                          peer->search_str,
781                          GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
782
783       if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
784         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
785     }
786
787     if (NULL == peer->stats_handle)
788     {
789       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
790                   "Cannot get statistics for peer %u, stats handle is NULL!\n");
791       return;
792     }
793
794     if (NULL == GNUNET_STATISTICS_get (peer->stats_handle, "mesh", NULL,
795                                        GNUNET_TIME_UNIT_FOREVER_REL,
796                                        NULL,
797                                        &stats_iterator, peer))
798     {
799       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
800                   "Could not get mesh statistics of peer %u!\n", peer->id);
801     }
802     if (NULL == GNUNET_STATISTICS_get (peer->stats_handle, "transport", NULL,
803                                        GNUNET_TIME_UNIT_FOREVER_REL,
804                                        NULL,
805                                        &stats_iterator, peer))
806     {
807       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
808                   "Could not get transport statistics of peer %u!\n", peer->id);
809     }
810     if (NULL == GNUNET_STATISTICS_get (peer->stats_handle, "dht", NULL,
811                                        GNUNET_TIME_UNIT_FOREVER_REL,
812                                        &stats_cb,
813                                        &stats_iterator, peer))
814     {
815       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
816                   "Could not get dht statistics of peer %u!\n", peer->id);
817     }
818   }
819
820   if (peers_found == num_search_strings)
821   {
822     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
823     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
824                 "All strings successfully matched in %s\n",
825                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
826     printf ("All strings successfully matched. Shutting down.\n");
827     fflush (stdout);
828
829     if (GNUNET_SCHEDULER_NO_TASK != search_timeout_task)
830       GNUNET_SCHEDULER_cancel (search_timeout_task);
831   }
832 }
833
834
835 /**
836  * Connect by string timeout task
837  *
838  * @param cls NULL
839  * @param tc the task context
840  */
841 static void
842 do_connect_by_string_timeout (void *cls,
843                               const struct GNUNET_SCHEDULER_TaskContext * tc)
844 {
845   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
846               "Finding matches to all strings did not succeed after %s.\n",
847               GNUNET_STRINGS_relative_time_to_string (search_timeout, GNUNET_NO));
848   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
849               "Found %i of %i strings\n", peers_found, num_search_strings);
850
851   GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
852 }
853
854
855 /**
856  * Connect by string task that is run to search for a string in the NFA
857  *
858  * @param cls NULL
859  * @param tc the task context
860  */
861 static void
862 do_connect_by_string (void *cls,
863                       const struct GNUNET_SCHEDULER_TaskContext * tc)
864 {
865   unsigned int search_cnt;
866   struct RegexPeer *peer;
867
868   printf ("Starting string search.\n");
869   fflush (stdout);
870
871   for (search_cnt = 0; search_cnt < num_search_strings; search_cnt++)
872   {
873     peer = &peers[search_cnt % num_peers];
874     peer->search_str = search_strings[search_cnt];
875
876     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
877                 "Searching for string \"%s\" on peer %d with file %s\n",
878                 peer->search_str, (search_cnt % num_peers), peer->policy_file);
879
880     /* First connect to mesh service, then connect to stats service
881        and then try connecting by string in stats_connect_cb */
882     peer->mesh_op_handle =
883       GNUNET_TESTBED_service_connect (NULL,
884                                       peers->peer_handle,
885                                       "mesh",
886                                       &mesh_connect_cb,
887                                       peer,
888                                       &mesh_ca,
889                                       &mesh_da,
890                                       peer);
891   }
892
893   search_timeout_task = GNUNET_SCHEDULER_add_delayed (search_timeout,
894                                                       &do_connect_by_string_timeout, NULL);
895 }
896
897
898 /**
899  * Delayed operation done for mesh service disconnects.
900  *
901  * @param cls NULL
902  * @param tc the task context
903  */
904 static void
905 do_mesh_op_done (void *cls,
906                  const struct GNUNET_SCHEDULER_TaskContext * tc)
907 {
908   struct RegexPeer *peer = cls;
909   static unsigned int peer_cnt;
910   GNUNET_TESTBED_operation_done (peer->mesh_op_handle);
911   peer->mesh_op_handle = NULL;
912
913   if (++peer_cnt < num_peers)
914   {
915     peers[peer_cnt].mesh_op_handle =
916       GNUNET_TESTBED_service_connect (NULL,
917                                       peers[peer_cnt].peer_handle,
918                                       "mesh",
919                                       &mesh_connect_cb,
920                                       &peers[peer_cnt],
921                                       &mesh_ca,
922                                       &mesh_da,
923                                       &peers[peer_cnt]);
924   }
925 }
926
927
928 /**
929  * Mesh connect callback.
930  *
931  * @param cls internal peer id.
932  * @param op operation handle.
933  * @param ca_result connect adapter result.
934  * @param emsg error message.
935  */
936 void
937 mesh_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
938                  void *ca_result, const char *emsg)
939 {
940   struct RegexPeer *peer = (struct RegexPeer *) cls;
941   char *regex;
942   char *data;
943   char *buf;
944   uint64_t filesize;
945   unsigned int offset;
946
947   if (NULL != emsg || NULL == op || NULL == ca_result)
948   {
949     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Mesh connect failed: %s\n", emsg);
950     GNUNET_assert (0);
951   }
952   
953   GNUNET_assert (peer->mesh_handle != NULL);
954   GNUNET_assert (peer->mesh_op_handle == op);
955   GNUNET_assert (peer->mesh_handle == ca_result);
956   GNUNET_assert (NULL != peer->policy_file);
957
958   switch (state)
959   {
960   case STATE_ANNOUNCE_REGEX:
961     {
962       static unsigned int num_files_announced;
963
964       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
965                   "Announcing regexes for peer %u with file %s\n",
966                   peer->id, peer->policy_file);
967       
968       if (GNUNET_YES != GNUNET_DISK_file_test (peer->policy_file))
969       {
970         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
971                     "Could not find policy file %s\n", peer->policy_file);
972         return;
973       }
974       if (GNUNET_OK != GNUNET_DISK_file_size (peer->policy_file, &filesize, GNUNET_YES, GNUNET_YES))
975         filesize = 0;
976       if (0 == filesize)
977       {
978         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Policy file %s is empty.\n", peer->policy_file);
979         return;
980       }
981       data = GNUNET_malloc (filesize);
982       if (filesize != GNUNET_DISK_fn_read (peer->policy_file, data, filesize))
983       {
984         GNUNET_free (data);
985         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read policy file %s.\n",
986                     peer->policy_file);
987         return;
988       }
989       buf = data;
990       offset = 0;
991       regex = NULL;
992       while (offset < (filesize - 1))
993       {
994         offset++;
995         if (((data[offset] == '\n')) && (buf != &data[offset]))
996         {
997           data[offset] = '\0';
998           regex = buf;
999           GNUNET_assert (NULL != regex);
1000           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Announcing regex: %s on peer %u \n",
1001                   regex, peer->id);
1002           GNUNET_MESH_announce_regex (peer->mesh_handle, regex, max_path_compression);
1003           buf = &data[offset + 1];
1004         }
1005         else if ((data[offset] == '\n') || (data[offset] == '\0'))
1006           buf = &data[offset + 1];
1007       }
1008       GNUNET_free (data);
1009
1010       GNUNET_SCHEDULER_add_delayed (mesh_done_delay, &do_mesh_op_done, peer);
1011       
1012       if (++num_files_announced == num_peers)
1013       {
1014         state = STATE_SEARCH_REGEX;
1015
1016         prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1017         
1018         printf ("All files announced in %s.\n",
1019                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1020         printf ("Waiting %s before starting to search.\n", 
1021                 GNUNET_STRINGS_relative_time_to_string (search_delay, GNUNET_YES));
1022         fflush (stdout);
1023         
1024         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1025                     "All regexes announced in %s. Waiting %s before starting to search.\n",
1026                     GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
1027                     GNUNET_STRINGS_relative_time_to_string (search_delay, GNUNET_NO));
1028         
1029         search_task = GNUNET_SCHEDULER_add_delayed (search_delay,
1030                                                     &do_connect_by_string, NULL);    
1031       }
1032       break;
1033     }
1034   case STATE_SEARCH_REGEX:
1035     {
1036       /* First connect to the stats service, then start to search */
1037       peer->stats_op_handle =
1038         GNUNET_TESTBED_service_connect (NULL,
1039                                         peers->peer_handle,
1040                                         "statistics",
1041                                         &stats_connect_cb,
1042                                         peer,
1043                                         &stats_ca,
1044                                         &stats_da,
1045                                         peer);
1046       break;
1047     }
1048   default:
1049     GNUNET_break (0);
1050   }
1051 }
1052
1053
1054 /**
1055  * Mesh connect adapter.
1056  *
1057  * @param cls not used.
1058  * @param cfg configuration handle.
1059  *
1060  * @return
1061  */
1062 void *
1063 mesh_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
1064 {
1065   GNUNET_MESH_ApplicationType app;
1066   struct RegexPeer *peer = cls;
1067
1068   static struct GNUNET_MESH_MessageHandler handlers[] = {
1069     {NULL, 0, 0}
1070   };
1071
1072   app = (GNUNET_MESH_ApplicationType)0;
1073
1074   peer->mesh_handle =
1075     GNUNET_MESH_connect (cfg, cls, NULL, NULL, handlers, &app);
1076
1077   return peer->mesh_handle;
1078 }
1079
1080
1081 /**
1082  * Adapter function called to destroy a connection to
1083  * the mesh service
1084  *
1085  * @param cls closure
1086  * @param op_result service handle returned from the connect adapter
1087  */
1088 void
1089 mesh_da (void *cls, void *op_result)
1090 {
1091   struct RegexPeer *peer = (struct RegexPeer *) cls;
1092
1093   GNUNET_assert (peer->mesh_handle == op_result);
1094
1095   if (NULL != peer->mesh_tunnel_handle)
1096   {
1097     GNUNET_MESH_tunnel_destroy (peer->mesh_tunnel_handle);
1098     peer->mesh_tunnel_handle = NULL;
1099   }
1100
1101   if (NULL != peer->mesh_handle)
1102   {
1103     GNUNET_MESH_disconnect (peer->mesh_handle);
1104     peer->mesh_handle = NULL;
1105   }
1106 }
1107
1108
1109 /******************************************************************************/
1110 /***************************  TESTBED PEER SETUP  *****************************/
1111 /******************************************************************************/
1112
1113
1114 /**
1115  * Configure the peer overlay topology.
1116  *
1117  * @param cls NULL
1118  * @param tc the task context
1119  */
1120 static void
1121 do_configure_topology (void *cls,
1122                        const struct GNUNET_SCHEDULER_TaskContext * tc)
1123 {
1124   /*
1125     if (0 == linking_factor)
1126     linking_factor = 1;
1127     num_links = linking_factor * num_peers;
1128   */
1129   /* num_links = num_peers - 1; */
1130   num_links = linking_factor;
1131
1132   /* Do overlay connect */
1133   prof_start_time = GNUNET_TIME_absolute_get ();
1134   topology_op =
1135     GNUNET_TESTBED_overlay_configure_topology (NULL, num_peers, peer_handles,
1136                                                GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI,
1137                                                num_links,
1138                                                GNUNET_TESTBED_TOPOLOGY_DISABLE_AUTO_RETRY,
1139                                                GNUNET_TESTBED_TOPOLOGY_OPTION_END);
1140   if (NULL == topology_op)
1141   {
1142     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1143                 "Cannot create topology, op handle was NULL\n");
1144     GNUNET_assert (0);
1145   }
1146 }
1147
1148
1149 /**
1150  * Functions of this signature are called when a peer has been successfully
1151  * started or stopped.
1152  *
1153  * @param cls the closure from GNUNET_TESTBED_peer_start/stop()
1154  * @param emsg NULL on success; otherwise an error description
1155  */
1156 static void
1157 peer_churn_cb (void *cls, const char *emsg)
1158 {
1159   struct DLLOperation *dll_op = cls;
1160   struct GNUNET_TESTBED_Operation *op;
1161   static unsigned int started_peers;
1162   unsigned int peer_cnt;
1163
1164   op = dll_op->op;
1165   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1166   GNUNET_free (dll_op);
1167   if (NULL != emsg)
1168   {
1169     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1170          _("An operation has failed while starting peers\n"));
1171     GNUNET_TESTBED_operation_done (op);
1172     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1173       GNUNET_SCHEDULER_cancel (abort_task);
1174     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1175     return;
1176   }
1177   GNUNET_TESTBED_operation_done (op);
1178   if (++started_peers == num_peers)
1179   {
1180     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1181     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1182                 "All peers started successfully in %s\n",
1183                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1184     result = GNUNET_OK;
1185
1186     peer_handles = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *) * num_peers);
1187     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1188       peer_handles[peer_cnt] = peers[peer_cnt].peer_handle;
1189
1190     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1191                 "Waiting %s before starting to link peers\n", 
1192                 GNUNET_STRINGS_relative_time_to_string (conf_topo_delay, GNUNET_YES));
1193
1194     printf ("Waiting %s before starting to link peers\n", 
1195             GNUNET_STRINGS_relative_time_to_string (conf_topo_delay, GNUNET_YES));
1196     fflush (stdout);
1197
1198     state = STATE_PEERS_LINKING;
1199     GNUNET_SCHEDULER_add_delayed (conf_topo_delay, &do_configure_topology, NULL);
1200   }
1201 }
1202
1203
1204 /**
1205  * Functions of this signature are called when a peer has been successfully
1206  * created
1207  *
1208  * @param cls the closure from GNUNET_TESTBED_peer_create()
1209  * @param peer the handle for the created peer; NULL on any error during
1210  *          creation
1211  * @param emsg NULL if peer is not NULL; else MAY contain the error description
1212  */
1213 static void
1214 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
1215 {
1216   struct DLLOperation *dll_op = cls;
1217   struct RegexPeer *peer_ptr;
1218   static unsigned int created_peers;
1219   unsigned int peer_cnt;
1220
1221   if (NULL != emsg)
1222   {
1223     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1224          _("Creating a peer failed. Error: %s\n"), emsg);
1225     GNUNET_TESTBED_operation_done (dll_op->op);
1226     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1227     GNUNET_free (dll_op);
1228     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1229       GNUNET_SCHEDULER_cancel (abort_task);
1230     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1231     return;
1232   }
1233
1234   peer_ptr = dll_op->cls;
1235   GNUNET_assert (NULL == peer_ptr->peer_handle);
1236   peer_ptr->peer_handle = peer;
1237   GNUNET_TESTBED_operation_done (dll_op->op);
1238   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1239   GNUNET_free (dll_op);
1240
1241   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %i created on host %s\n",
1242               peer_ptr->id,
1243               GNUNET_TESTBED_host_get_hostname (peer_ptr->host_handle));
1244
1245   if (++created_peers == num_peers)
1246   {
1247     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1248     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1249                 "All peers created successfully in %s\n",
1250                 GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1251     /* Now peers are to be started */
1252     state = STATE_PEERS_STARTING;
1253     prof_start_time = GNUNET_TIME_absolute_get ();
1254     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
1255     {
1256       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1257       dll_op->op = GNUNET_TESTBED_peer_start (dll_op, peers[peer_cnt].peer_handle,
1258                                               &peer_churn_cb, dll_op);
1259       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1260     }
1261   }
1262 }
1263
1264 /**
1265  * Function called with a filename.
1266  *
1267  * @param cls closure
1268  * @param filename complete filename (absolute path)
1269  * @return GNUNET_OK to continue to iterate,
1270  *  GNUNET_SYSERR to abort iteration with error!
1271  */
1272 int
1273 policy_filename_cb (void *cls, const char *filename)
1274 {
1275   static unsigned int peer_cnt;
1276   struct DLLOperation *dll_op;
1277   struct RegexPeer *peer = &peers[peer_cnt];
1278
1279   GNUNET_assert (NULL != peer);
1280
1281   peer->id = peer_cnt;
1282   peer->policy_file = GNUNET_strdup (filename);
1283   /* Do not start peers on hosts[0] (master controller) */
1284   peer->host_handle = hosts[1 + (peer_cnt % (num_hosts -1))];
1285   peer->mesh_handle = NULL;
1286   peer->mesh_tunnel_handle = NULL;
1287   peer->stats_handle = NULL;
1288   peer->stats_op_handle = NULL;
1289
1290   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Creating peer %i on host %s for policy file %s\n",
1291               peer->id,
1292               GNUNET_TESTBED_host_get_hostname (peer->host_handle),
1293               filename);
1294
1295   dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1296   dll_op->cls = &peers[peer_cnt];
1297   dll_op->op = GNUNET_TESTBED_peer_create (mc,
1298                                            peer->host_handle,
1299                                            cfg,
1300                                            &peer_create_cb,
1301                                            dll_op);
1302   GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1303   peer_cnt++;
1304
1305   return GNUNET_OK;
1306 }
1307
1308
1309 /**
1310  * Controller event callback.
1311  *
1312  * @param cls NULL
1313  * @param event the controller event
1314  */
1315 static void
1316 controller_event_cb (void *cls,
1317                      const struct GNUNET_TESTBED_EventInformation *event)
1318 {
1319   struct DLLOperation *dll_op;
1320   struct GNUNET_TESTBED_Operation *op;
1321
1322   switch (state)
1323   {
1324   case STATE_SLAVES_STARTING:
1325     switch (event->type)
1326     {
1327     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1328       {
1329         static unsigned int slaves_started;
1330
1331         dll_op = event->details.operation_finished.op_cls;
1332         GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
1333         GNUNET_free (dll_op);
1334         op = event->details.operation_finished.operation;
1335         if (NULL != event->details.operation_finished.emsg)
1336         {
1337           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1338                _("An operation has failed while starting slaves\n"));
1339           GNUNET_TESTBED_operation_done (op);
1340           if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1341             GNUNET_SCHEDULER_cancel (abort_task);
1342           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1343           return;
1344         }
1345         GNUNET_TESTBED_operation_done (op);
1346         /* Proceed to start peers */
1347         if (++slaves_started == num_hosts - 1)
1348         {
1349           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1350                       "All slaves started successfully\n");
1351
1352           state = STATE_PEERS_CREATING;
1353           prof_start_time = GNUNET_TIME_absolute_get ();
1354
1355           num_peers = GNUNET_DISK_directory_scan (policy_dir,
1356                                                   NULL,
1357                                                   NULL);
1358           peers = GNUNET_malloc (sizeof (struct RegexPeer) * num_peers);
1359
1360           GNUNET_DISK_directory_scan (policy_dir,
1361                                       &policy_filename_cb,
1362                                       NULL);
1363         }
1364       }
1365       break;
1366     default:
1367       GNUNET_assert (0);
1368     }
1369     break;
1370   case STATE_PEERS_STARTING:
1371     switch (event->type)
1372     {
1373     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1374       /* Control reaches here when peer start fails */
1375     case GNUNET_TESTBED_ET_PEER_START:
1376       /* we handle peer starts in peer_churn_cb */
1377       break;
1378     default:
1379       GNUNET_assert (0);
1380     }
1381     break;
1382   case STATE_PEERS_LINKING:
1383    switch (event->type)
1384    {
1385      static unsigned int established_links;
1386    case GNUNET_TESTBED_ET_OPERATION_FINISHED:
1387      /* Control reaches here when a peer linking operation fails */
1388      if (NULL != event->details.operation_finished.emsg)
1389      {
1390        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1391                    _("An operation has failed while linking\n"));
1392        printf ("F");
1393        fflush (stdout);
1394        retry_links++;
1395        
1396        if (++cont_fails > num_cont_fails)
1397        {
1398          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1399                      "We have a very high peer linking failure rate: %u (threshold: %u)\n",
1400                      cont_fails,
1401                      num_cont_fails);
1402        }
1403      }
1404      /* We do no retries, consider this link as established */
1405      /* break; */
1406    case GNUNET_TESTBED_ET_CONNECT:
1407    {
1408      char output_buffer[512];
1409      size_t size;
1410      
1411      if (0 == established_links)
1412        printf ("Establishing links .");
1413      else
1414      {
1415        printf (".");
1416        fflush (stdout);
1417      }
1418      if (++established_links == num_links)
1419      {
1420        fflush (stdout);
1421        prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
1422        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1423                    "%u links established in %s\n",
1424                    num_links,
1425                    GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
1426        result = GNUNET_OK;
1427        GNUNET_free (peer_handles);
1428        
1429        if (NULL != data_file)
1430        {
1431          size =
1432            GNUNET_snprintf (output_buffer,
1433                             sizeof (output_buffer),
1434                             "# of peers: %u\n# of links established: %u\n"
1435                             "Time to establish links: %s\nLinking failures: %u\n"
1436                             "path compression length: %u\n# of search strings: %u\n",
1437                             num_peers,
1438                             (established_links - cont_fails),
1439                             GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
1440                             cont_fails,
1441                             max_path_compression,
1442                             num_search_strings);
1443
1444          if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
1445            GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
1446        }
1447        
1448        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1449                    "Connecting to mesh service and start announcing regex...\n");
1450        printf ("\nStarting to connect to mesh services and announce regex\n");
1451        fflush (stdout);
1452        
1453        prof_start_time = GNUNET_TIME_absolute_get ();
1454        peers[0].mesh_op_handle =
1455          GNUNET_TESTBED_service_connect (NULL,
1456                                          peers[0].peer_handle,
1457                                          "mesh",
1458                                          &mesh_connect_cb,
1459                                          &peers[0],
1460                                          &mesh_ca,
1461                                          &mesh_da,
1462                                          &peers[0]);
1463        state = STATE_ANNOUNCE_REGEX;
1464      }
1465    }
1466    break;
1467    default:
1468      GNUNET_assert (0);
1469    }
1470    break;
1471   case STATE_ANNOUNCE_REGEX:
1472   {
1473     /* Handled in service connect callback */
1474     break;
1475   }
1476   case STATE_SEARCH_REGEX:
1477   {
1478     /* Handled in service connect callback */
1479     break;
1480   }
1481   default:
1482     switch (state)
1483     {
1484     case STATE_PEERS_CREATING:
1485       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create peer\n");
1486       break;
1487     default:
1488       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1489                   "Unexpected controller_cb with state %i!\n", state);
1490     }
1491     GNUNET_assert (0);
1492   }
1493 }
1494
1495
1496 /**
1497  * Task to register all hosts available in the global host list
1498  *
1499  * @param cls NULL
1500  * @param tc the scheduler task context
1501  */
1502 static void
1503 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1504
1505
1506 /**
1507  * Callback which will be called to after a host registration succeeded or failed
1508  *
1509  * @param cls the closure
1510  * @param emsg the error message; NULL if host registration is successful
1511  */
1512 static void
1513 host_registration_completion (void *cls, const char *emsg)
1514 {
1515   reg_handle = NULL;
1516   if (NULL != emsg)
1517   {
1518     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1519                 _("Host registration failed for a host. Error: %s\n"), emsg);
1520     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1521       GNUNET_SCHEDULER_cancel (abort_task);
1522     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1523     return;
1524   }
1525   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1526 }
1527
1528
1529 /**
1530  * Task to register all hosts available in the global host list
1531  *
1532  * @param cls NULL
1533  * @param tc the scheduler task context
1534  */
1535 static void
1536 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1537 {
1538   struct DLLOperation *dll_op;
1539   static unsigned int reg_host;
1540   unsigned int slave;
1541
1542   register_hosts_task = GNUNET_SCHEDULER_NO_TASK;
1543   if (reg_host == num_hosts - 1)
1544   {
1545     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1546                 "All hosts successfully registered\n");
1547     /* Start slaves */
1548     state = STATE_SLAVES_STARTING;
1549     for (slave = 1; slave < num_hosts; slave++)
1550     {
1551       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1552       dll_op->op = GNUNET_TESTBED_controller_link (dll_op,
1553                                                    mc,
1554                                                    hosts[slave],
1555                                                    hosts[0],
1556                                                    cfg,
1557                                                    GNUNET_YES);
1558       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1559     }
1560     return;
1561   }
1562   reg_handle = GNUNET_TESTBED_register_host (mc, hosts[++reg_host],
1563                                              host_registration_completion,
1564                                              NULL);
1565 }
1566
1567
1568 /**
1569  * Callback to signal successfull startup of the controller process
1570  *
1571  * @param cls the closure from GNUNET_TESTBED_controller_start()
1572  * @param config the configuration with which the controller has been started;
1573  *          NULL if status is not GNUNET_OK
1574  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
1575  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
1576  */
1577 static void
1578 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
1579 {
1580   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1581     GNUNET_SCHEDULER_cancel (abort_task);
1582   if (GNUNET_OK != status)
1583   {
1584     mc_proc = NULL;
1585     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1586     return;
1587   }
1588   event_mask = 0;
1589   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
1590   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
1591   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1592   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
1593   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
1594   mc = GNUNET_TESTBED_controller_connect (config, hosts[0], event_mask,
1595                                           &controller_event_cb, NULL);
1596   if (NULL == mc)
1597   {
1598     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1599                 _("Unable to connect to master controller -- Check config\n"));
1600     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1601     return;
1602   }
1603   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1604   abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1605                                              &do_abort, NULL);
1606 }
1607
1608
1609 /**
1610  * Load search strings from given filename. One search string per line.
1611  *
1612  * @param filename filename of the file containing the search strings.
1613  * @param strings set of strings loaded from file. Caller needs to free this
1614  *                if number returned is greater than zero.
1615  * @param limit upper limit on the number of strings read from the file
1616  * @return number of strings found in the file. GNUNET_SYSERR on error.
1617  */
1618 static int
1619 load_search_strings (const char *filename, char ***strings, unsigned int limit)
1620 {
1621   char *data;
1622   char *buf;
1623   uint64_t filesize;
1624   unsigned int offset;
1625   int str_cnt;
1626   unsigned int i;
1627
1628   if (NULL == filename)
1629   {
1630     return GNUNET_SYSERR;
1631   }
1632
1633   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
1634   {
1635     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1636                 "Could not find search strings file %s\n", filename);
1637     return GNUNET_SYSERR;
1638   }
1639   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &filesize, GNUNET_YES, GNUNET_YES))
1640     filesize = 0;
1641   if (0 == filesize)
1642   {
1643     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Search strings file %s is empty.\n", filename);
1644     return GNUNET_SYSERR;
1645   }
1646   data = GNUNET_malloc (filesize);
1647   if (filesize != GNUNET_DISK_fn_read (filename, data, filesize))
1648   {
1649     GNUNET_free (data);
1650     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read search strings file %s.\n",
1651          filename);
1652     return GNUNET_SYSERR;
1653   }
1654   buf = data;
1655   offset = 0;
1656   str_cnt = 0;
1657   while (offset < (filesize - 1) && str_cnt < limit)
1658   {
1659     offset++;
1660     if (((data[offset] == '\n')) && (buf != &data[offset]))
1661     {
1662       data[offset] = '\0';
1663       str_cnt++;
1664       buf = &data[offset + 1];
1665     }
1666     else if ((data[offset] == '\n') || (data[offset] == '\0'))
1667       buf = &data[offset + 1];
1668   }
1669   *strings = GNUNET_malloc (sizeof (char *) * str_cnt);
1670   offset = 0;
1671   for (i = 0; i < str_cnt; i++)
1672   {
1673     (*strings)[i] = GNUNET_strdup (&data[offset]);
1674     offset += strlen ((*strings)[i]) + 1;
1675   }
1676   free (data);
1677   return str_cnt;
1678 }
1679
1680
1681 /**
1682  * Main function that will be run by the scheduler.
1683  *
1684  * @param cls closure
1685  * @param args remaining command-line arguments
1686  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1687  * @param config configuration
1688  */
1689 static void
1690 run (void *cls, char *const *args, const char *cfgfile,
1691      const struct GNUNET_CONFIGURATION_Handle *config)
1692 {
1693   unsigned int nhost;
1694
1695   if (NULL == args[0])
1696   {
1697     fprintf (stderr, _("No hosts-file specified on command line. Exiting.\n"));
1698     return;
1699   }
1700   if (NULL == args[1])
1701   {
1702     fprintf (stderr, _("No policy directory specified on command line. Exiting.\n"));
1703     return;
1704   }
1705   num_hosts = GNUNET_TESTBED_hosts_load_from_file (args[0], &hosts);
1706   if (0 == num_hosts)
1707   {
1708     fprintf (stderr, _("No hosts loaded. Need at least one host\n"));
1709     return;
1710   }
1711   for (nhost = 0; nhost < num_hosts; nhost++)
1712   {
1713     if (GNUNET_YES != GNUNET_TESTBED_is_host_habitable (hosts[nhost], config))
1714     {
1715       fprintf (stderr, _("Host %s cannot start testbed\n"),
1716                          GNUNET_TESTBED_host_get_hostname (hosts[nhost]));
1717       break;
1718     }
1719   }
1720   if (num_hosts != nhost)
1721   {
1722     fprintf (stderr, _("Exiting\n"));
1723     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1724     return;
1725   }
1726   if (NULL == config)
1727   {
1728     fprintf (stderr, _("No configuration file given. Exiting\n"));
1729     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1730     return;
1731   }
1732   if ( (NULL != data_filename) &&
1733        (NULL == (data_file =
1734                  GNUNET_DISK_file_open (data_filename,
1735                                         GNUNET_DISK_OPEN_READWRITE |
1736                                         GNUNET_DISK_OPEN_TRUNCATE |
1737                                         GNUNET_DISK_OPEN_CREATE,
1738                                         GNUNET_DISK_PERM_USER_READ |
1739                                         GNUNET_DISK_PERM_USER_WRITE))) )
1740     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1741                               "open",
1742                               data_filename);
1743   if (GNUNET_YES != GNUNET_DISK_directory_test (args[1]))
1744   {
1745     fprintf (stderr, _("Specified policies directory does not exist. Exiting.\n"));
1746     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1747     return;
1748   }
1749   policy_dir = args[1];
1750   if (GNUNET_YES != GNUNET_DISK_file_test (args[2]))
1751   {
1752     fprintf (stderr, _("No search strings file given. Exiting.\n"));
1753     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1754     return;
1755   }
1756   if (num_search_strings != load_search_strings (args[2], &search_strings, num_search_strings))
1757   {
1758     fprintf (stderr, _("Error loading search strings. Given file does not contain enough strings. Exiting.\n"));
1759     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1760     return;
1761   }
1762   if (0 >= num_search_strings || NULL == search_strings)
1763   {
1764     fprintf (stderr, _("Error loading search strings. Exiting.\n"));
1765     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1766     return;
1767   }
1768   unsigned int i;
1769   for (i = 0; i < num_search_strings; i++)
1770     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "search string: %s\n", search_strings[i]);
1771   cfg = GNUNET_CONFIGURATION_dup (config);
1772   mc_proc =
1773       GNUNET_TESTBED_controller_start (GNUNET_TESTBED_host_get_hostname
1774                                        (hosts[0]),
1775                                        hosts[0],
1776                                        cfg,
1777                                        status_cb,
1778                                        NULL);
1779   abort_task =
1780       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
1781                                     (GNUNET_TIME_UNIT_SECONDS, 5), &do_abort,
1782                                     NULL);
1783 }
1784
1785
1786 /**
1787  * Main function.
1788  *
1789  * @param argc argument count
1790  * @param argv argument values
1791  * @return 0 on success
1792  */
1793 int
1794 main (int argc, char *const *argv)
1795 {
1796   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1797     {'d', "details", "FILENAME",
1798      gettext_noop ("name of the file for writing statistics"),
1799      1, &GNUNET_GETOPT_set_string, &data_filename},
1800     {'n', "linking-factor", "FACTOR",
1801       gettext_noop ("create FACTOR times number of peers random links"),
1802       GNUNET_YES, &GNUNET_GETOPT_set_uint, &linking_factor },
1803     {'e', "num-errors", "COUNT",
1804       gettext_noop ("tolerate COUNT number of continious timeout failures"),
1805       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_cont_fails },
1806     {'t', "matching-timeout", "TIMEOUT",
1807       gettext_noop ("wait TIMEOUT before considering a string match as failed"),
1808       GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &search_timeout },
1809     {'s', "search-delay", "DELAY",
1810       gettext_noop ("wait DELAY before starting string search"),
1811       GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &search_delay },
1812     {'a', "num-search-strings", "COUNT",
1813       gettext_noop ("number of search strings to read from search strings file"),
1814       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_search_strings },
1815     {'p', "max-path-compression", "MAX_PATH_COMPRESSION",
1816      gettext_noop ("maximum path compression length"),
1817      1, &GNUNET_GETOPT_set_uint, &max_path_compression},
1818     GNUNET_GETOPT_OPTION_END
1819   };
1820   int ret;
1821
1822   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1823     return 2;
1824
1825   result = GNUNET_SYSERR;
1826   ret =
1827       GNUNET_PROGRAM_run (argc, argv, "gnunet-regex-profiler [OPTIONS] hosts-file policy-dir search-strings-file",
1828                           _("Profiler for regex/mesh"),
1829                           options, &run, NULL);
1830   if (GNUNET_OK != ret)
1831     return ret;
1832   if (GNUNET_OK != result)
1833     return 1;
1834   return 0;
1835 }