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