e89e0f025212b6bce099ae9a71885170cf611333
[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
33 /**
34  * Time to wait for the peer to startup completely
35  */
36 #define PEER_STARTUP_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
37
38 /**
39  * Information about a single peer
40  */
41 struct PeerInfo
42 {
43
44   /**
45    * Peer Identity
46    */
47   struct GNUNET_PeerIdentity peer_id;
48
49   /**
50    * Testbed peer handle
51    */
52   struct GNUNET_TESTBED_Peer *testbed_peer;
53
54 };
55
56
57 /**
58  * Name of the configuration file used
59  */
60 static const char *cfg_filename = "gnunet-sensor-profiler.conf";
61
62 /**
63  * Directory to read sensor definitions from
64  */
65 static const char *sensor_src_dir = "sensors";
66
67 /**
68  * Directory to write new sensor definitions to
69  */
70 static const char *sensor_dst_dir = "/tmp/gnunet-sensor-profiler";
71
72 /**
73  * Return value of the program
74  */
75 static int ok = 1;
76
77 /**
78  * Number of peers to run (Option -p)
79  */
80 static unsigned int num_peers = 0;
81
82 /**
83  * Set sensors running interval to this value (Option -i)
84  */
85 static unsigned int sensors_interval = 0;
86
87 /**
88  * Path to topology file (Option -t)
89  */
90 static char *topology_file;
91
92 /**
93  * Array of peer info for all peers
94  */
95 static struct PeerInfo *all_peers_info;
96
97 /**
98  * Number of peers that we already collected and start their info
99  */
100 static int peers_known = 0;
101
102 /**
103  * TESTBED operation connecting us to peerstore service on collection point
104  */
105 static struct GNUNET_TESTBED_Operation *peerstore_op;
106
107 /**
108  * Handle to peerstore service on collection point
109  */
110 static struct GNUNET_PEERSTORE_Handle *peerstore;
111
112 /**
113  * Dashboard service on collection point started?
114  */
115 static int dashboard_service_started = GNUNET_NO;
116
117 /**
118  * Number of peers started the sensor service successfully
119  */
120 static int sensor_services_started = 0;
121
122 /**
123  * Array of sensor names to be used for watching peerstore records
124  */
125 static char **sensor_names;
126
127 /**
128  * Size of 'sensor_names' array
129  */
130 static unsigned int sensor_names_size = 0;
131
132
133 /**
134  * Copy directory recursively
135  *
136  * @param src Path to source directory
137  * @param dst Destination directory, will be created if it does not exist
138  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
139  */
140 static int
141 copy_dir (const char *src, const char *dst);
142
143
144 /**
145  * Do clean up and shutdown scheduler
146  */
147 static void
148 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
149 {
150   int i;
151
152   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down.\n");
153   if (NULL != sensor_names)
154   {
155     for (i = 0; i < sensor_names_size; i++)
156       GNUNET_free (sensor_names[i]);
157     GNUNET_array_grow (sensor_names, sensor_names_size, 0);
158   }
159   if (NULL != peerstore_op)
160   {
161     GNUNET_TESTBED_operation_done (peerstore_op);
162     peerstore_op = NULL;
163   }
164   if (NULL != all_peers_info)
165   {
166     GNUNET_free (all_peers_info);
167     all_peers_info = NULL;
168   }
169   GNUNET_SCHEDULER_shutdown ();
170 }
171
172
173 /**
174  * Function called with each file/folder inside a directory that is being copied.
175  *
176  * @param cls closure, destination directory
177  * @param filename complete filename (absolute path)
178  * @return #GNUNET_OK to continue to iterate.
179  *         #GNUNET_SYSERR to abort iteration with error
180  */
181 static int
182 copy_dir_scanner (void *cls, const char *filename)
183 {
184   char *dst_dir = cls;
185   char *dst;
186   int copy_result;
187
188   GNUNET_asprintf (&dst, "%s%s%s", dst_dir, DIR_SEPARATOR_STR,
189                    GNUNET_STRINGS_get_short_name (filename));
190   if (GNUNET_YES == GNUNET_DISK_directory_test (filename, GNUNET_YES))
191     copy_result = copy_dir (filename, dst);
192   else
193   {
194     if (GNUNET_YES == GNUNET_DISK_file_test (dst))
195       GNUNET_DISK_directory_remove (dst);
196     copy_result = GNUNET_DISK_file_copy (filename, dst);
197   }
198   GNUNET_free (dst);
199   return copy_result;
200 }
201
202
203 /**
204  * Copy directory recursively
205  *
206  * @param src Path to source directory
207  * @param dst Destination directory, will be created if it does not exist
208  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
209  */
210 static int
211 copy_dir (const char *src, const char *dst)
212 {
213   if (GNUNET_YES != GNUNET_DISK_directory_test (src, GNUNET_YES))
214     return GNUNET_SYSERR;
215   if (GNUNET_OK != GNUNET_DISK_directory_create (dst))
216     return GNUNET_SYSERR;
217   if (GNUNET_SYSERR ==
218       GNUNET_DISK_directory_scan (src, &copy_dir_scanner, (char *) dst))
219     return GNUNET_SYSERR;
220   return GNUNET_OK;
221 }
222
223
224 /**
225  * Function called with each file/folder inside source sensor directory.
226  *
227  * @param cls closure (unused)
228  * @param filename complete filename (absolute path)
229  * @return #GNUNET_OK to continue to iterate.
230  */
231 static int
232 sensor_dir_scanner (void *cls, const char *filename)
233 {
234   const char *file_basename;
235   char *dst_path;
236   struct GNUNET_CONFIGURATION_Handle *sensor_cfg;
237   char *sensor_name;
238
239   file_basename = GNUNET_STRINGS_get_short_name (filename);
240   GNUNET_asprintf (&dst_path, "%s%s%s", sensor_dst_dir, DIR_SEPARATOR_STR,
241                    file_basename);
242   if (GNUNET_YES == GNUNET_DISK_directory_test (filename, GNUNET_NO))
243   {
244     GNUNET_assert (GNUNET_OK == copy_dir (filename, dst_path));
245   }
246   else
247   {
248     sensor_name = GNUNET_strdup (file_basename);
249     GNUNET_array_append (sensor_names, sensor_names_size, sensor_name);
250     sensor_cfg = GNUNET_CONFIGURATION_create ();
251     GNUNET_assert (GNUNET_OK ==
252                    GNUNET_CONFIGURATION_parse (sensor_cfg, filename));
253     GNUNET_CONFIGURATION_set_value_string (sensor_cfg, file_basename,
254                                            "COLLECTION_POINT",
255                                            GNUNET_i2s_full (&all_peers_info[0].
256                                                             peer_id));
257     if (sensors_interval > 0)
258     {
259       GNUNET_CONFIGURATION_set_value_number (sensor_cfg, file_basename,
260                                              "INTERVAL",
261                                              (unsigned long long int)
262                                              sensors_interval);
263     }
264     GNUNET_CONFIGURATION_write (sensor_cfg, dst_path);
265     GNUNET_CONFIGURATION_destroy (sensor_cfg);
266   }
267   GNUNET_free (dst_path);
268   return GNUNET_OK;
269 }
270
271
272 /**
273  * Load sensor definitions and rewrite them to tmp location.
274  * Add collection point peer id and change running interval if needed.
275  */
276 static void
277 rewrite_sensors ()
278 {
279   GNUNET_assert (GNUNET_YES ==
280                  GNUNET_DISK_directory_test (sensor_src_dir, GNUNET_YES));
281   GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_create (sensor_dst_dir));
282   GNUNET_DISK_directory_scan (sensor_src_dir, &sensor_dir_scanner, NULL);
283 }
284
285
286 /**
287  * Callback to be called when dashboard service is started
288  *
289  * @param cls the callback closure from functions generating an operation
290  * @param op the operation that has been finished
291  * @param emsg error message in case the operation has failed; will be NULL if
292  *          operation has executed successfully.
293  */
294 static void
295 dashboard_started (void *cls, struct GNUNET_TESTBED_Operation *op,
296                    const char *emsg)
297 {
298   if (NULL != emsg)
299   {
300     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
301     GNUNET_assert (0);
302   }
303   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dashboard service started.\n");
304   GNUNET_TESTBED_operation_done (op);
305   dashboard_service_started = GNUNET_YES;
306 }
307
308
309 /**
310  * Function called by PEERSTORE for each matching record.
311  *
312  * @param cls closure
313  * @param record peerstore record information
314  * @param emsg error message, or NULL if no errors
315  * @return #GNUNET_YES to continue iterating, #GNUNET_NO to stop
316  */
317 static int
318 peerstore_watch_cb (void *cls, struct GNUNET_PEERSTORE_Record *record,
319                     char *emsg)
320 {
321   struct PeerInfo *peer = cls;
322   struct GNUNET_SENSOR_DashboardAnomalyEntry *anomaly;
323
324   if (NULL != emsg)
325   {
326     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
327     GNUNET_assert (0);
328   }
329   GNUNET_assert (record->value_size ==
330                  sizeof (struct GNUNET_SENSOR_DashboardAnomalyEntry));
331   anomaly = record->value;
332   GNUNET_assert (0 ==
333                  GNUNET_CRYPTO_cmp_peer_identity (&peer->peer_id,
334                                                   record->peer));
335   printf ("Anomaly report:\n" "  Peer: `%s'\n" "  Sensor: `%s'\n"
336           "  Anomalous: `%d'\n" "  Anomalous neighbors: %f.\n\n",
337           GNUNET_i2s (&peer->peer_id), record->key, anomaly->anomalous,
338           anomaly->anomalous_neighbors);
339   return GNUNET_YES;
340 }
341
342
343 /**
344  * Callback to be called when peerstore service connect operation is completed
345  *
346  * @param cls the callback closure from functions generating an operation
347  * @param op the operation that has been finished
348  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
349  * @param emsg error message in case the operation has failed; will be NULL if
350  *          operation has executed successfully.
351  */
352 static void
353 peerstore_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
354                       void *ca_result, const char *emsg)
355 {
356   int i;
357   int j;
358   struct PeerInfo *peer;
359
360   if (NULL != emsg)
361   {
362     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
363     GNUNET_assert (0);
364   }
365   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected to peerstore service.\n");
366   /* Watch for anomaly reports from other peers */
367   for (i = 0; i < num_peers; i++)
368   {
369     peer = &all_peers_info[i];
370     for (j = 0; j < sensor_names_size; j++)
371     {
372       GNUNET_PEERSTORE_watch (peerstore, "sensordashboard-anomalies",
373                               &peer->peer_id, sensor_names[j],
374                               &peerstore_watch_cb, peer);
375     }
376   }
377 }
378
379
380 /**
381  * Adapter function called to establish a connection to peerstore service.
382  *
383  * @param cls closure
384  * @param cfg configuration of the peer to connect to; will be available until
385  *          GNUNET_TESTBED_operation_done() is called on the operation returned
386  *          from GNUNET_TESTBED_service_connect()
387  * @return service handle to return in 'op_result', NULL on error
388  */
389 static void *
390 peerstore_connect_adapter (void *cls,
391                            const struct GNUNET_CONFIGURATION_Handle *cfg)
392 {
393   peerstore = GNUNET_PEERSTORE_connect (cfg);
394   GNUNET_assert (NULL != peerstore);
395   return peerstore;
396 }
397
398
399 /**
400  * Adapter function called to destroy a connection to peerstore service.
401  *
402  * @param cls closure
403  * @param op_result service handle returned from the connect adapter
404  */
405 static void
406 peerstore_disconnect_adapter (void *cls, void *op_result)
407 {
408   GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_NO);
409   peerstore = NULL;
410   peerstore_op = NULL;
411 }
412
413
414 /**
415  * This function is called after a delay which ensures that all peers are
416  * properly initialized
417  */
418 static void
419 peers_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
420 {
421   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All peers are ready.\n");
422   //TODO
423 }
424
425
426 /**
427  * Callback to be called when sensor service is started
428  *
429  * @param cls the callback closure from functions generating an operation
430  * @param op the operation that has been finished
431  * @param emsg error message in case the operation has failed; will be NULL if
432  *          operation has executed successfully.
433  */
434 static void
435 sensor_service_started (void *cls, struct GNUNET_TESTBED_Operation *op,
436                         const char *emsg)
437 {
438   struct PeerInfo *peer = cls;
439
440   if (NULL != emsg)
441   {
442     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
443     GNUNET_assert (0);
444   }
445   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sensor service started on peer `%s'.\n",
446               GNUNET_i2s (&peer->peer_id));
447   GNUNET_TESTBED_operation_done (op);
448   sensor_services_started++;
449   if (sensor_services_started == num_peers)
450   {
451     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
452                                   (PEER_STARTUP_TIME, num_peers), &peers_ready,
453                                   NULL);
454   }
455 }
456
457
458 /**
459  * Callback to be called when the requested peer information is available
460  *
461  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
462  * @param op the operation this callback corresponds to
463  * @param pinfo the result; will be NULL if the operation has failed
464  * @param emsg error message if the operation has failed; will be NULL if the
465  *          operation is successfull
466  */
467 static void
468 peer_info_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op,
469               const struct GNUNET_TESTBED_PeerInformation *pinfo,
470               const char *emsg)
471 {
472   struct GNUNET_TESTBED_Peer *testbed_peer = cb_cls;
473   struct PeerInfo *peer = &all_peers_info[peers_known];
474
475   if (NULL != emsg)
476   {
477     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
478     GNUNET_assert (0);
479   }
480   peer->testbed_peer = testbed_peer;
481   GNUNET_CRYPTO_get_peer_identity (pinfo->result.cfg, &peer->peer_id);
482   peers_known++;
483   if (1 == peers_known)         /* First peer is collection point */
484   {
485     /* Rewrite sensors */
486     rewrite_sensors ();
487     /* Start dashboard */
488     GNUNET_TESTBED_peer_manage_service (NULL, testbed_peer, "sensordashboard",
489                                         &dashboard_started, NULL, 1);
490   }
491   /* Start sensor service on every peer */
492   GNUNET_TESTBED_peer_manage_service (NULL, testbed_peer, "sensor",
493                                       &sensor_service_started, peer, 1);
494   if (num_peers == peers_known) /* Last peer */
495   {
496     /* Connect to peerstore on first peer (collection point) */
497     peerstore_op =
498         GNUNET_TESTBED_service_connect (NULL, all_peers_info[0].testbed_peer,
499                                         "peerstore", &peerstore_connect_cb,
500                                         NULL, &peerstore_connect_adapter,
501                                         &peerstore_disconnect_adapter, NULL);
502   }
503   GNUNET_TESTBED_operation_done (op);
504 }
505
506
507 /**
508  * Signature of a main function for a testcase.
509  *
510  * @param cls closure
511  * @param h the run handle
512  * @param num number of peers in 'peers'
513  * @param peers handle to peers run in the testbed.  NULL upon timeout (see
514  *          GNUNET_TESTBED_test_run()).
515  * @param links_succeeded the number of overlay link connection attempts that
516  *          succeeded
517  * @param links_failed the number of overlay link connection attempts that
518  *          failed
519  * @see GNUNET_TESTBED_test_run()
520  */
521 static void
522 test_master (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num,
523              struct GNUNET_TESTBED_Peer **peers, unsigned int links_succeeded,
524              unsigned int links_failed)
525 {
526   int i;
527
528   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
529               "%d peers started. %d links succeeded. %d links failed.\n",
530               num_peers, links_succeeded, links_failed);
531   GNUNET_assert (num == num_peers);
532   GNUNET_assert (0 == links_failed);
533   /* Collect peer information */
534   all_peers_info = GNUNET_new_array (num_peers, struct PeerInfo);
535
536   for (i = 0; i < num_peers; i++)
537   {
538     GNUNET_TESTBED_peer_get_information (peers[i],
539                                          GNUNET_TESTBED_PIT_CONFIGURATION,
540                                          &peer_info_cb, peers[i]);
541   }
542 }
543
544
545 /**
546  * Verify that the user passed correct CL args
547  *
548  * @return #GNUNET_OK if arguments are valid, #GNUNET_SYSERR otherwise
549  */
550 static int
551 verify_args ()
552 {
553   if (num_peers < 3)
554   {
555     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
556                 _
557                 ("Invalid or missing number of peers. Set at least 3 peers.\n"));
558     return GNUNET_SYSERR;
559   }
560   if (NULL == topology_file ||
561       GNUNET_YES != GNUNET_DISK_file_test (topology_file))
562   {
563     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
564                 _("Missing or invalid topology file.\n"));
565     return GNUNET_SYSERR;
566   }
567   return GNUNET_OK;
568 }
569
570
571 /**
572  * Actual main function.
573  *
574  * @param cls unused
575  * @param args remaining args, unused
576  * @param cfgfile name of the configuration
577  * @param cfg configuration handle
578  */
579 static void
580 run (void *cls, char *const *args, const char *cf,
581      const struct GNUNET_CONFIGURATION_Handle *c)
582 {
583   struct GNUNET_CONFIGURATION_Handle *cfg;
584
585   if (GNUNET_OK != verify_args ())
586   {
587     do_shutdown (NULL, NULL);
588     return;
589   }
590   cfg = GNUNET_CONFIGURATION_create ();
591   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (cfg, cfg_filename));
592   GNUNET_CONFIGURATION_set_value_string ((struct GNUNET_CONFIGURATION_Handle *)
593                                          cfg, "TESTBED",
594                                          "OVERLAY_TOPOLOGY_FILE",
595                                          topology_file);
596   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown,
597                                 NULL);
598   GNUNET_TESTBED_run (NULL, cfg, num_peers, 0, NULL, NULL, &test_master, NULL);
599   GNUNET_CONFIGURATION_destroy (cfg);
600 }
601
602
603 /**
604  * Main function.
605  *
606  * @return 0 on success
607  */
608 int
609 main (int argc, char *const *argv)
610 {
611   static struct GNUNET_GETOPT_CommandLineOption options[] = {
612     {'p', "peers", "COUNT", gettext_noop ("Number of peers to run"), GNUNET_YES,
613      &GNUNET_GETOPT_set_uint, &num_peers},
614     {'t', "topology-file", "FILEPATH", gettext_noop ("Path to topology file"),
615      GNUNET_YES, &GNUNET_GETOPT_set_filename, &topology_file},
616     {'i', "sensors-interval", "INTERVAL",
617      gettext_noop ("Change the interval or running sensors to given value"),
618      GNUNET_YES, &GNUNET_GETOPT_set_uint, &sensors_interval},
619     GNUNET_GETOPT_OPTION_END
620   };
621
622   return (GNUNET_OK ==
623           GNUNET_PROGRAM_run (argc, argv, "gnunet-sensor-profiler",
624                               gettext_noop ("Profiler for sensor service"),
625                               options, &run, NULL)) ? ok : 1;
626 }
627
628 /* end of gnunet-sensor-profiler.c */