sensor profiler fix
[oweals/gnunet.git] / src / sensor / gnunet-sensor-profiler.c
1 /*
2      This file is part of GNUnet.
3      (C)
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 sensor/gnunet-sensor-profiler.c
23  * @brief Profiler for the sensor service
24  * @author Omar Tarabai
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_testbed_service.h"
29 #include "gnunet_peerstore_service.h"
30 #include "gnunet_sensor_service.h"
31 #include "gnunet_sensor_util_lib.h"
32 #include "gnunet_transport_service.h"
33
34 /**
35  * Time to wait for the peer to startup completely
36  */
37 #define PEER_STARTUP_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
38
39 /**
40  * Information about a single peer
41  */
42 struct PeerInfo
43 {
44
45   /**
46    * Peer Identity
47    */
48   struct GNUNET_PeerIdentity peer_id;
49
50   /**
51    * Testbed peer handle
52    */
53   struct GNUNET_TESTBED_Peer *testbed_peer;
54
55 };
56
57 struct DisconnectionContext
58 {
59
60   struct DisconnectionContext *prev;
61
62   struct DisconnectionContext *next;
63
64   struct PeerInfo *p1;
65
66   struct PeerInfo *p2;
67
68   struct GNUNET_TESTBED_Operation *p1_transport_op;
69
70   struct GNUNET_TRANSPORT_Blacklist *blacklist;
71
72 };
73
74
75 /**
76  * Name of the configuration file used
77  */
78 static const char *cfg_filename = "gnunet-sensor-profiler.conf";
79
80 /**
81  * Directory to read sensor definitions from
82  */
83 static const char *sensor_src_dir = "sensors";
84
85 /**
86  * Directory to write new sensor definitions to
87  */
88 static const char *sensor_dst_dir = "/tmp/gnunet-sensor-profiler";
89
90 /**
91  * Scheduled task to shutdown
92  */
93 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task = GNUNET_SCHEDULER_NO_TASK;
94
95 /**
96  * GNUnet configuration
97  */
98 static struct GNUNET_CONFIGURATION_Handle *cfg;
99
100 /**
101  * Number of peers to run (Option -p)
102  */
103 static unsigned int num_peers = 0;
104
105 /**
106  * Set sensors running interval to this value (Option -i)
107  */
108 static unsigned int sensors_interval = 0;
109
110 /**
111  * Path to topology file (Option -t)
112  */
113 static char *topology_file;
114
115 /**
116  * Array of peer info for all peers
117  */
118 static struct PeerInfo *all_peers_info;
119
120 /**
121  * Number of peers that we already collected and start their info
122  */
123 static int peers_known = 0;
124
125 /**
126  * TESTBED operation connecting us to peerstore service on collection point
127  */
128 static struct GNUNET_TESTBED_Operation *peerstore_op;
129
130 /**
131  * Handle to peerstore service on collection point
132  */
133 static struct GNUNET_PEERSTORE_Handle *peerstore;
134
135 /**
136  * Dashboard service on collection point started?
137  */
138 static int dashboard_service_started = GNUNET_NO;
139
140 /**
141  * Number of peers started the sensor service successfully
142  */
143 static int sensor_services_started = 0;
144
145 /**
146  * Array of sensor names to be used for watching peerstore records
147  */
148 static char **sensor_names;
149
150 /**
151  * Size of 'sensor_names' array
152  */
153 static unsigned int sensor_names_size = 0;
154
155 /**
156  * Task run after any waiting period
157  */
158 static GNUNET_SCHEDULER_TaskIdentifier delayed_task = GNUNET_SCHEDULER_NO_TASK;
159
160 /**
161  * Head of list of disconnection contexts
162  */
163 static struct DisconnectionContext *dc_head;
164
165 /*
166  * Tail of list of disconnection contexts
167  */
168 static struct DisconnectionContext *dc_tail;
169
170
171 /**
172  * Copy directory recursively
173  *
174  * @param src Path to source directory
175  * @param dst Destination directory, will be created if it does not exist
176  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
177  */
178 static int
179 copy_dir (const char *src, const char *dst);
180
181
182 /**
183  * Prompt the user to disconnect two peers
184  */
185 static void
186 prompt_peer_disconnection ();
187
188
189 /**
190  * Do clean up and shutdown scheduler
191  */
192 static void
193 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
194 {
195   int i;
196   struct DisconnectionContext *dc;
197
198   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down.\n");
199   if (GNUNET_SCHEDULER_NO_TASK != delayed_task)
200   {
201     GNUNET_SCHEDULER_cancel (delayed_task);
202     delayed_task = GNUNET_SCHEDULER_NO_TASK;
203   }
204   dc = dc_head;
205   while (NULL != dc)
206   {
207     GNUNET_CONTAINER_DLL_remove (dc_head, dc_tail, dc);
208     if (NULL != dc->blacklist)
209     {
210       GNUNET_TRANSPORT_blacklist_cancel (dc->blacklist);
211       dc->blacklist = NULL;
212     }
213     if (NULL != dc->p1_transport_op)
214     {
215       GNUNET_TESTBED_operation_done (dc->p1_transport_op);
216       dc->p1_transport_op = NULL;
217     }
218     GNUNET_free (dc);
219     dc = dc_head;
220   }
221   if (NULL != peerstore_op)
222   {
223     GNUNET_TESTBED_operation_done (peerstore_op);
224     peerstore_op = NULL;
225   }
226   if (NULL != all_peers_info)
227   {
228     GNUNET_free (all_peers_info);
229     all_peers_info = NULL;
230   }
231   if (NULL != cfg)
232   {
233     GNUNET_CONFIGURATION_destroy (cfg);
234     cfg = NULL;
235   }
236   if (NULL != sensor_names)
237   {
238     for (i = 0; i < sensor_names_size; i++)
239       GNUNET_free (sensor_names[i]);
240     GNUNET_array_grow (sensor_names, sensor_names_size, 0);
241   }
242   GNUNET_SCHEDULER_shutdown ();
243 }
244
245
246 /*****************************************************************************/
247 /****************************** DISCONNECT PEERS *****************************/
248 /*****************************************************************************/
249
250
251 /**
252  * Function to call with result of the TRANSPORT try disconnect request.
253  *
254  * @param cls closure
255  * @param result #GNUNET_OK if message was transmitted to transport service
256  *               #GNUNET_SYSERR if message was not transmitted to transport service
257  */
258 static void
259 transport_disconnect_cb (void *cls, const int result)
260 {
261   struct DisconnectionContext *dc = cls;
262
263   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
264               "Disconnection request between `%s' and `%s' sent.\n",
265               GNUNET_i2s (&dc->p1->peer_id), GNUNET_i2s (&dc->p2->peer_id));
266 }
267
268
269 /**
270  * Callback to be called when transport service connect operation is completed
271  *
272  * @param cls the callback closure from functions generating an operation
273  * @param op the operation that has been finished
274  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
275  * @param emsg error message in case the operation has failed; will be NULL if
276  *          operation has executed successfully.
277  */
278 static void
279 transport_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
280                       void *ca_result, const char *emsg)
281 {
282   struct DisconnectionContext *dc = cls;
283   struct GNUNET_TRANSPORT_Handle *transport = ca_result;
284
285   if (NULL != emsg)
286   {
287     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
288     GNUNET_assert (0);
289   }
290   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "transport_connect_cb().\n");
291   GNUNET_TRANSPORT_try_disconnect (transport, &dc->p2->peer_id,
292                                    &transport_disconnect_cb, dc);
293 }
294
295
296 /**
297  * Callback from TRANSPORT service to ask if the given peer ID is blacklisted.
298  *
299  * @param cls closure, DisconnectionContext
300  * @param pid peer to approve or disapproave
301  * @return #GNUNET_OK if the connection is allowed, #GNUNET_SYSERR if not
302  */
303 static int
304 blacklist_cb (void *cls, const struct GNUNET_PeerIdentity *pid)
305 {
306   struct DisconnectionContext *dc = cls;
307
308   if (0 == GNUNET_CRYPTO_cmp_peer_identity (&dc->p2->peer_id, pid))
309     return GNUNET_SYSERR;
310   return GNUNET_OK;
311 }
312
313
314 /**
315  * Adapter function called to establish a connection to transport service.
316  *
317  * @param cls closure
318  * @param cfg configuration of the peer to connect to; will be available until
319  *          GNUNET_TESTBED_operation_done() is called on the operation returned
320  *          from GNUNET_TESTBED_service_connect()
321  * @return service handle to return in 'op_result', NULL on error
322  */
323 static void *
324 transport_connect_adapter (void *cls,
325                            const struct GNUNET_CONFIGURATION_Handle *cfg)
326 {
327   struct DisconnectionContext *dc = cls;
328   struct GNUNET_TRANSPORT_Handle *transport;
329
330   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "transport_connect_adapter().\n");
331   dc->blacklist = GNUNET_TRANSPORT_blacklist (cfg, &blacklist_cb, dc);
332   GNUNET_assert (NULL != dc->blacklist);
333   transport = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, NULL, NULL, NULL);
334   GNUNET_assert (NULL != transport);
335   return transport;
336 }
337
338
339 /**
340  * Adapter function called to destroy a connection to transport service.
341  *
342  * @param cls closure
343  * @param op_result service handle returned from the connect adapter
344  */
345 static void
346 transport_disconnect_adapter (void *cls, void *op_result)
347 {
348   struct GNUNET_TRANSPORT_Handle *transport = op_result;
349
350   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "transport_disconnect_adapter().\n");
351   GNUNET_TRANSPORT_disconnect (transport);
352 }
353
354
355 /**
356  * Kill any connection between two peers. Has no effect if the peers are not
357  * connected.
358  */
359 static void
360 disconnect_peers (struct PeerInfo *p1, struct PeerInfo *p2)
361 {
362   struct DisconnectionContext *dc;
363
364   GNUNET_assert (p1 != p2);
365   dc = GNUNET_new (struct DisconnectionContext);
366
367   dc->p1 = p1;
368   dc->p2 = p2;
369   GNUNET_CONTAINER_DLL_insert (dc_head, dc_tail, dc);
370   dc->p1_transport_op =
371       GNUNET_TESTBED_service_connect (NULL, p1->testbed_peer, "transport",
372                                       &transport_connect_cb, dc,
373                                       &transport_connect_adapter,
374                                       &transport_disconnect_adapter, dc);
375 }
376
377
378 /*****************************************************************************/
379 /**************************** END DISCONNECT PEERS ***************************/
380 /*****************************************************************************/
381
382
383 /**
384  * Function called with each file/folder inside a directory that is being copied.
385  *
386  * @param cls closure, destination directory
387  * @param filename complete filename (absolute path)
388  * @return #GNUNET_OK to continue to iterate.
389  *         #GNUNET_SYSERR to abort iteration with error
390  */
391 static int
392 copy_dir_scanner (void *cls, const char *filename)
393 {
394   char *dst_dir = cls;
395   char *dst;
396   int copy_result;
397
398   GNUNET_asprintf (&dst, "%s%s%s", dst_dir, DIR_SEPARATOR_STR,
399                    GNUNET_STRINGS_get_short_name (filename));
400   if (GNUNET_YES == GNUNET_DISK_directory_test (filename, GNUNET_YES))
401     copy_result = copy_dir (filename, dst);
402   else
403   {
404     if (GNUNET_YES == GNUNET_DISK_file_test (dst))
405       GNUNET_DISK_directory_remove (dst);
406     copy_result = GNUNET_DISK_file_copy (filename, dst);
407     if (GNUNET_OK == copy_result)
408       GNUNET_DISK_fix_permissions (dst, GNUNET_NO, GNUNET_NO);
409   }
410   GNUNET_free (dst);
411   return copy_result;
412 }
413
414
415 /**
416  * Copy directory recursively
417  *
418  * @param src Path to source directory
419  * @param dst Destination directory, will be created if it does not exist
420  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
421  */
422 static int
423 copy_dir (const char *src, const char *dst)
424 {
425   if (GNUNET_YES != GNUNET_DISK_directory_test (src, GNUNET_YES))
426     return GNUNET_SYSERR;
427   if (GNUNET_OK != GNUNET_DISK_directory_create (dst))
428     return GNUNET_SYSERR;
429   if (GNUNET_SYSERR ==
430       GNUNET_DISK_directory_scan (src, &copy_dir_scanner, (char *) dst))
431     return GNUNET_SYSERR;
432   return GNUNET_OK;
433 }
434
435
436 /**
437  * Function called with each file/folder inside source sensor directory.
438  *
439  * @param cls closure (unused)
440  * @param filename complete filename (absolute path)
441  * @return #GNUNET_OK to continue to iterate.
442  */
443 static int
444 sensor_dir_scanner (void *cls, const char *filename)
445 {
446   const char *file_basename;
447   char *dst_path;
448   struct GNUNET_CONFIGURATION_Handle *sensor_cfg;
449   char *sensor_name;
450
451   file_basename = GNUNET_STRINGS_get_short_name (filename);
452   GNUNET_asprintf (&dst_path, "%s%s%s", sensor_dst_dir, DIR_SEPARATOR_STR,
453                    file_basename);
454   if (GNUNET_YES == GNUNET_DISK_directory_test (filename, GNUNET_NO))
455   {
456     GNUNET_assert (GNUNET_OK == copy_dir (filename, dst_path));
457   }
458   else
459   {
460     sensor_name = GNUNET_strdup (file_basename);
461     GNUNET_array_append (sensor_names, sensor_names_size, sensor_name);
462     sensor_cfg = GNUNET_CONFIGURATION_create ();
463     GNUNET_assert (GNUNET_OK ==
464                    GNUNET_CONFIGURATION_parse (sensor_cfg, filename));
465     GNUNET_CONFIGURATION_set_value_string (sensor_cfg, file_basename,
466                                            "COLLECTION_POINT",
467                                            GNUNET_i2s_full (&all_peers_info
468                                                             [0].peer_id));
469     if (sensors_interval > 0)
470     {
471       GNUNET_CONFIGURATION_set_value_number (sensor_cfg, file_basename,
472                                              "INTERVAL",
473                                              (unsigned long long int)
474                                              sensors_interval);
475     }
476     GNUNET_CONFIGURATION_write (sensor_cfg, dst_path);
477     GNUNET_CONFIGURATION_destroy (sensor_cfg);
478   }
479   GNUNET_free (dst_path);
480   return GNUNET_OK;
481 }
482
483
484 /**
485  * Load sensor definitions and rewrite them to tmp location.
486  * Add collection point peer id and change running interval if needed.
487  */
488 static void
489 rewrite_sensors ()
490 {
491   GNUNET_assert (GNUNET_YES ==
492                  GNUNET_DISK_directory_test (sensor_src_dir, GNUNET_YES));
493   GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_create (sensor_dst_dir));
494   GNUNET_DISK_directory_scan (sensor_src_dir, &sensor_dir_scanner, NULL);
495 }
496
497
498 /**
499  * Callback to be called when dashboard service is started
500  *
501  * @param cls the callback closure from functions generating an operation
502  * @param op the operation that has been finished
503  * @param emsg error message in case the operation has failed; will be NULL if
504  *          operation has executed successfully.
505  */
506 static void
507 dashboard_started (void *cls, struct GNUNET_TESTBED_Operation *op,
508                    const char *emsg)
509 {
510   if (NULL != emsg)
511   {
512     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
513     GNUNET_assert (0);
514   }
515   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dashboard service started.\n");
516   GNUNET_TESTBED_operation_done (op);
517   dashboard_service_started = GNUNET_YES;
518 }
519
520
521 /**
522  * Function called by PEERSTORE for each matching record.
523  *
524  * @param cls closure
525  * @param record peerstore record information
526  * @param emsg error message, or NULL if no errors
527  * @return #GNUNET_YES to continue iterating, #GNUNET_NO to stop
528  */
529 static int
530 peerstore_watch_cb (void *cls, struct GNUNET_PEERSTORE_Record *record,
531                     char *emsg)
532 {
533   struct PeerInfo *peer = cls;
534   struct GNUNET_SENSOR_DashboardAnomalyEntry *anomaly;
535
536   if (NULL != emsg)
537   {
538     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
539     GNUNET_assert (0);
540   }
541   GNUNET_assert (record->value_size ==
542                  sizeof (struct GNUNET_SENSOR_DashboardAnomalyEntry));
543   anomaly = record->value;
544   GNUNET_assert (0 ==
545                  GNUNET_CRYPTO_cmp_peer_identity (&peer->peer_id,
546                                                   record->peer));
547   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
548               "Anomaly report:\n" "  Peer: `%s'\n" "  Sensor: `%s'\n"
549               "  Anomalous: `%d'\n" "  Anomalous neighbors: %f.\n\n",
550               GNUNET_i2s (&peer->peer_id), record->key, anomaly->anomalous,
551               anomaly->anomalous_neighbors);
552   return GNUNET_YES;
553 }
554
555
556 /**
557  * Callback to be called when peerstore service connect operation is completed
558  *
559  * @param cls the callback closure from functions generating an operation
560  * @param op the operation that has been finished
561  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
562  * @param emsg error message in case the operation has failed; will be NULL if
563  *          operation has executed successfully.
564  */
565 static void
566 peerstore_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
567                       void *ca_result, const char *emsg)
568 {
569   int i;
570   int j;
571   struct PeerInfo *peer;
572
573   if (NULL != emsg)
574   {
575     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
576     GNUNET_assert (0);
577   }
578   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected to peerstore service.\n");
579   /* Watch for anomaly reports from other peers */
580   for (i = 0; i < num_peers; i++)
581   {
582     peer = &all_peers_info[i];
583     for (j = 0; j < sensor_names_size; j++)
584     {
585       GNUNET_PEERSTORE_watch (peerstore, "sensordashboard-anomalies",
586                               &peer->peer_id, sensor_names[j],
587                               &peerstore_watch_cb, peer);
588     }
589   }
590 }
591
592
593 /**
594  * Adapter function called to establish a connection to peerstore service.
595  *
596  * @param cls closure
597  * @param cfg configuration of the peer to connect to; will be available until
598  *          GNUNET_TESTBED_operation_done() is called on the operation returned
599  *          from GNUNET_TESTBED_service_connect()
600  * @return service handle to return in 'op_result', NULL on error
601  */
602 static void *
603 peerstore_connect_adapter (void *cls,
604                            const struct GNUNET_CONFIGURATION_Handle *cfg)
605 {
606   peerstore = GNUNET_PEERSTORE_connect (cfg);
607   GNUNET_assert (NULL != peerstore);
608   return peerstore;
609 }
610
611
612 /**
613  * Adapter function called to destroy a connection to peerstore service.
614  *
615  * @param cls closure
616  * @param op_result service handle returned from the connect adapter
617  */
618 static void
619 peerstore_disconnect_adapter (void *cls, void *op_result)
620 {
621   GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_NO);
622   peerstore = NULL;
623   peerstore_op = NULL;
624 }
625
626
627 /**
628  * Prompt the user to disconnect two peers
629  */
630 static void
631 prompt_peer_disconnection ()
632 {
633   int p1;
634   int p2;
635   char line[10];
636
637   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
638               "Disconnect peers (e.g. '0,2') or empty line to execute:\n");
639   if (NULL == fgets (line, sizeof (line), stdin) || 1 == strlen (line))
640   {
641     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Continuing.\n");
642     return;
643   }
644   if (2 != sscanf (line, "%d,%d", &p1, &p2) || p1 >= num_peers ||
645       p2 >= num_peers || p1 < 0 || p2 < 0 || p1 == p2)
646   {
647     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Invalid input.\n");
648     prompt_peer_disconnection ();
649     return;
650   }
651   disconnect_peers (&all_peers_info[p1], &all_peers_info[p2]);
652   prompt_peer_disconnection ();
653 }
654
655
656 /**
657  * This function is called after the estimated training period is over.
658  */
659 static void
660 simulate_anomalies (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
661 {
662   delayed_task = GNUNET_SCHEDULER_NO_TASK;
663   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
664               "Training period over, simulating anomalies now.\n");
665   //TODO:
666 }
667
668
669 /**
670  * This function is called after a delay which ensures that all peers are
671  * properly initialized
672  */
673 static void
674 peers_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
675 {
676   unsigned long long int training_points;
677   struct GNUNET_TIME_Relative training_period;
678
679   delayed_task = GNUNET_SCHEDULER_NO_TASK;
680   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All peers are ready.\n");
681   GNUNET_assert (GNUNET_OK ==
682                  GNUNET_CONFIGURATION_get_value_number (cfg,
683                                                         "sensor-model-gaussian",
684                                                         "TRAINING_WINDOW",
685                                                         &training_points));
686   training_period =
687       GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_multiply
688                                      (GNUNET_TIME_UNIT_SECONDS,
689                                       (sensors_interval ==
690                                        0) ? 60 : sensors_interval),
691                                      training_points);
692   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
693               "Sleeping for a training period of %s.\n",
694               GNUNET_STRINGS_relative_time_to_string (training_period,
695                                                       GNUNET_NO));
696   delayed_task =
697       GNUNET_SCHEDULER_add_delayed (training_period, &simulate_anomalies, NULL);
698   prompt_peer_disconnection (); //TODO: move to simulate_anomalies()
699 }
700
701
702 /**
703  * Callback to be called when sensor service is started
704  *
705  * @param cls the callback closure from functions generating an operation
706  * @param op the operation that has been finished
707  * @param emsg error message in case the operation has failed; will be NULL if
708  *          operation has executed successfully.
709  */
710 static void
711 sensor_service_started (void *cls, struct GNUNET_TESTBED_Operation *op,
712                         const char *emsg)
713 {
714   struct PeerInfo *peer = cls;
715
716   if (NULL != emsg)
717   {
718     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
719     GNUNET_assert (0);
720   }
721   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sensor service started on peer `%s'.\n",
722               GNUNET_i2s (&peer->peer_id));
723   GNUNET_TESTBED_operation_done (op);
724   sensor_services_started++;
725   if (sensor_services_started == num_peers)
726   {
727     delayed_task =
728         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
729                                       (PEER_STARTUP_TIME, num_peers),
730                                       &peers_ready, NULL);
731   }
732 }
733
734
735 /**
736  * Callback to be called when the requested peer information is available
737  *
738  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
739  * @param op the operation this callback corresponds to
740  * @param pinfo the result; will be NULL if the operation has failed
741  * @param emsg error message if the operation has failed; will be NULL if the
742  *          operation is successfull
743  */
744 static void
745 peer_info_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op,
746               const struct GNUNET_TESTBED_PeerInformation *pinfo,
747               const char *emsg)
748 {
749   struct GNUNET_TESTBED_Peer *testbed_peer = cb_cls;
750   struct PeerInfo *peer = &all_peers_info[peers_known];
751
752   if (NULL != emsg)
753   {
754     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
755     GNUNET_assert (0);
756   }
757   peer->testbed_peer = testbed_peer;
758   GNUNET_CRYPTO_get_peer_identity (pinfo->result.cfg, &peer->peer_id);
759   peers_known++;
760   if (1 == peers_known)         /* First peer is collection point */
761   {
762     /* Rewrite sensors */
763     rewrite_sensors ();
764     /* Start dashboard */
765     GNUNET_TESTBED_peer_manage_service (NULL, testbed_peer, "sensordashboard",
766                                         &dashboard_started, NULL, 1);
767   }
768   /* Start sensor service on every peer */
769   GNUNET_TESTBED_peer_manage_service (NULL, testbed_peer, "sensor",
770                                       &sensor_service_started, peer, 1);
771   if (num_peers == peers_known) /* Last peer */
772   {
773     /* Connect to peerstore on first peer (collection point) */
774     peerstore_op =
775         GNUNET_TESTBED_service_connect (NULL, all_peers_info[0].testbed_peer,
776                                         "peerstore", &peerstore_connect_cb,
777                                         NULL, &peerstore_connect_adapter,
778                                         &peerstore_disconnect_adapter, NULL);
779   }
780   GNUNET_TESTBED_operation_done (op);
781 }
782
783
784 /**
785  * Signature of a main function for a testcase.
786  *
787  * @param cls closure
788  * @param h the run handle
789  * @param num number of peers in 'peers'
790  * @param peers handle to peers run in the testbed.  NULL upon timeout (see
791  *          GNUNET_TESTBED_test_run()).
792  * @param links_succeeded the number of overlay link connection attempts that
793  *          succeeded
794  * @param links_failed the number of overlay link connection attempts that
795  *          failed
796  * @see GNUNET_TESTBED_test_run()
797  */
798 static void
799 test_master (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num,
800              struct GNUNET_TESTBED_Peer **peers, unsigned int links_succeeded,
801              unsigned int links_failed)
802 {
803   int i;
804
805   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
806               "%d peers started. %d links succeeded. %d links failed.\n",
807               num_peers, links_succeeded, links_failed);
808   GNUNET_assert (num == num_peers);
809   GNUNET_assert (0 == links_failed);
810   /* Collect peer information */
811   all_peers_info = GNUNET_new_array (num_peers, struct PeerInfo);
812
813   for (i = 0; i < num_peers; i++)
814   {
815     GNUNET_TESTBED_peer_get_information (peers[i],
816                                          GNUNET_TESTBED_PIT_CONFIGURATION,
817                                          &peer_info_cb, peers[i]);
818   }
819 }
820
821
822 /**
823  * Verify that the user passed correct CL args
824  *
825  * @return #GNUNET_OK if arguments are valid, #GNUNET_SYSERR otherwise
826  */
827 static int
828 verify_args ()
829 {
830   if (num_peers < 2)
831   {
832     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
833                 _
834                 ("Invalid or missing number of peers. Set at least 2 peers.\n"));
835     return GNUNET_SYSERR;
836   }
837   if (NULL == topology_file ||
838       GNUNET_YES != GNUNET_DISK_file_test (topology_file))
839   {
840     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
841                 _("Missing or invalid topology file.\n"));
842     return GNUNET_SYSERR;
843   }
844   return GNUNET_OK;
845 }
846
847
848 /**
849  * Actual main function.
850  *
851  * @param cls unused
852  * @param args remaining args, unused
853  * @param cfgfile name of the configuration
854  * @param cfg configuration handle
855  */
856 static void
857 run (void *cls, char *const *args, const char *cf,
858      const struct GNUNET_CONFIGURATION_Handle *c)
859 {
860   if (GNUNET_OK != verify_args ())
861   {
862     do_shutdown (NULL, NULL);
863     return;
864   }
865   cfg = GNUNET_CONFIGURATION_create ();
866   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (cfg, cfg_filename));
867   GNUNET_CONFIGURATION_set_value_string ((struct GNUNET_CONFIGURATION_Handle *)
868                                          cfg, "TESTBED",
869                                          "OVERLAY_TOPOLOGY_FILE",
870                                          topology_file);
871   shutdown_task =
872       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown,
873                                     NULL);
874   GNUNET_TESTBED_run (NULL, cfg, num_peers, 0, NULL, NULL, &test_master, NULL);
875 }
876
877
878 /**
879  * Main function.
880  *
881  * @return 0 on success
882  */
883 int
884 main (int argc, char *const *argv)
885 {
886   static struct GNUNET_GETOPT_CommandLineOption options[] = {
887     {'p', "peers", "COUNT", gettext_noop ("Number of peers to run"), GNUNET_YES,
888      &GNUNET_GETOPT_set_uint, &num_peers},
889     {'t', "topology-file", "FILEPATH", gettext_noop ("Path to topology file"),
890      GNUNET_YES, &GNUNET_GETOPT_set_filename, &topology_file},
891     {'i', "sensors-interval", "INTERVAL",
892      gettext_noop ("Change the interval or running sensors to given value"),
893      GNUNET_YES, &GNUNET_GETOPT_set_uint, &sensors_interval},
894     GNUNET_GETOPT_OPTION_END
895   };
896
897   return (GNUNET_OK ==
898           GNUNET_PROGRAM_run (argc, argv, "gnunet-sensor-profiler",
899                               gettext_noop ("Profiler for sensor service"),
900                               options, &run, NULL)) ? 0 : 1;
901 }
902
903 /* end of gnunet-sensor-profiler.c */