regex profiler fixes
[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
469     if (GNUNET_SCHEDULER_NO_TASK != search_timeout_task)
470       GNUNET_SCHEDULER_cancel (search_timeout_task);
471     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
472   }
473 }
474
475
476 /**
477  * Connect by string timeout task
478  *
479  * @param cls NULL
480  * @param tc the task context
481  */
482 static void
483 do_connect_by_string_timeout (void *cls,
484                               const struct GNUNET_SCHEDULER_TaskContext * tc)
485 {
486   long sec = (long)cls;
487
488   printf ("Searching for all strings did not succeed after %ld seconds\n", sec);
489   printf ("Found %i of %i strings\n", peers_found, num_search_strings);
490
491   GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
492 }
493
494
495 /**
496  * Connect by string task that is run to search for a string in the NFA
497  *
498  * @param cls NULL
499  * @param tc the task context
500  */
501 static void
502 do_connect_by_string (void *cls,
503                       const struct GNUNET_SCHEDULER_TaskContext * tc)
504 {
505   unsigned int search_cnt;
506   struct Peer *peer;
507
508   for (search_cnt = 0; search_cnt < num_search_strings; search_cnt++)
509   {
510     peer = &peers[search_cnt % num_peers];
511
512     printf ("Searching for string \"%s\" on peer %d with file %s\n", 
513             search_strings[search_cnt], (search_cnt % num_peers), peer->policy_file);
514
515     peer->mesh_tunnel_handle = GNUNET_MESH_tunnel_create (peer->mesh_handle,
516                                                           NULL,
517                                                           &mesh_peer_connect_handler,
518                                                           &mesh_peer_disconnect_handler,
519                                                           search_strings[search_cnt]);
520     GNUNET_MESH_peer_request_connect_by_string (peer->mesh_tunnel_handle,
521                                                 search_strings[search_cnt]);
522       
523   }
524
525   prof_start_time = GNUNET_TIME_absolute_get ();
526
527   search_timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
528                                                       (GNUNET_TIME_UNIT_SECONDS, search_timeout_sec),
529                                                       &do_connect_by_string_timeout, (void *)search_timeout_sec);
530 }
531
532
533 /**
534  * Mesh connect callback.
535  *
536  * @param cls internal peer id.
537  * @param op operation handle.
538  * @param ca_result connect adapter result.
539  * @param emsg error message.
540  */
541 void
542 mesh_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
543                  void *ca_result, const char *emsg)
544 {
545   static unsigned int connected_mesh_handles;
546   struct Peer *peer = (struct Peer *) cls;
547   char *regex;
548   char *data;
549   char *buf;
550   uint64_t filesize;
551   unsigned int offset;
552
553   if (NULL != emsg)
554   {
555     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Mesh connect failed: %s\n", emsg);
556     GNUNET_assert (0);
557   }
558
559   GNUNET_assert (peer->op_handle == op);
560   GNUNET_assert (peer->mesh_handle == ca_result);
561   GNUNET_assert (NULL != peer->policy_file);
562
563   printf ("Announcing regexes for peer with file %s\n", peer->policy_file);
564   fflush (stdout);
565
566   if (GNUNET_YES != GNUNET_DISK_file_test (peer->policy_file))
567   {
568     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
569                 "Could not find policy file %s\n", peer->policy_file);
570     return;
571   }
572   if (GNUNET_OK != GNUNET_DISK_file_size (peer->policy_file, &filesize, GNUNET_YES, GNUNET_YES))
573     filesize = 0;
574   if (0 == filesize)
575   {
576     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Policy file %s is empty.\n", peer->policy_file);
577     return;
578   }
579   data = GNUNET_malloc (filesize);
580   if (filesize != GNUNET_DISK_fn_read (peer->policy_file, data, filesize))
581   {
582     GNUNET_free (data);
583     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read policy file %s.\n",
584          peer->policy_file);
585     return;
586   }
587   buf = data;
588   offset = 0;
589   regex = NULL;
590   while (offset < (filesize - 1))
591   {
592     offset++;
593     if (((data[offset] == '\n')) && (buf != &data[offset]))
594     {
595       data[offset] = '\0';
596       regex = buf;
597       GNUNET_assert (NULL != regex);
598       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Announcing regex: %s on peer\n", regex);
599       GNUNET_MESH_announce_regex (peer->mesh_handle, regex);
600       buf = &data[offset + 1];
601     }
602     else if ((data[offset] == '\n') || (data[offset] == '\0'))
603       buf = &data[offset + 1];
604   }
605   GNUNET_free (data);
606
607   if (++connected_mesh_handles == num_peers)
608   {
609     printf ("\nAll mesh handles connected.\nWaiting %ld minutes before starting to search.\n", 
610             search_wait_min);
611
612     search_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
613                                                 (GNUNET_TIME_UNIT_MINUTES, search_wait_min),
614                                                 &do_connect_by_string, NULL);
615   }
616 }
617
618
619 /**
620  * Mesh connect adapter.
621  *
622  * @param cls not used.
623  * @param cfg configuration handle.
624  *
625  * @return
626  */
627 void *
628 mesh_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
629 {
630   GNUNET_MESH_ApplicationType app;
631   struct Peer *peer = (struct Peer *) cls;
632
633   static struct GNUNET_MESH_MessageHandler handlers[] = {
634     {NULL, 0, 0}
635   };
636
637   app = (GNUNET_MESH_ApplicationType)0;
638
639   peer->mesh_handle =
640     GNUNET_MESH_connect (cfg, cls, NULL, NULL, handlers, &app);
641
642   return peer->mesh_handle;
643 }
644
645
646 /**
647  * Adapter function called to destroy a connection to
648  * the mesh service
649  *
650  * @param cls closure
651  * @param op_result service handle returned from the connect adapter
652  */
653 void
654 mesh_da (void *cls, void *op_result)
655 {
656   struct Peer *peer = (struct Peer *) cls;
657
658   GNUNET_assert (peer->mesh_handle == op_result);
659
660   if (NULL != peer->mesh_tunnel_handle)
661   {
662     GNUNET_MESH_tunnel_destroy (peer->mesh_tunnel_handle);
663     peer->mesh_tunnel_handle = NULL;
664   }
665
666   if (NULL != peer->mesh_handle)
667   {
668     GNUNET_MESH_disconnect (peer->mesh_handle);
669     peer->mesh_handle = NULL;
670   }
671 }
672
673
674 /**
675  * Functions of this signature are called when a peer has been successfully
676  * started or stopped.
677  *
678  * @param cls the closure from GNUNET_TESTBED_peer_start/stop()
679  * @param emsg NULL on success; otherwise an error description
680  */
681 static void
682 peer_churn_cb (void *cls, const char *emsg)
683 {
684   struct DLLOperation *dll_op = cls;
685   struct GNUNET_TESTBED_Operation *op;
686   static unsigned int started_peers;
687   unsigned int peer_cnt;
688
689   op = dll_op->op;
690   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
691   GNUNET_free (dll_op);
692   if (NULL != emsg)
693   {
694     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
695          _("An operation has failed while starting peers\n"));
696     GNUNET_TESTBED_operation_done (op);
697     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
698       GNUNET_SCHEDULER_cancel (abort_task);
699     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
700     return;
701   }
702   GNUNET_TESTBED_operation_done (op);
703   if (++started_peers == num_peers)
704   {
705     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
706     printf ("All peers started successfully in %.2f seconds\n",
707             ((double) prof_time.rel_value) / 1000.00);
708     result = GNUNET_OK;
709
710     if (0 == num_links)
711       num_links = num_peers * 5;
712
713     peer_handles = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *) * num_peers);
714     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
715       peer_handles[peer_cnt] = peers[peer_cnt].peer_handle;
716
717     state = STATE_PEERS_LINKING;
718     /* Do overlay connect */
719     prof_start_time = GNUNET_TIME_absolute_get ();
720     topology_op =
721         GNUNET_TESTBED_overlay_configure_topology (NULL, num_peers, peer_handles,
722                                                    GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI,
723                                                    num_links,
724                                                    GNUNET_TESTBED_TOPOLOGY_OPTION_END);
725     if (NULL == topology_op)
726     {
727       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
728                   "Cannot create topology, op handle was NULL\n");
729       GNUNET_assert (0);
730     }
731   }
732 }
733
734
735 /**
736  * Functions of this signature are called when a peer has been successfully
737  * created
738  *
739  * @param cls the closure from GNUNET_TESTBED_peer_create()
740  * @param peer the handle for the created peer; NULL on any error during
741  *          creation
742  * @param emsg NULL if peer is not NULL; else MAY contain the error description
743  */
744 static void
745 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
746 {
747   struct DLLOperation *dll_op = cls;
748   struct Peer *peer_ptr;
749   static unsigned int created_peers;
750   unsigned int peer_cnt;
751
752   if (NULL != emsg)
753   {
754     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
755          _("Creating a peer failed. Error: %s\n"), emsg);
756     GNUNET_TESTBED_operation_done (dll_op->op);
757     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
758     GNUNET_free (dll_op);
759     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
760       GNUNET_SCHEDULER_cancel (abort_task);
761     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
762     return;
763   }
764
765   peer_ptr = dll_op->cls;
766   GNUNET_assert (NULL == peer_ptr->peer_handle);
767   peer_ptr->peer_handle = peer;
768   GNUNET_TESTBED_operation_done (dll_op->op);
769   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
770   GNUNET_free (dll_op);
771
772   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %i created on host %s\n",
773               created_peers,
774               GNUNET_TESTBED_host_get_hostname (peer_ptr->host_handle));
775
776   if (++created_peers == num_peers)
777   {
778     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
779     printf ("All peers created successfully in %.2f seconds\n",
780             ((double) prof_time.rel_value) / 1000.00);
781     /* Now peers are to be started */
782     state = STATE_PEERS_STARTING;
783     prof_start_time = GNUNET_TIME_absolute_get ();
784     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
785     {
786       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
787       dll_op->op = GNUNET_TESTBED_peer_start (dll_op, peers[peer_cnt].peer_handle,
788                                               &peer_churn_cb, dll_op);
789       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
790     }
791   }
792 }
793
794 /**
795  * Function called with a filename.
796  *
797  * @param cls closure
798  * @param filename complete filename (absolute path)
799  * @return GNUNET_OK to continue to iterate,
800  *  GNUNET_SYSERR to abort iteration with error!
801  */
802 int
803 policy_filename_cb (void *cls, const char *filename)
804 {
805   static unsigned int peer_cnt;
806   struct DLLOperation *dll_op;
807
808   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating peer %i on host %s for policy file %s\n",
809               peer_cnt,
810               GNUNET_TESTBED_host_get_hostname (hosts[peer_cnt % num_hosts]),
811               filename);
812
813   peers[peer_cnt].policy_file = GNUNET_strdup (filename);
814   peers[peer_cnt].host_handle = hosts[peer_cnt % num_hosts];
815   peers[peer_cnt].mesh_handle = NULL;
816   peers[peer_cnt].mesh_tunnel_handle = NULL;
817
818   dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
819   dll_op->cls = &peers[peer_cnt];
820   dll_op->op = GNUNET_TESTBED_peer_create (mc,
821                                            hosts[peer_cnt % num_hosts],
822                                            cfg,
823                                            &peer_create_cb,
824                                            dll_op);
825   GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
826   peer_cnt++;
827
828   return GNUNET_OK;
829 }
830
831
832 /**
833  * Controller event callback
834  *
835  * @param cls NULL
836  * @param event the controller event
837  */
838 static void
839 controller_event_cb (void *cls,
840                      const struct GNUNET_TESTBED_EventInformation *event)
841 {
842   struct DLLOperation *dll_op;
843   struct GNUNET_TESTBED_Operation *op;
844
845   switch (state)
846   {
847   case STATE_SLAVES_STARTING:
848     switch (event->type)
849     {
850     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
851       {
852         static unsigned int slaves_started;
853
854         dll_op = event->details.operation_finished.op_cls;
855         GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
856         GNUNET_free (dll_op);
857         op = event->details.operation_finished.operation;
858         if (NULL != event->details.operation_finished.emsg)
859         {
860           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
861                _("An operation has failed while starting slaves\n"));
862           GNUNET_TESTBED_operation_done (op);
863           if (GNUNET_SCHEDULER_NO_TASK != abort_task)
864             GNUNET_SCHEDULER_cancel (abort_task);
865           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
866           return;
867         }
868         GNUNET_TESTBED_operation_done (op);
869         /* Proceed to start peers */
870         if (++slaves_started == num_hosts - 1)
871         {
872           printf ("All slaves started successfully\n");
873
874           state = STATE_PEERS_CREATING;
875           prof_start_time = GNUNET_TIME_absolute_get ();
876
877           num_peers = GNUNET_DISK_directory_scan (policy_dir,
878                                                   NULL,
879                                                   NULL);
880           peers = GNUNET_malloc (sizeof (struct Peer) * num_peers);
881
882           GNUNET_DISK_directory_scan (policy_dir,
883                                       &policy_filename_cb,
884                                       NULL);
885         }
886       }
887       break;
888     default:
889       GNUNET_assert (0);
890     }
891     break;
892   case STATE_PEERS_STARTING:
893     switch (event->type)
894     {
895     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
896       /* Control reaches here when peer start fails */
897     case GNUNET_TESTBED_ET_PEER_START:
898       /* we handle peer starts in peer_churn_cb */
899       break;
900     default:
901       GNUNET_assert (0);
902     }
903     break;
904   case STATE_PEERS_LINKING:
905    switch (event->type)
906     {
907     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
908       /* Control reaches here when a peer linking operation fails */
909       if (NULL != event->details.operation_finished.emsg)
910       {
911         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
912              _("An operation has failed while linking\n"));
913         retry_links++;
914         if (++cont_fails > num_cont_fails)
915         {
916           printf ("\nAborting due to very high failure rate");
917           if (GNUNET_SCHEDULER_NO_TASK != abort_task)
918             GNUNET_SCHEDULER_cancel (abort_task);
919           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
920         }
921       }
922       break;
923     case GNUNET_TESTBED_ET_CONNECT:
924       {
925         static unsigned int established_links;
926         unsigned int peer_cnt;
927
928         if (0 == established_links)
929           printf ("Establishing links\n .");
930         else
931         {
932           printf (".");
933           fflush (stdout);
934         }
935         if (++established_links == num_links)
936         {
937           prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
938           printf ("\n%u links established in %.2f seconds\n",
939                   num_links, ((double) prof_time.rel_value) / 1000.00);
940           result = GNUNET_OK;
941           GNUNET_free (peer_handles);
942           printf ("\nConnecting to mesh service...\n");
943           for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
944           {
945             peers[peer_cnt].op_handle = GNUNET_TESTBED_service_connect (NULL,
946                                                                         peers[peer_cnt].peer_handle,
947                                                                         "mesh",
948                                                                         &mesh_connect_cb,
949                                                                         &peers[peer_cnt],
950                                                                         &mesh_ca,
951                                                                         &mesh_da,
952                                                                         &peers[peer_cnt]);
953           }
954         }
955       }
956       break;
957     default:
958       GNUNET_assert (0);
959     }
960     break;
961   default:
962     GNUNET_assert (0);
963   }
964 }
965
966
967 /**
968  * Task to register all hosts available in the global host list
969  *
970  * @param cls NULL
971  * @param tc the scheduler task context
972  */
973 static void
974 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
975
976
977 /**
978  * Callback which will be called to after a host registration succeeded or failed
979  *
980  * @param cls the closure
981  * @param emsg the error message; NULL if host registration is successful
982  */
983 static void
984 host_registration_completion (void *cls, const char *emsg)
985 {
986   reg_handle = NULL;
987   if (NULL != emsg)
988   {
989     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
990                 _("Host registration failed for a host. Error: %s\n"), emsg);
991     if (GNUNET_SCHEDULER_NO_TASK != abort_task)
992       GNUNET_SCHEDULER_cancel (abort_task);
993     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
994     return;
995   }
996   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
997 }
998
999
1000 /**
1001  * Task to register all hosts available in the global host list
1002  *
1003  * @param cls NULL
1004  * @param tc the scheduler task context
1005  */
1006 static void
1007 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1008 {
1009   struct DLLOperation *dll_op;
1010   static unsigned int reg_host;
1011   unsigned int slave;
1012
1013   register_hosts_task = GNUNET_SCHEDULER_NO_TASK;
1014   if (reg_host == num_hosts - 1)
1015   {
1016     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1017                 "All hosts successfully registered\n");
1018     /* Start slaves */
1019     state = STATE_SLAVES_STARTING;
1020     for (slave = 1; slave < num_hosts; slave++)
1021     {
1022       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
1023       dll_op->op = GNUNET_TESTBED_controller_link (dll_op,
1024                                                    mc,
1025                                                    hosts[slave],
1026                                                    hosts[0],
1027                                                    cfg,
1028                                                    GNUNET_YES);
1029       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
1030     }
1031     return;
1032   }
1033   reg_handle = GNUNET_TESTBED_register_host (mc, hosts[++reg_host],
1034                                              host_registration_completion,
1035                                              NULL);
1036 }
1037
1038
1039 /**
1040  * Callback to signal successfull startup of the controller process
1041  *
1042  * @param cls the closure from GNUNET_TESTBED_controller_start()
1043  * @param config the configuration with which the controller has been started;
1044  *          NULL if status is not GNUNET_OK
1045  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
1046  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
1047  */
1048 static void
1049 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
1050 {
1051   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
1052     GNUNET_SCHEDULER_cancel (abort_task);
1053   if (GNUNET_OK != status)
1054   {
1055     mc_proc = NULL;
1056     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1057     return;
1058   }
1059   event_mask = 0;
1060   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
1061   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
1062   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1063   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
1064   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
1065   mc = GNUNET_TESTBED_controller_connect (config, hosts[0], event_mask,
1066                                           &controller_event_cb, NULL);
1067   if (NULL == mc)
1068   {
1069     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1070                 _("Unable to connect to master controller -- Check config\n"));
1071     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
1072     return;
1073   }
1074   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
1075   abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1076                                              &do_abort, NULL);
1077 }
1078
1079 /**
1080  * Load search strings from given filename. One search string per line.
1081  *
1082  * @param filename filename of the file containing the search strings.
1083  * @param strings set of strings loaded from file. Caller needs to free this 
1084  *                if number returned is greater than zero.
1085  * @return number of strings found in the file. GNUNET_SYSERR on error.
1086  */
1087 static int
1088 load_search_strings (const char *filename, char ***strings)
1089 {
1090   char *data;
1091   char *buf;
1092   uint64_t filesize;
1093   unsigned int offset;
1094   int str_cnt;
1095   unsigned int i;
1096
1097   if (NULL == filename)
1098   {
1099     return GNUNET_SYSERR;
1100   }
1101
1102   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
1103   {
1104     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1105                 "Could not find search strings file %s\n", filename);
1106     return GNUNET_SYSERR;
1107   }
1108   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &filesize, GNUNET_YES, GNUNET_YES))
1109     filesize = 0;
1110   if (0 == filesize)
1111   {
1112     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Search strings file %s is empty.\n", filename);
1113     return GNUNET_SYSERR;
1114   }
1115   data = GNUNET_malloc (filesize);
1116   if (filesize != GNUNET_DISK_fn_read (filename, data, filesize))
1117   {
1118     GNUNET_free (data);
1119     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not read search strings file %s.\n",
1120          filename);
1121     return GNUNET_SYSERR;
1122   }
1123   buf = data;
1124   offset = 0;
1125   str_cnt = 0;
1126   while (offset < (filesize - 1))
1127   {
1128     offset++;
1129     if (((data[offset] == '\n')) && (buf != &data[offset]))
1130     {
1131       data[offset] = '\0';
1132       str_cnt++;
1133       buf = &data[offset + 1];
1134     }
1135     else if ((data[offset] == '\n') || (data[offset] == '\0'))
1136       buf = &data[offset + 1];
1137   }
1138   *strings = GNUNET_malloc (sizeof (char *) * str_cnt);
1139   offset = 0;
1140   for (i = 0; i < str_cnt; i++)
1141   {
1142     (*strings)[i] = GNUNET_strdup (&data[offset]);
1143     offset += strlen ((*strings)[i]) + 1;
1144   }
1145   free (data);
1146   return str_cnt;
1147 }
1148
1149 /**
1150  * Main function that will be run by the scheduler.
1151  *
1152  * @param cls closure
1153  * @param args remaining command-line arguments
1154  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1155  * @param config configuration
1156  */
1157 static void
1158 run (void *cls, char *const *args, const char *cfgfile,
1159      const struct GNUNET_CONFIGURATION_Handle *config)
1160 {
1161   unsigned int nhost;
1162
1163   if (NULL == args[0])
1164   {
1165     fprintf (stderr, _("No hosts-file specified on command line. Exiting.\n"));
1166     return;
1167   }
1168   if (NULL == args[1])
1169   {
1170     fprintf (stderr, _("No policy directory specified on command line. Exiting.\n"));
1171     return;
1172   }
1173   num_hosts = GNUNET_TESTBED_hosts_load_from_file (args[0], &hosts);
1174   if (0 == num_hosts)
1175   {
1176     fprintf (stderr, _("No hosts loaded. Need at least one host\n"));
1177     return;
1178   }
1179   for (nhost = 0; nhost < num_hosts; nhost++)
1180   {
1181     if (GNUNET_YES != GNUNET_TESTBED_is_host_habitable (hosts[nhost]))
1182     {
1183       fprintf (stderr, _("Host %s cannot start testbed\n"),
1184                          GNUNET_TESTBED_host_get_hostname (hosts[nhost]));
1185       break;
1186     }
1187   }
1188   if (num_hosts != nhost)
1189   {
1190     fprintf (stderr, _("Exiting\n"));
1191     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1192     return;
1193   }
1194   if (NULL == config)
1195   {
1196     fprintf (stderr, _("No configuration file given. Exiting\n"));
1197     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1198     return;
1199   }
1200   if (GNUNET_YES != GNUNET_DISK_directory_test (args[1]))
1201   {
1202     fprintf (stderr, _("Specified policies directory does not exist. Exiting.\n"));
1203     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1204     return;
1205   }
1206   policy_dir = args[1];
1207   if (GNUNET_YES != GNUNET_DISK_file_test (args[2]))
1208   {
1209     fprintf (stderr, _("No search strings file given. Exiting.\n"));
1210     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1211     return;
1212   }
1213   num_search_strings = load_search_strings (args[2], &search_strings);
1214   if (0 >= num_search_strings || NULL == search_strings)
1215   {
1216     fprintf (stderr, _("Error loading search strings. Exiting.\n"));
1217     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
1218     return;
1219   }
1220   unsigned int i;
1221   for (i = 0; i < num_search_strings; i++)
1222     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "search string: %s\n", search_strings[i]);
1223   cfg = GNUNET_CONFIGURATION_dup (config);
1224   mc_proc =
1225       GNUNET_TESTBED_controller_start (GNUNET_TESTBED_host_get_hostname
1226                                        (hosts[0]),
1227                                        hosts[0],
1228                                        cfg,
1229                                        status_cb,
1230                                        NULL);
1231   abort_task =
1232       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
1233                                     (GNUNET_TIME_UNIT_SECONDS, 5), &do_abort,
1234                                     NULL);
1235 }
1236
1237
1238 /**
1239  * Main function.
1240  *
1241  * @param argc argument count
1242  * @param argv argument values
1243  * @return 0 on success
1244  */
1245 int
1246 main (int argc, char *const *argv)
1247 {
1248   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1249     { 'n', "num-links", "COUNT",
1250       gettext_noop ("create COUNT number of random links"),
1251       GNUNET_NO, &GNUNET_GETOPT_set_uint, &num_links },
1252     { 'e', "num-errors", "COUNT",
1253       gettext_noop ("tolerate COUNT number of continious timeout failures"),
1254       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_cont_fails },
1255     { 't', "matching-timeout", "TIMEOUT",
1256       gettext_noop ("wait TIMEOUT seconds before considering a string match as failed"),
1257       GNUNET_YES, &GNUNET_GETOPT_set_uint, &search_timeout_sec },
1258     { 's', "search-wait", "WAIT",
1259       gettext_noop ("wait WAIT minutes before starting string search"),
1260       GNUNET_YES, &GNUNET_GETOPT_set_uint, &search_wait_min },
1261     GNUNET_GETOPT_OPTION_END
1262   };
1263   int ret;
1264
1265   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1266     return 2;
1267
1268   result = GNUNET_SYSERR;
1269   ret =
1270       GNUNET_PROGRAM_run (argc, argv, "gnunet-regex-profiler [OPTIONS] hosts-file policy-dir search-strings-file",
1271                           _("Profiler for regex/mesh"),
1272                           options, &run, NULL);
1273   if (GNUNET_OK != ret)
1274     return ret;
1275   if (GNUNET_OK != result)
1276     return 1;
1277   return 0;
1278 }