sensor: towards profiler
[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[0].
468                                                             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   printf ("Anomaly report:\n" "  Peer: `%s'\n" "  Sensor: `%s'\n"
548           "  Anomalous: `%d'\n" "  Anomalous neighbors: %f.\n\n",
549           GNUNET_i2s (&peer->peer_id), record->key, anomaly->anomalous,
550           anomaly->anomalous_neighbors);
551   return GNUNET_YES;
552 }
553
554
555 /**
556  * Callback to be called when peerstore service connect operation is completed
557  *
558  * @param cls the callback closure from functions generating an operation
559  * @param op the operation that has been finished
560  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
561  * @param emsg error message in case the operation has failed; will be NULL if
562  *          operation has executed successfully.
563  */
564 static void
565 peerstore_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
566                       void *ca_result, const char *emsg)
567 {
568   int i;
569   int j;
570   struct PeerInfo *peer;
571
572   if (NULL != emsg)
573   {
574     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
575     GNUNET_assert (0);
576   }
577   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected to peerstore service.\n");
578   /* Watch for anomaly reports from other peers */
579   for (i = 0; i < num_peers; i++)
580   {
581     peer = &all_peers_info[i];
582     for (j = 0; j < sensor_names_size; j++)
583     {
584       GNUNET_PEERSTORE_watch (peerstore, "sensordashboard-anomalies",
585                               &peer->peer_id, sensor_names[j],
586                               &peerstore_watch_cb, peer);
587     }
588   }
589 }
590
591
592 /**
593  * Adapter function called to establish a connection to peerstore service.
594  *
595  * @param cls closure
596  * @param cfg configuration of the peer to connect to; will be available until
597  *          GNUNET_TESTBED_operation_done() is called on the operation returned
598  *          from GNUNET_TESTBED_service_connect()
599  * @return service handle to return in 'op_result', NULL on error
600  */
601 static void *
602 peerstore_connect_adapter (void *cls,
603                            const struct GNUNET_CONFIGURATION_Handle *cfg)
604 {
605   peerstore = GNUNET_PEERSTORE_connect (cfg);
606   GNUNET_assert (NULL != peerstore);
607   return peerstore;
608 }
609
610
611 /**
612  * Adapter function called to destroy a connection to peerstore service.
613  *
614  * @param cls closure
615  * @param op_result service handle returned from the connect adapter
616  */
617 static void
618 peerstore_disconnect_adapter (void *cls, void *op_result)
619 {
620   GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_NO);
621   peerstore = NULL;
622   peerstore_op = NULL;
623 }
624
625
626 /**
627  * Prompt the user to disconnect two peers
628  */
629 static void
630 prompt_peer_disconnection ()
631 {
632   int p1;
633   int p2;
634   char line[10];
635
636   printf ("Disconnect peers (e.g. '0,2') or empty line to execute: ");
637   if (NULL == fgets (line, sizeof (line), stdin) || 1 == strlen (line))
638   {
639     printf ("Continuing.\n");
640     return;
641   }
642   if (2 != sscanf (line, "%d,%d", &p1, &p2) || p1 >= num_peers ||
643       p2 >= num_peers || p1 < 0 || p2 < 0 || p1 == p2)
644   {
645     printf ("Invalid input.\n");
646     prompt_peer_disconnection ();
647     return;
648   }
649   disconnect_peers (&all_peers_info[p1], &all_peers_info[p2]);
650   prompt_peer_disconnection ();
651 }
652
653
654 /**
655  * This function is called after the estimated training period is over.
656  */
657 static void
658 simulate_anomalies (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
659 {
660   delayed_task = GNUNET_SCHEDULER_NO_TASK;
661   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
662               "Training period over, simulating anomalies now.\n");
663   prompt_peer_disconnection ();
664 }
665
666
667 /**
668  * This function is called after a delay which ensures that all peers are
669  * properly initialized
670  */
671 static void
672 peers_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
673 {
674   unsigned long long int training_points;
675   struct GNUNET_TIME_Relative training_period;
676
677   delayed_task = GNUNET_SCHEDULER_NO_TASK;
678   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All peers are ready.\n");
679   GNUNET_assert (GNUNET_OK ==
680                  GNUNET_CONFIGURATION_get_value_number (cfg,
681                                                         "sensor-model-gaussian",
682                                                         "TRAINING_WINDOW",
683                                                         &training_points));
684   training_period =
685       GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_multiply
686                                      (GNUNET_TIME_UNIT_SECONDS,
687                                       (sensors_interval ==
688                                        0) ? 60 : sensors_interval),
689                                      training_points);
690   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
691               "Sleeping for a training period of %s.\n",
692               GNUNET_STRINGS_relative_time_to_string (training_period,
693                                                       GNUNET_NO));
694   delayed_task =
695       GNUNET_SCHEDULER_add_delayed (training_period, &simulate_anomalies, NULL);
696 }
697
698
699 /**
700  * Callback to be called when sensor service is started
701  *
702  * @param cls the callback closure from functions generating an operation
703  * @param op the operation that has been finished
704  * @param emsg error message in case the operation has failed; will be NULL if
705  *          operation has executed successfully.
706  */
707 static void
708 sensor_service_started (void *cls, struct GNUNET_TESTBED_Operation *op,
709                         const char *emsg)
710 {
711   struct PeerInfo *peer = cls;
712
713   if (NULL != emsg)
714   {
715     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
716     GNUNET_assert (0);
717   }
718   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sensor service started on peer `%s'.\n",
719               GNUNET_i2s (&peer->peer_id));
720   GNUNET_TESTBED_operation_done (op);
721   sensor_services_started++;
722   if (sensor_services_started == num_peers)
723   {
724     delayed_task =
725         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
726                                       (PEER_STARTUP_TIME, num_peers),
727                                       &peers_ready, NULL);
728   }
729 }
730
731
732 /**
733  * Callback to be called when the requested peer information is available
734  *
735  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
736  * @param op the operation this callback corresponds to
737  * @param pinfo the result; will be NULL if the operation has failed
738  * @param emsg error message if the operation has failed; will be NULL if the
739  *          operation is successfull
740  */
741 static void
742 peer_info_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op,
743               const struct GNUNET_TESTBED_PeerInformation *pinfo,
744               const char *emsg)
745 {
746   struct GNUNET_TESTBED_Peer *testbed_peer = cb_cls;
747   struct PeerInfo *peer = &all_peers_info[peers_known];
748
749   if (NULL != emsg)
750   {
751     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
752     GNUNET_assert (0);
753   }
754   peer->testbed_peer = testbed_peer;
755   GNUNET_CRYPTO_get_peer_identity (pinfo->result.cfg, &peer->peer_id);
756   peers_known++;
757   if (1 == peers_known)         /* First peer is collection point */
758   {
759     /* Rewrite sensors */
760     rewrite_sensors ();
761     /* Start dashboard */
762     GNUNET_TESTBED_peer_manage_service (NULL, testbed_peer, "sensordashboard",
763                                         &dashboard_started, NULL, 1);
764   }
765   /* Start sensor service on every peer */
766   GNUNET_TESTBED_peer_manage_service (NULL, testbed_peer, "sensor",
767                                       &sensor_service_started, peer, 1);
768   if (num_peers == peers_known) /* Last peer */
769   {
770     /* Connect to peerstore on first peer (collection point) */
771     peerstore_op =
772         GNUNET_TESTBED_service_connect (NULL, all_peers_info[0].testbed_peer,
773                                         "peerstore", &peerstore_connect_cb,
774                                         NULL, &peerstore_connect_adapter,
775                                         &peerstore_disconnect_adapter, NULL);
776   }
777   GNUNET_TESTBED_operation_done (op);
778 }
779
780
781 /**
782  * Signature of a main function for a testcase.
783  *
784  * @param cls closure
785  * @param h the run handle
786  * @param num number of peers in 'peers'
787  * @param peers handle to peers run in the testbed.  NULL upon timeout (see
788  *          GNUNET_TESTBED_test_run()).
789  * @param links_succeeded the number of overlay link connection attempts that
790  *          succeeded
791  * @param links_failed the number of overlay link connection attempts that
792  *          failed
793  * @see GNUNET_TESTBED_test_run()
794  */
795 static void
796 test_master (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num,
797              struct GNUNET_TESTBED_Peer **peers, unsigned int links_succeeded,
798              unsigned int links_failed)
799 {
800   int i;
801
802   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
803               "%d peers started. %d links succeeded. %d links failed.\n",
804               num_peers, links_succeeded, links_failed);
805   GNUNET_assert (num == num_peers);
806   GNUNET_assert (0 == links_failed);
807   /* Collect peer information */
808   all_peers_info = GNUNET_new_array (num_peers, struct PeerInfo);
809
810   for (i = 0; i < num_peers; i++)
811   {
812     GNUNET_TESTBED_peer_get_information (peers[i],
813                                          GNUNET_TESTBED_PIT_CONFIGURATION,
814                                          &peer_info_cb, peers[i]);
815   }
816 }
817
818
819 /**
820  * Verify that the user passed correct CL args
821  *
822  * @return #GNUNET_OK if arguments are valid, #GNUNET_SYSERR otherwise
823  */
824 static int
825 verify_args ()
826 {
827   if (num_peers < 2)
828   {
829     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
830                 _
831                 ("Invalid or missing number of peers. Set at least 2 peers.\n"));
832     return GNUNET_SYSERR;
833   }
834   if (NULL == topology_file ||
835       GNUNET_YES != GNUNET_DISK_file_test (topology_file))
836   {
837     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
838                 _("Missing or invalid topology file.\n"));
839     return GNUNET_SYSERR;
840   }
841   return GNUNET_OK;
842 }
843
844
845 /**
846  * Actual main function.
847  *
848  * @param cls unused
849  * @param args remaining args, unused
850  * @param cfgfile name of the configuration
851  * @param cfg configuration handle
852  */
853 static void
854 run (void *cls, char *const *args, const char *cf,
855      const struct GNUNET_CONFIGURATION_Handle *c)
856 {
857   if (GNUNET_OK != verify_args ())
858   {
859     do_shutdown (NULL, NULL);
860     return;
861   }
862   cfg = GNUNET_CONFIGURATION_create ();
863   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (cfg, cfg_filename));
864   GNUNET_CONFIGURATION_set_value_string ((struct GNUNET_CONFIGURATION_Handle *)
865                                          cfg, "TESTBED",
866                                          "OVERLAY_TOPOLOGY_FILE",
867                                          topology_file);
868   shutdown_task =
869       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown,
870                                     NULL);
871   GNUNET_TESTBED_run (NULL, cfg, num_peers, 0, NULL, NULL, &test_master, NULL);
872 }
873
874
875 /**
876  * Main function.
877  *
878  * @return 0 on success
879  */
880 int
881 main (int argc, char *const *argv)
882 {
883   static struct GNUNET_GETOPT_CommandLineOption options[] = {
884     {'p', "peers", "COUNT", gettext_noop ("Number of peers to run"), GNUNET_YES,
885      &GNUNET_GETOPT_set_uint, &num_peers},
886     {'t', "topology-file", "FILEPATH", gettext_noop ("Path to topology file"),
887      GNUNET_YES, &GNUNET_GETOPT_set_filename, &topology_file},
888     {'i', "sensors-interval", "INTERVAL",
889      gettext_noop ("Change the interval or running sensors to given value"),
890      GNUNET_YES, &GNUNET_GETOPT_set_uint, &sensors_interval},
891     GNUNET_GETOPT_OPTION_END
892   };
893
894   return (GNUNET_OK ==
895           GNUNET_PROGRAM_run (argc, argv, "gnunet-sensor-profiler",
896                               gettext_noop ("Profiler for sensor service"),
897                               options, &run, NULL)) ? 0 : 1;
898 }
899
900 /* end of gnunet-sensor-profiler.c */