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