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