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