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