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