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