regex profiler: measuring the time it takes to match all strings
[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 #include <string.h>
29
30 #include "platform.h"
31 #include "gnunet_applications.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_mesh_service.h"
34 #include "gnunet_stream_lib.h"
35 #include "gnunet_testbed_service.h"
36
37 /**
38  * DLL of operations
39  */
40 struct DLLOperation
41 {
42   /**
43    * The testbed operation handle
44    */
45   struct GNUNET_TESTBED_Operation *op;
46
47   /**
48    * Closure
49    */
50   void *cls;
51
52   /**
53    * The next pointer for DLL
54    */
55   struct DLLOperation *next;
56
57   /**
58    * The prev pointer for DLL
59    */
60   struct DLLOperation *prev;
61 };
62
63
64 /**
65  * Available states during profiling
66  */
67 enum State
68 {
69   /**
70    * Initial state
71    */
72   STATE_INIT = 0,
73
74   /**
75    * Starting slaves
76    */
77   STATE_SLAVES_STARTING,
78
79   /**
80    * Creating peers
81    */
82   STATE_PEERS_CREATING,
83
84   /**
85    * Starting peers
86    */
87   STATE_PEERS_STARTING,
88
89   /**
90    * Linking peers
91    */
92   STATE_PEERS_LINKING,
93
94   /**
95    * Destroying peers; we can do this as the controller takes care of stopping a
96    * peer if it is running
97    */
98   STATE_PEERS_DESTROYING
99 };
100
101
102 /**
103  * An array of hosts loaded from the hostkeys file
104  */
105 static struct GNUNET_TESTBED_Host **hosts;
106
107 /**
108  * Peer handles.
109  */
110 struct Peer
111 {
112   /**
113    * The actual testbed peer handle.
114    */
115   struct GNUNET_TESTBED_Peer *peer_handle;
116
117   /**
118    * Peer's mesh handle.
119    */
120   struct GNUNET_MESH_Handle *mesh_handle;
121
122   /**
123    * Peer's mesh tunnel handle.
124    */
125   struct GNUNET_MESH_Tunnel *mesh_tunnel_handle;
126
127   /**
128    * Host on which the peer is running.
129    */
130   struct GNUNET_TESTBED_Host *host_handle;
131
132   /**
133    * Testbed operation handle.
134    */
135   struct GNUNET_TESTBED_Operation *op_handle;
136
137   /**
138    * Filename of the peer's policy file.
139    */
140   char *policy_file;
141 };
142
143 /**
144  * Array of peer handles used to pass to
145  * GNUNET_TESTBED_overlay_configure_topology
146  */
147 struct GNUNET_TESTBED_Peer **peer_handles;
148
149 /**
150  * The array of peers; we fill this as the peers are given to us by the testbed
151  */
152 static struct Peer *peers;
153
154 /**
155  * Host registration handle
156  */
157 static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
158
159 /**
160  * Handle to the master controller process
161  */
162 struct GNUNET_TESTBED_ControllerProc *mc_proc;
163
164 /**
165  * Handle to the master controller
166  */
167 struct GNUNET_TESTBED_Controller *mc;
168
169 /**
170  * Handle to global configuration
171  */
172 struct GNUNET_CONFIGURATION_Handle *cfg;
173
174 /**
175  * Head of the operations list
176  */
177 struct DLLOperation *dll_op_head;
178
179 /**
180  * Tail of the operations list
181  */
182 struct DLLOperation *dll_op_tail;
183
184 /**
185  * Peer linking - topology operation
186  */
187 struct GNUNET_TESTBED_Operation *topology_op;
188
189 /**
190  * Abort task identifier
191  */
192 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
193
194 /**
195  * Host registration task identifier
196  */
197 static GNUNET_SCHEDULER_TaskIdentifier register_hosts_task;
198
199 /**
200  * Global event mask for all testbed events
201  */
202 uint64_t event_mask;
203
204 /**
205  * The starting time of a profiling step
206  */
207 struct GNUNET_TIME_Absolute prof_start_time;
208
209 /**
210  * Duration profiling step has taken
211  */
212 struct GNUNET_TIME_Relative prof_time;
213
214 /**
215  * Current peer id
216  */
217 unsigned int peer_id;
218
219 /**
220  * Number of peers to be started by the profiler
221  */
222 static unsigned int num_peers;
223
224 /**
225  * Number of hosts in the hosts array
226  */
227 static unsigned int num_hosts;
228
229 /**
230  * Number of random links to be established between peers
231  */
232 static unsigned int num_links;
233
234 /**
235  * Number of timeout failures to tolerate
236  */
237 static unsigned int num_cont_fails;
238
239 /**
240  * Number of times we try overlay connect operations
241  */
242 static unsigned int retry_links;
243
244 /**
245  * Continuous failures during overlay connect operations
246  */
247 static unsigned int cont_fails;
248
249 /**
250  * Global testing status
251  */
252 static int result;
253
254 /**
255  * current state of profiling
256  */
257 enum State state;
258
259 /**
260  * Folder where policy files are stored.
261  */
262 static char * policy_dir;
263
264 /**
265  * Search strings.
266  */
267 static char **search_strings;
268
269 /**
270  * Number of search strings.
271  */
272 static int num_search_strings;
273
274 /**
275  * Number of peers found with search strings.
276  */
277 static unsigned int peers_found;
278
279 /**
280  * Search task identifier
281  */
282 static GNUNET_SCHEDULER_TaskIdentifier search_task;
283
284 /**
285  * Search timeout task identifier.
286  */
287 static GNUNET_SCHEDULER_TaskIdentifier search_timeout_task;
288
289 /**
290  * Search timeout in seconds.
291  */
292 static long search_timeout_sec;
293
294 /**
295  * Search wait time in minutes.
296  */
297 static long search_wait_min;
298
299
300 /**
301  * Shutdown nicely
302  *
303  * @param cls NULL
304  * @param tc the task context
305  */
306 static void
307 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
308 {
309   struct DLLOperation *dll_op;
310   unsigned int nhost;
311   unsigned int peer_cnt;
312   unsigned int search_str_cnt;
313  
314   for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
315   {
316     if (NULL != peers[peer_cnt].op_handle)
317       GNUNET_TESTBED_operation_cancel (peers[peer_cnt].op_handle);
318   }
319   for (search_str_cnt = 0; search_str_cnt < num_search_strings; search_str_cnt++)
320   {
321     GNUNET_free (search_strings[search_str_cnt]);
322   }
323   GNUNET_free (search_strings);
324   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
325     GNUNET_SCHEDULER_cancel (abort_task);
326   if (GNUNET_SCHEDULER_NO_TASK != register_hosts_task)
327     GNUNET_SCHEDULER_cancel (register_hosts_task);
328   if (NULL != reg_handle)
329     GNUNET_TESTBED_cancel_registration (reg_handle);
330   if (NULL != topology_op)
331     GNUNET_TESTBED_operation_cancel (topology_op);
332   for (nhost = 0; nhost < num_hosts; nhost++)
333     if (NULL != hosts[nhost])
334       GNUNET_TESTBED_host_destroy (hosts[nhost]);
335   GNUNET_free_non_null (hosts);
336   while (NULL != (dll_op = dll_op_head))
337   {
338     GNUNET_TESTBED_operation_cancel (dll_op->op);
339     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
340     GNUNET_free (dll_op);
341   }
342   if (NULL != mc)
343     GNUNET_TESTBED_controller_disconnect (mc);
344   if (NULL != mc_proc)
345     GNUNET_TESTBED_controller_stop (mc_proc);
346   if (NULL != cfg)
347     GNUNET_CONFIGURATION_destroy (cfg);
348
349   GNUNET_SCHEDULER_shutdown (); /* Stop scheduler to shutdown testbed run */
350 }
351
352
353 /**
354  * abort task to run on test timed out
355  *
356  * @param cls NULL
357  * @param tc the task context
358  */
359 static void
360 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
361 {
362   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Aborting\n");
363   abort_task = GNUNET_SCHEDULER_NO_TASK;
364   result = GNUNET_SYSERR;
365   GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
366 }
367
368
369 /**
370  * Method called whenever another peer has added us to a tunnel
371  * the other peer initiated.
372  * Only called (once) upon reception of data with a message type which was
373  * subscribed to in GNUNET_MESH_connect. A call to GNUNET_MESH_tunnel_destroy
374  * causes te tunnel to be ignored and no further notifications are sent about
375  * the same tunnel.
376  *
377  * @param cls closure
378  * @param tunnel new handle to the tunnel
379  * @param initiator peer that started the tunnel
380  * @param atsi performance information for the tunnel
381  * @return initial tunnel context for the tunnel
382  *         (can be NULL -- that's not an error)
383  */
384 void *
385 mesh_inbound_tunnel_handler (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
386                              const struct GNUNET_PeerIdentity *initiator,
387                              const struct GNUNET_ATS_Information *atsi)
388 {
389   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh inbound tunnel handler.\n");
390
391   return NULL;
392 }
393
394
395 /**
396  * Function called whenever an inbound tunnel is destroyed.  Should clean up
397  * any associated state.  This function is NOT called if the client has
398  * explicitly asked for the tunnel to be destroyed using
399  * GNUNET_MESH_tunnel_destroy. It must NOT call GNUNET_MESH_tunnel_destroy on
400  * the tunnel.
401  *
402  * @param cls closure (set from GNUNET_MESH_connect)
403  * @param tunnel connection to the other end (henceforth invalid)
404  * @param tunnel_ctx place where local state associated
405  *                   with the tunnel is stored
406  */
407 void
408 mesh_tunnel_end_handler (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
409                          void *tunnel_ctx)
410 {
411   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh tunnel end handler.\n");
412 }
413
414
415 /**
416  * Method called whenever a peer has disconnected from the tunnel.
417  * Implementations of this callback must NOT call
418  * GNUNET_MESH_tunnel_destroy immediately, but instead schedule those
419  * to run in some other task later.  However, calling
420  * "GNUNET_MESH_notify_transmit_ready_cancel" is allowed.
421  *
422  * @param cls closure
423  * @param peer_id peer identity the tunnel stopped working with
424  */
425 void
426 mesh_peer_disconnect_handler (void *cls,
427                               const struct GNUNET_PeerIdentity * peer_id)
428 {
429   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh peer disconnect handler.\n");
430 }
431
432
433 /**
434  * Method called whenever a peer has connected to the tunnel.
435  *
436  * @param cls closure
437  * @param peer_id peer identity the tunnel was created to, NULL on timeout
438  * @param atsi performance data for the connection
439  *
440  */
441 void
442 mesh_peer_connect_handler (void *cls,
443                            const struct GNUNET_PeerIdentity* peer_id,
444                            const struct GNUNET_ATS_Information * atsi)
445 {
446   const char * search_str = (const char *)cls;
447
448   peers_found++;
449
450   if (NULL == peer_id)
451   {
452     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
453                 "String matching timed out for string %s (%i/%i)\n",
454                 search_str, peers_found, num_search_strings);
455   }
456   else
457   {
458     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
459                 "String %s successfully matched (%i/%i)\n", 
460                 search_str, peers_found, num_search_strings);
461   }
462
463   if (peers_found == num_search_strings)
464   {
465
466     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
467     printf ("\nAll strings successfully matched in %.2f minutes\n", ((double)prof_time.rel_value / 1000.0 / 60.0));
468     GNUNET_SCHEDULER_cancel (search_timeout_task);
469     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
470   }
471 }
472
473
474 /**
475  * Connect by string timeout task
476  *
477  * @param cls NULL
478  * @param tc the task context
479  */
480 static void
481 do_connect_by_string_timeout (void *cls,
482                               const struct GNUNET_SCHEDULER_TaskContext * tc)
483 {
484   long sec = (long)cls;
485
486   printf ("Searching for all strings did not succeed after %ld seconds\n", sec);
487   printf ("Found %i of %i strings\n", peers_found, num_search_strings);
488
489   GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
490 }
491
492
493 /**
494  * Connect by string task that is run to search for a string in the NFA
495  *
496  * @param cls NULL
497  * @param tc the task context
498  */
499 static void
500 do_connect_by_string (void *cls,
501                       const struct GNUNET_SCHEDULER_TaskContext * tc)
502 {
503   unsigned int search_cnt;
504   struct Peer *peer;
505
506   for (search_cnt = 0; search_cnt < num_search_strings; search_cnt++)
507   {
508     peer = &peers[search_cnt % num_peers];
509
510     printf ("Searching for string \"%s\" on peer %d\n", 
511             search_strings[search_cnt], (search_cnt % num_peers));
512
513     peer->mesh_tunnel_handle = GNUNET_MESH_tunnel_create (peer->mesh_handle,
514                                                           NULL,
515                                                           &mesh_peer_connect_handler,
516                                                           &mesh_peer_disconnect_handler,
517                                                           search_strings[search_cnt]);
518     GNUNET_MESH_peer_request_connect_by_string (peer->mesh_tunnel_handle,
519                                                 search_strings[search_cnt]);
520       
521   }
522
523   prof_start_time = GNUNET_TIME_absolute_get ();
524
525   search_timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
526                                                       (GNUNET_TIME_UNIT_SECONDS, search_timeout_sec),
527                                                       &do_connect_by_string_timeout, (void *)search_timeout_sec);
528 }
529
530
531 /**
532  * Mesh connect callback.
533  *
534  * @param cls internal peer id.
535  * @param op operation handle.
536  * @param ca_result connect adapter result.
537  * @param emsg error message.
538  */
539 void
540 mesh_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
541                  void *ca_result, const char *emsg)
542 {
543   static unsigned int connected_mesh_handles;
544   struct Peer *peer = (struct Peer *) cls;
545   char *regex;
546   char *data;
547   char *buf;
548   uint64_t filesize;
549   unsigned int offset;
550
551   if (NULL != emsg)
552   {
553     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Mesh connect failed: %s\n", emsg);
554     GNUNET_assert (0);
555   }
556
557   GNUNET_assert (peer->op_handle == op);
558   GNUNET_assert (peer->mesh_handle == ca_result);
559   GNUNET_assert (NULL != peer->policy_file);
560
561   printf ("Announcing regexes for peer with file %s\n", peer->policy_file);
562   fflush (stdout);
563
564   if (GNUNET_YES != GNUNET_DISK_file_test (peer->policy_file))
565   {
566     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
567                 "Could not find policy file %s\n", peer->policy_file);
568     return;
569   }
570   if (GNUNET_OK != GNUNET_DISK_file_size (peer->policy_file, &filesize, GNUNET_YES, GNUNET_YES))
571     filesize = 0;
572   if (0 == filesize)
573   {
574     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Policy file %s is empty.\n", peer->policy_file);
575     return;
576   }
577   data = GNUNET_malloc (filesize);
578   if (filesize != GNUNET_DISK_fn_read (peer->policy_file, data, filesize))
579   {
580     GNUNET_free (data);
581     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read policy file %s.\n",
582          peer->policy_file);
583     return;
584   }
585   buf = data;
586   offset = 0;
587   regex = NULL;
588   while (offset < (filesize - 1))
589   {
590     offset++;
591     if (((data[offset] == '\n')) && (buf != &data[offset]))
592     {
593       data[offset] = '\0';
594       regex = buf;
595       GNUNET_assert (NULL != regex);
596       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Announcing regex: %s\n", regex);
597       GNUNET_MESH_announce_regex (peer->mesh_handle, regex);
598       buf = &data[offset + 1];
599     }
600     else if ((data[offset] == '\n') || (data[offset] == '\0'))
601       buf = &data[offset + 1];
602   }
603   GNUNET_free (data);
604
605   if (++connected_mesh_handles == num_peers)
606   {
607     printf ("\nAll mesh handles connected.\nWaiting %ld minutes before starting to search.\n", 
608             search_wait_min);
609
610     search_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
611                                                 (GNUNET_TIME_UNIT_MINUTES, search_wait_min),
612                                                 &do_connect_by_string, NULL);
613   }
614 }
615
616
617 /**
618  * Mesh connect adapter.
619  *
620  * @param cls not used.
621  * @param cfg configuration handle.
622  *
623  * @return
624  */
625 void *
626 mesh_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
627 {
628   GNUNET_MESH_ApplicationType app;
629   struct Peer *peer = (struct Peer *) cls;
630
631   static struct GNUNET_MESH_MessageHandler handlers[] = {
632     {NULL, 0, 0}
633   };
634
635   app = (GNUNET_MESH_ApplicationType)0;
636
637   peer->mesh_handle =
638     GNUNET_MESH_connect (cfg, cls, NULL, NULL, handlers, &app);
639
640   return peer->mesh_handle;
641 }
642
643
644 /**
645  * Adapter function called to destroy a connection to
646  * the mesh service
647  *
648  * @param cls closure
649  * @param op_result service handle returned from the connect adapter
650  */
651 void
652 mesh_da (void *cls, void *op_result)
653 {
654   struct Peer *peer = (struct Peer *) cls;
655
656   GNUNET_assert (peer->mesh_handle == op_result);
657
658   if (NULL != peer->mesh_tunnel_handle)
659   {
660     GNUNET_MESH_tunnel_destroy (peer->mesh_tunnel_handle);
661     peer->mesh_tunnel_handle = NULL;
662   }
663   if (NULL != peer->mesh_handle)
664   {
665     GNUNET_MESH_disconnect (peer->mesh_handle);
666     peer->mesh_handle = NULL;
667   }
668 }
669
670
671 /**
672  * Functions of this signature are called when a peer has been successfully
673  * started or stopped.
674  *
675  * @param cls the closure from GNUNET_TESTBED_peer_start/stop()
676  * @param emsg NULL on success; otherwise an error description
677  */
678 static void
679 peer_churn_cb (void *cls, const char *emsg)
680 {
681   struct DLLOperation *dll_op = cls;
682   struct GNUNET_TESTBED_Operation *op;
683   static unsigned int started_peers;
684   unsigned int peer_cnt;
685
686   op = dll_op->op;
687   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
688   GNUNET_free (dll_op);
689   if (NULL != emsg)
690   {
691     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
692          _("An operation has failed while starting peers\n"));
693     GNUNET_TESTBED_operation_done (op);
694     GNUNET_SCHEDULER_cancel (abort_task);
695     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
696     return;
697   }
698   GNUNET_TESTBED_operation_done (op);
699   if (++started_peers == num_peers)
700   {
701     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
702     printf ("All peers started successfully in %.2f seconds\n",
703             ((double) prof_time.rel_value) / 1000.00);
704     result = GNUNET_OK;
705     if (0 == num_links)
706     {
707       GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
708       return;
709     }
710
711     peer_handles = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *) * num_peers);
712     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
713       peer_handles[peer_cnt] = peers[peer_cnt].peer_handle;
714
715     state = STATE_PEERS_LINKING;
716     /* Do overlay connect */
717     prof_start_time = GNUNET_TIME_absolute_get ();
718     topology_op =
719         GNUNET_TESTBED_overlay_configure_topology (NULL, num_peers, peer_handles,
720                                                    GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI,
721                                                    num_links,
722                                                    GNUNET_TESTBED_TOPOLOGY_OPTION_END);
723     if (NULL == topology_op)
724     {
725       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
726                   "Cannot create topology, op handle was NULL\n");
727       GNUNET_assert (0);
728     }
729   }
730 }
731
732
733 /**
734  * Functions of this signature are called when a peer has been successfully
735  * created
736  *
737  * @param cls the closure from GNUNET_TESTBED_peer_create()
738  * @param peer the handle for the created peer; NULL on any error during
739  *          creation
740  * @param emsg NULL if peer is not NULL; else MAY contain the error description
741  */
742 static void
743 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
744 {
745   struct DLLOperation *dll_op = cls;
746   struct Peer *peer_ptr;
747   static unsigned int created_peers;
748   unsigned int peer_cnt;
749
750   if (NULL != emsg)
751   {
752     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
753          _("Creating a peer failed. Error: %s\n"), emsg);
754     GNUNET_TESTBED_operation_done (dll_op->op);
755     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
756     GNUNET_free (dll_op);
757     GNUNET_SCHEDULER_cancel (abort_task);
758     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
759     return;
760   }
761
762   peer_ptr = dll_op->cls;
763   GNUNET_assert (NULL == peer_ptr->peer_handle);
764   peer_ptr->peer_handle = peer;
765   GNUNET_TESTBED_operation_done (dll_op->op);
766   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
767   GNUNET_free (dll_op);
768
769   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %i created on host %s\n",
770               created_peers,
771               GNUNET_TESTBED_host_get_hostname (peer_ptr->host_handle));
772
773   if (++created_peers == num_peers)
774   {
775     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
776     printf ("All peers created successfully in %.2f seconds\n",
777             ((double) prof_time.rel_value) / 1000.00);
778     /* Now peers are to be started */
779     state = STATE_PEERS_STARTING;
780     prof_start_time = GNUNET_TIME_absolute_get ();
781     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
782     {
783       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
784       dll_op->op = GNUNET_TESTBED_peer_start (dll_op, peers[peer_cnt].peer_handle,
785                                               &peer_churn_cb, dll_op);
786       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
787     }
788   }
789 }
790
791 /**
792  * Function called with a filename.
793  *
794  * @param cls closure
795  * @param filename complete filename (absolute path)
796  * @return GNUNET_OK to continue to iterate,
797  *  GNUNET_SYSERR to abort iteration with error!
798  */
799 int
800 policy_filename_cb (void *cls, const char *filename)
801 {
802   static unsigned int peer_cnt;
803   struct DLLOperation *dll_op;
804
805   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating peer %i on host %s for policy file %s\n",
806               peer_cnt,
807               GNUNET_TESTBED_host_get_hostname (hosts[peer_cnt % num_hosts]),
808               filename);
809
810   peers[peer_cnt].policy_file = GNUNET_strdup (filename);
811   peers[peer_cnt].host_handle = hosts[peer_cnt % num_hosts];
812
813   dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
814   dll_op->cls = &peers[peer_cnt];
815   dll_op->op = GNUNET_TESTBED_peer_create (mc,
816                                            hosts[peer_cnt % num_hosts],
817                                            cfg,
818                                            &peer_create_cb,
819                                            dll_op);
820   GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
821   peer_cnt++;
822
823   return GNUNET_OK;
824 }
825
826
827 /**
828  * Controller event callback
829  *
830  * @param cls NULL
831  * @param event the controller event
832  */
833 static void
834 controller_event_cb (void *cls,
835                      const struct GNUNET_TESTBED_EventInformation *event)
836 {
837   struct DLLOperation *dll_op;
838   struct GNUNET_TESTBED_Operation *op;
839
840   switch (state)
841   {
842   case STATE_SLAVES_STARTING:
843     switch (event->type)
844     {
845     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
846       {
847         static unsigned int slaves_started;
848
849         dll_op = event->details.operation_finished.op_cls;
850         GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
851         GNUNET_free (dll_op);
852         op = event->details.operation_finished.operation;
853         if (NULL != event->details.operation_finished.emsg)
854         {
855           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
856                _("An operation has failed while starting slaves\n"));
857           GNUNET_TESTBED_operation_done (op);
858           GNUNET_SCHEDULER_cancel (abort_task);
859           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
860           return;
861         }
862         GNUNET_TESTBED_operation_done (op);
863         /* Proceed to start peers */
864         if (++slaves_started == num_hosts - 1)
865         {
866           printf ("All slaves started successfully\n");
867
868           state = STATE_PEERS_CREATING;
869           prof_start_time = GNUNET_TIME_absolute_get ();
870
871           num_peers = GNUNET_DISK_directory_scan (policy_dir,
872                                                   NULL,
873                                                   NULL);
874           peers = GNUNET_malloc (sizeof (struct Peer) * num_peers);
875
876           GNUNET_DISK_directory_scan (policy_dir,
877                                       &policy_filename_cb,
878                                       NULL);
879         }
880       }
881       break;
882     default:
883       GNUNET_assert (0);
884     }
885     break;
886   case STATE_PEERS_STARTING:
887     switch (event->type)
888     {
889     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
890       /* Control reaches here when peer start fails */
891     case GNUNET_TESTBED_ET_PEER_START:
892       /* we handle peer starts in peer_churn_cb */
893       break;
894     default:
895       GNUNET_assert (0);
896     }
897     break;
898   case STATE_PEERS_LINKING:
899    switch (event->type)
900     {
901     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
902       /* Control reaches here when a peer linking operation fails */
903       if (NULL != event->details.operation_finished.emsg)
904       {
905         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
906              _("An operation has failed while linking\n"));
907         retry_links++;
908         if (++cont_fails > num_cont_fails)
909         {
910           printf ("\nAborting due to very high failure rate");
911           GNUNET_SCHEDULER_cancel (abort_task);
912           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
913         }
914       }
915       break;
916     case GNUNET_TESTBED_ET_CONNECT:
917       {
918         static unsigned int established_links;
919         unsigned int peer_cnt;
920
921         if (0 == established_links)
922           printf ("Establishing links\n .");
923         else
924         {
925           printf (".");
926           fflush (stdout);
927         }
928         if (++established_links == num_links)
929         {
930           prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
931           printf ("\n%u links established in %.2f seconds\n",
932                   num_links, ((double) prof_time.rel_value) / 1000.00);
933           result = GNUNET_OK;
934           GNUNET_free (peer_handles);
935           printf ("\nConnecting to mesh service...\n");
936           for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
937           {
938             peers[peer_cnt].op_handle = GNUNET_TESTBED_service_connect (NULL,
939                                                                         peers[peer_cnt].peer_handle,
940                                                                         "mesh",
941                                                                         &mesh_connect_cb,
942                                                                         &peers[peer_cnt],
943                                                                         &mesh_ca,
944                                                                         &mesh_da,
945                                                                         &peers[peer_cnt]);
946           }
947         }
948       }
949       break;
950     default:
951       GNUNET_assert (0);
952     }
953     break;
954   default:
955     GNUNET_assert (0);
956   }
957 }
958
959
960 /**
961  * Task to register all hosts available in the global host list
962  *
963  * @param cls NULL
964  * @param tc the scheduler task context
965  */
966 static void
967 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
968
969
970 /**
971  * Callback which will be called to after a host registration succeeded or failed
972  *
973  * @param cls the closure
974  * @param emsg the error message; NULL if host registration is successful
975  */
976 static void
977 host_registration_completion (void *cls, const char *emsg)
978 {
979   reg_handle = NULL;
980   if (NULL != emsg)
981   {
982     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
983                 _("Host registration failed for a host. Error: %s\n"), emsg);
984     GNUNET_SCHEDULER_cancel (abort_task);
985     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
986     return;
987   }
988   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
989 }
990
991
992 /**
993  * Task to register all hosts available in the global host list
994  *
995  * @param cls NULL
996  * @param tc the scheduler task context
997  */
998 static void
999 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1000 {
1001   struct DLLOperation *dll_op;
1002   static unsigned int reg_host;
1003   unsigned int slave;
1004
1005   register_hosts_task = GNUNET_SCHEDULER_NO_TASK;
1006   if (reg_host == num_hosts - 1)
1007   {
1008     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1009                 "All hosts successfully registered\n");
1010     /* Start slaves */
1011     state = STATE_SLAVES_STARTING;
1012     for (slave = 1; slave < num_hosts; slave++)
1013     {
1014       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1015       dll_op->op = GNUNET_TESTBED_controller_link (dll_op,
1016                                                    mc,
1017                                                    hosts[slave],
1018                                                    hosts[0],
1019                                                    cfg,
1020                                                    GNUNET_YES);
1021       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1022     }
1023     return;
1024   }
1025   reg_handle = GNUNET_TESTBED_register_host (mc, hosts[++reg_host],
1026                                              host_registration_completion,
1027                                              NULL);
1028 }
1029
1030
1031 /**
1032  * Callback to signal successfull startup of the controller process
1033  *
1034  * @param cls the closure from GNUNET_TESTBED_controller_start()
1035  * @param config the configuration with which the controller has been started;
1036  *          NULL if status is not GNUNET_OK
1037  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
1038  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
1039  */
1040 static void
1041 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
1042 {
1043   GNUNET_SCHEDULER_cancel (abort_task);
1044   if (GNUNET_OK != status)
1045   {
1046     mc_proc = NULL;
1047     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1048     return;
1049   }
1050   event_mask = 0;
1051   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
1052   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
1053   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1054   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
1055   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
1056   mc = GNUNET_TESTBED_controller_connect (config, hosts[0], event_mask,
1057                                           &controller_event_cb, NULL);
1058   if (NULL == mc)
1059   {
1060     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1061                 _("Unable to connect to master controller -- Check config\n"));
1062     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1063     return;
1064   }
1065   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1066   abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1067                                              &do_abort, NULL);
1068 }
1069
1070 /**
1071  * Load search strings from given filename. One search string per line.
1072  *
1073  * @param filename filename of the file containing the search strings.
1074  * @param strings set of strings loaded from file. Caller needs to free this 
1075  *                if number returned is greater than zero.
1076  * @return number of strings found in the file. GNUNET_SYSERR on error.
1077  */
1078 static int
1079 load_search_strings (const char *filename, char ***strings)
1080 {
1081   char *data;
1082   char *buf;
1083   uint64_t filesize;
1084   unsigned int offset;
1085   int str_cnt;
1086   unsigned int i;
1087
1088   if (NULL == filename)
1089   {
1090     return GNUNET_SYSERR;
1091   }
1092
1093   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
1094   {
1095     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1096                 "Could not find search strings file %s\n", filename);
1097     return GNUNET_SYSERR;
1098   }
1099   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &filesize, GNUNET_YES, GNUNET_YES))
1100     filesize = 0;
1101   if (0 == filesize)
1102   {
1103     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Search strings file %s is empty.\n", filename);
1104     return GNUNET_SYSERR;
1105   }
1106   data = GNUNET_malloc (filesize);
1107   if (filesize != GNUNET_DISK_fn_read (filename, data, filesize))
1108   {
1109     GNUNET_free (data);
1110     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read search strings file %s.\n",
1111          filename);
1112     return GNUNET_SYSERR;
1113   }
1114   buf = data;
1115   offset = 0;
1116   str_cnt = 0;
1117   while (offset < (filesize - 1))
1118   {
1119     offset++;
1120     if (((data[offset] == '\n')) && (buf != &data[offset]))
1121     {
1122       data[offset] = '\0';
1123       str_cnt++;
1124       buf = &data[offset + 1];
1125     }
1126     else if ((data[offset] == '\n') || (data[offset] == '\0'))
1127       buf = &data[offset + 1];
1128   }
1129   *strings = GNUNET_malloc (sizeof (char *) * str_cnt);
1130   offset = 0;
1131   for (i = 0; i < str_cnt; i++)
1132   {
1133     (*strings)[i] = GNUNET_strdup (&data[offset]);
1134     offset += strlen ((*strings)[i]) + 1;
1135   }
1136   free (data);
1137   return str_cnt;
1138 }
1139
1140 /**
1141  * Main function that will be run by the scheduler.
1142  *
1143  * @param cls closure
1144  * @param args remaining command-line arguments
1145  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1146  * @param config configuration
1147  */
1148 static void
1149 run (void *cls, char *const *args, const char *cfgfile,
1150      const struct GNUNET_CONFIGURATION_Handle *config)
1151 {
1152   unsigned int nhost;
1153
1154   if (NULL == args[0])
1155   {
1156     fprintf (stderr, _("No hosts-file specified on command line. Exiting.\n"));
1157     return;
1158   }
1159   if (NULL == args[1])
1160   {
1161     fprintf (stderr, _("No policy directory specified on command line. Exiting.\n"));
1162     return;
1163   }
1164   num_hosts = GNUNET_TESTBED_hosts_load_from_file (args[0], &hosts);
1165   if (0 == num_hosts)
1166   {
1167     fprintf (stderr, _("No hosts loaded. Need at least one host\n"));
1168     return;
1169   }
1170   for (nhost = 0; nhost < num_hosts; nhost++)
1171   {
1172     if (GNUNET_YES != GNUNET_TESTBED_is_host_habitable (hosts[nhost]))
1173     {
1174       fprintf (stderr, _("Host %s cannot start testbed\n"),
1175                          GNUNET_TESTBED_host_get_hostname (hosts[nhost]));
1176       break;
1177     }
1178   }
1179   if (num_hosts != nhost)
1180   {
1181     fprintf (stderr, _("Exiting\n"));
1182     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1183     return;
1184   }
1185   if (NULL == config)
1186   {
1187     fprintf (stderr, _("No configuration file given. Exiting\n"));
1188     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1189     return;
1190   }
1191   if (GNUNET_YES != GNUNET_DISK_directory_test (args[1]))
1192   {
1193     fprintf (stderr, _("Specified policies directory does not exist. Exiting.\n"));
1194     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1195     return;
1196   }
1197   policy_dir = args[1];
1198   if (GNUNET_YES != GNUNET_DISK_file_test (args[2]))
1199   {
1200     fprintf (stderr, _("No search strings file given. Exiting.\n"));
1201     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1202     return;
1203   }
1204   num_search_strings = load_search_strings (args[2], &search_strings);
1205   if (0 >= num_search_strings || NULL == search_strings)
1206   {
1207     fprintf (stderr, _("Error loading search strings. Exiting.\n"));
1208     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1209     return;
1210   }
1211   unsigned int i;
1212   for (i = 0; i < num_search_strings; i++)
1213     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "search string: %s\n", search_strings[i]);
1214   cfg = GNUNET_CONFIGURATION_dup (config);
1215   mc_proc =
1216       GNUNET_TESTBED_controller_start (GNUNET_TESTBED_host_get_hostname
1217                                        (hosts[0]),
1218                                        hosts[0],
1219                                        cfg,
1220                                        status_cb,
1221                                        NULL);
1222   abort_task =
1223       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
1224                                     (GNUNET_TIME_UNIT_SECONDS, 5), &do_abort,
1225                                     NULL);
1226 }
1227
1228
1229 /**
1230  * Main function.
1231  *
1232  * @param argc argument count
1233  * @param argv argument values
1234  * @return 0 on success
1235  */
1236 int
1237 main (int argc, char *const *argv)
1238 {
1239   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1240     { 'n', "num-links", "COUNT",
1241       gettext_noop ("create COUNT number of random links"),
1242       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_links },
1243     { 'e', "num-errors", "COUNT",
1244       gettext_noop ("tolerate COUNT number of continious timeout failures"),
1245       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_cont_fails },
1246     { 't', "matching-timeout", "TIMEOUT",
1247       gettext_noop ("wait TIMEOUT seconds before considering a string match as failed"),
1248       GNUNET_YES, &GNUNET_GETOPT_set_uint, &search_timeout_sec },
1249     { 's', "search-wait", "WAIT",
1250       gettext_noop ("wait WAIT minutes before starting string search"),
1251       GNUNET_YES, &GNUNET_GETOPT_set_uint, &search_wait_min },
1252     GNUNET_GETOPT_OPTION_END
1253   };
1254   int ret;
1255
1256   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1257     return 2;
1258
1259   result = GNUNET_SYSERR;
1260   ret =
1261       GNUNET_PROGRAM_run (argc, argv, "gnunet-regex-profiler [OPTIONS] hosts-file policy-dir search-strings-file",
1262                           _("Profiler for regex/mesh"),
1263                           options, &run, NULL);
1264   if (GNUNET_OK != ret)
1265     return ret;
1266   if (GNUNET_OK != result)
1267     return 1;
1268   return 0;
1269 }