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