-use separate message types for TRY_CONNECT and TRY_DISCONNECT
[oweals/gnunet.git] / src / sensor / sensor_util_lib.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/sensor_util_lib.c
23  * @brief sensor utilities
24  * @author Omar Tarabai
25  */
26 #include <inttypes.h>
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_sensor_util_lib.h"
30 #include "gnunet_statistics_service.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "sensor-util",__VA_ARGS__)
33
34 /**
35  * Supported sources of sensor information
36  */
37 static const char *sources[] = { "gnunet-statistics", "process", NULL };
38
39 /**
40  * Supported datatypes of sensor information
41  */
42 static const char *datatypes[] = { "numeric", "string", NULL };
43
44 /**
45  * Parses a version number string into major and minor
46  *
47  * @param version full version string
48  * @param major pointer to parsed major value
49  * @param minor pointer to parsed minor value
50  * @return #GNUNET_OK if parsing went ok, #GNUNET_SYSERR in case of error
51  */
52 static int
53 version_parse (char *version, uint16_t * major, uint16_t * minor)
54 {
55   int majorval = 0;
56   int minorval = 0;
57
58   for (; isdigit (*version); version++)
59   {
60     majorval *= 10;
61     majorval += *version - '0';
62   }
63   if (*version != '.')
64     return GNUNET_SYSERR;
65   version++;
66   for (; isdigit (*version); version++)
67   {
68     minorval *= 10;
69     minorval += *version - '0';
70   }
71   if (*version != 0)
72     return GNUNET_SYSERR;
73   *major = majorval;
74   *minor = minorval;
75
76   return GNUNET_OK;
77 }
78
79
80 /**
81  * Load sensor definition from configuration
82  *
83  * @param cfg configuration handle
84  * @param sectionname configuration section containing definition
85  */
86 static struct GNUNET_SENSOR_SensorInfo *
87 load_sensor_from_cfg (struct GNUNET_CONFIGURATION_Handle *cfg,
88                       const char *sectionname)
89 {
90   struct GNUNET_SENSOR_SensorInfo *sensor;
91   char *version_str;
92   char *starttime_str;
93   char *endtime_str;
94   unsigned long long time_sec;
95   char *dummy;
96   struct GNUNET_CRYPTO_EddsaPublicKey public_key;
97
98   sensor = GNUNET_new (struct GNUNET_SENSOR_SensorInfo);
99
100   //name
101   sensor->name = GNUNET_strdup (sectionname);
102   //version
103   if (GNUNET_OK !=
104       GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "VERSION",
105                                              &version_str))
106   {
107     LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor version\n"));
108     GNUNET_free (sensor);
109     return NULL;
110   }
111   if (GNUNET_OK !=
112       version_parse (version_str, &(sensor->version_major),
113                      &(sensor->version_minor)))
114   {
115     LOG (GNUNET_ERROR_TYPE_ERROR,
116          _("Invalid sensor version number, format should be major.minor\n"));
117     GNUNET_free (sensor);
118     GNUNET_free (version_str);
119     return NULL;
120   }
121   GNUNET_free (version_str);
122   //description
123   GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "DESCRIPTION",
124                                          &sensor->description);
125   //category
126   if (GNUNET_OK !=
127       GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "CATEGORY",
128                                              &sensor->category) ||
129       NULL == sensor->category)
130   {
131     LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor category\n"));
132     GNUNET_free (sensor);
133     return NULL;
134   }
135   //enabled
136   if (GNUNET_NO ==
137       GNUNET_CONFIGURATION_get_value_yesno (cfg, sectionname, "ENABLED"))
138     sensor->enabled = GNUNET_NO;
139   else
140     sensor->enabled = GNUNET_YES;
141   //start time
142   sensor->start_time = NULL;
143   if (GNUNET_OK ==
144       GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "START_TIME",
145                                              &starttime_str))
146   {
147     GNUNET_STRINGS_fancy_time_to_absolute (starttime_str, sensor->start_time);
148     GNUNET_free (starttime_str);
149   }
150   //end time
151   sensor->end_time = NULL;
152   if (GNUNET_OK ==
153       GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "END_TIME",
154                                              &endtime_str))
155   {
156     GNUNET_STRINGS_fancy_time_to_absolute (endtime_str, sensor->end_time);
157     GNUNET_free (endtime_str);
158   }
159   //interval
160   if (GNUNET_OK !=
161       GNUNET_CONFIGURATION_get_value_number (cfg, sectionname, "INTERVAL",
162                                              &time_sec))
163   {
164     LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor run interval\n"));
165     GNUNET_free (sensor);
166     return NULL;
167   }
168   sensor->interval =
169       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, time_sec);
170   //lifetime
171   if (GNUNET_OK ==
172       GNUNET_CONFIGURATION_get_value_number (cfg, sectionname, "LIFETIME",
173                                              &time_sec))
174   {
175     sensor->lifetime =
176         GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, time_sec);
177     if (sensor->lifetime.rel_value_us < sensor->interval.rel_value_us)
178       LOG (GNUNET_ERROR_TYPE_WARNING,
179            "Lifetime of sensor data is preferred to be higher than interval for sensor `%s'.\n",
180            sensor->name);
181   }
182   else
183     sensor->lifetime = GNUNET_TIME_UNIT_FOREVER_REL;
184   //capabilities TODO
185   //source
186   if (GNUNET_OK !=
187       GNUNET_CONFIGURATION_get_value_choice (cfg, sectionname, "SOURCE",
188                                              sources,
189                                              (const char **) &sensor->source))
190   {
191     LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor source\n"));
192     GNUNET_free (sensor);
193     return NULL;
194   }
195   if (sources[0] == sensor->source)     //gnunet-statistics
196   {
197     if (GNUNET_OK !=
198         GNUNET_CONFIGURATION_get_value_string (cfg, sectionname,
199                                                "GNUNET_STAT_SERVICE",
200                                                &sensor->gnunet_stat_service) ||
201         GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, sectionname,
202                                                             "GNUNET_STAT_NAME",
203                                                             &sensor->gnunet_stat_name))
204     {
205       LOG (GNUNET_ERROR_TYPE_ERROR,
206            _("Error reading sensor gnunet-statistics source information\n"));
207       GNUNET_free (sensor);
208       return NULL;
209     }
210     sensor->gnunet_stat_get_handle = NULL;
211   }
212   else if (sources[1] == sensor->source)        //process
213   {
214     if (GNUNET_OK !=
215         GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "EXT_PROCESS",
216                                                &sensor->ext_process))
217     {
218       LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor process name\n"));
219       GNUNET_free (sensor);
220       return NULL;
221     }
222     GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "EXT_ARGS",
223                                            &sensor->ext_args);
224   }
225   //expected datatype
226   if (GNUNET_OK !=
227       GNUNET_CONFIGURATION_get_value_choice (cfg, sectionname,
228                                              "EXPECTED_DATATYPE", datatypes,
229                                              (const char **)
230                                              &sensor->expected_datatype))
231   {
232     LOG (GNUNET_ERROR_TYPE_ERROR,
233          _("Error reading sensor expected datatype\n"));
234     GNUNET_free (sensor);
235     return NULL;
236   }
237   if (sources[0] == sensor->source && datatypes[0] != sensor->expected_datatype)
238   {
239     LOG (GNUNET_ERROR_TYPE_ERROR,
240          _
241          ("Invalid expected datatype, gnunet-statistics returns uint64 values\n"));
242     GNUNET_free (sensor);
243     return NULL;
244   }
245   //reporting
246   sensor->collection_point = NULL;
247   sensor->report_values = GNUNET_NO;
248   sensor->report_anomalies = GNUNET_NO;
249   if (GNUNET_OK ==
250       GNUNET_CONFIGURATION_get_value_string (cfg, sectionname,
251                                              "COLLECTION_POINT", &dummy))
252   {
253     if (GNUNET_OK ==
254         GNUNET_CRYPTO_eddsa_public_key_from_string (dummy, strlen (dummy),
255                                                     &public_key))
256     {
257       sensor->collection_point = GNUNET_new (struct GNUNET_PeerIdentity);
258
259       sensor->collection_point->public_key = public_key;
260       if (GNUNET_OK ==
261           GNUNET_CONFIGURATION_get_value_number (cfg, sectionname,
262                                                  "VALUE_COLLECTION_INTERVAL",
263                                                  &time_sec))
264       {
265         sensor->report_values = GNUNET_YES;
266         sensor->value_reporting_interval =
267             GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, time_sec);
268       }
269       if (GNUNET_YES ==
270           GNUNET_CONFIGURATION_get_value_yesno (cfg, sectionname,
271                                                 "REPORT_ANOMALIES"))
272         sensor->report_anomalies = GNUNET_YES;
273     }
274     GNUNET_free (dummy);
275   }
276   //execution task
277   sensor->execution_task = GNUNET_SCHEDULER_NO_TASK;
278   //running
279   sensor->running = GNUNET_NO;
280   return sensor;
281 }
282
283
284 /**
285  * Load sensor definition from file
286  *
287  * @param filename full path to file containing sensor definition
288  */
289 static struct GNUNET_SENSOR_SensorInfo *
290 load_sensor_from_file (const char *filename)
291 {
292   struct GNUNET_CONFIGURATION_Handle *sensorcfg;
293   const char *filebasename;
294   struct GNUNET_SENSOR_SensorInfo *sensor;
295
296   //test file
297   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
298   {
299     LOG (GNUNET_ERROR_TYPE_ERROR, _("Failed to access sensor file: %s\n"),
300          filename);
301     return NULL;
302   }
303   //load file as configuration
304   sensorcfg = GNUNET_CONFIGURATION_create ();
305   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_parse (sensorcfg, filename))
306   {
307     GNUNET_CONFIGURATION_destroy (sensorcfg);
308     LOG (GNUNET_ERROR_TYPE_ERROR, _("Failed to load sensor definition: %s\n"),
309          filename);
310     return NULL;
311   }
312   //configuration section should be the same as filename
313   filebasename = GNUNET_STRINGS_get_short_name (filename);
314   sensor = load_sensor_from_cfg (sensorcfg, filebasename);
315   if (NULL == sensor)
316   {
317     GNUNET_CONFIGURATION_destroy (sensorcfg);
318     return NULL;
319   }
320   sensor->def_file = GNUNET_strdup (filename);
321   sensor->cfg = sensorcfg;
322   return sensor;
323 }
324
325
326 /**
327  * Given two version numbers as major and minor, compare them.
328  *
329  * @param v1_major First part of first version number
330  * @param v1_minor Second part of first version number
331  * @param v2_major First part of second version number
332  * @param v2_minor Second part of second version number
333  */
334 int
335 GNUNET_SENSOR_version_compare (uint16_t v1_major, uint16_t v1_minor,
336                                uint16_t v2_major, uint16_t v2_minor)
337 {
338   if (v1_major == v2_major)
339     return (v1_minor < v2_minor) ? -1 : (v1_minor > v2_minor);
340   else
341     return (v1_major < v2_major) ? -1 : (v1_major > v2_major);
342 }
343
344
345 /**
346  * Adds a new sensor to given hashmap.
347  * If the same name exist, compares versions and update if old.
348  *
349  * @param sensor Sensor structure to add
350  * @param map Hashmap to add to
351  * @return #GNUNET_YES if added
352  *         #GNUNET_NO if not added which is not necessarily an error
353  */
354 static int
355 add_sensor_to_hashmap (struct GNUNET_SENSOR_SensorInfo *sensor,
356                        struct GNUNET_CONTAINER_MultiHashMap *map)
357 {
358   struct GNUNET_HashCode key;
359   struct GNUNET_SENSOR_SensorInfo *existing;
360
361   GNUNET_CRYPTO_hash (sensor->name, strlen (sensor->name) + 1, &key);
362   existing = GNUNET_CONTAINER_multihashmap_get (map, &key);
363   if (NULL != existing)         //sensor with same name already exists
364   {
365     if (GNUNET_SENSOR_version_compare
366         (existing->version_major, existing->version_minor,
367          sensor->version_major, sensor->version_minor) >= 0)
368     {
369       LOG (GNUNET_ERROR_TYPE_INFO,
370            _("Sensor `%s' already exists with same or newer version\n"),
371            sensor->name);
372       return GNUNET_NO;
373     }
374     else
375     {
376       GNUNET_CONTAINER_multihashmap_remove (map, &key, existing);       //remove the old version
377       GNUNET_free (existing);
378       LOG (GNUNET_ERROR_TYPE_INFO, "Upgrading sensor `%s' to a newer version\n",
379            sensor->name);
380     }
381   }
382   if (GNUNET_SYSERR ==
383       GNUNET_CONTAINER_multihashmap_put (map, &key, sensor,
384                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
385   {
386     LOG (GNUNET_ERROR_TYPE_ERROR,
387          _("Error adding new sensor `%s' to global hashmap.\n"), sensor->name);
388     return GNUNET_NO;
389   }
390   return GNUNET_YES;
391 }
392
393
394 /**
395  * Iterating over files in sensors directory
396  *
397  * @param cls closure
398  * @param filename complete filename (absolute path)
399  * @return #GNUNET_OK to continue to iterate
400  */
401 static int
402 reload_sensors_dir_cb (void *cls, const char *filename)
403 {
404   struct GNUNET_CONTAINER_MultiHashMap *sensors = cls;
405   struct GNUNET_SENSOR_SensorInfo *sensor;
406
407   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
408     return GNUNET_OK;
409   sensor = load_sensor_from_file (filename);
410   if (NULL == sensor)
411   {
412     LOG (GNUNET_ERROR_TYPE_ERROR, _("Error loading sensor from file: %s\n"),
413          filename);
414     return GNUNET_OK;
415   }
416   if (GNUNET_YES != add_sensor_to_hashmap (sensor, sensors))
417     LOG (GNUNET_ERROR_TYPE_WARNING,
418          "Could not add sensor `%s' to global hashmap\n", sensor->name);
419   return GNUNET_OK;
420 }
421
422
423 /**
424  * Get path to the default directory containing the sensor definition files with
425  * a trailing directory separator.
426  *
427  * @return Default sensor files directory full path
428  */
429 char *
430 GNUNET_SENSOR_get_default_sensor_dir ()
431 {
432   char *datadir;
433   char *sensordir;
434
435   datadir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
436   GNUNET_asprintf (&sensordir, "%ssensors%s", datadir, DIR_SEPARATOR_STR);
437   GNUNET_free (datadir);
438   return sensordir;
439 }
440
441
442 /**
443  * Reads sensor definitions from given sensor directory.
444  *
445  * @param sensordir Path to sensor directory.
446  * @return a multihashmap of loaded sensors
447  */
448 struct GNUNET_CONTAINER_MultiHashMap *
449 GNUNET_SENSOR_load_all_sensors (char *sensor_dir)
450 {
451   struct GNUNET_CONTAINER_MultiHashMap *sensors;
452
453   GNUNET_assert (NULL != sensor_dir);
454   sensors = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
455   LOG (GNUNET_ERROR_TYPE_INFO,
456        "Loading sensor definitions from directory `%s'\n", sensor_dir);
457   GNUNET_assert (GNUNET_YES ==
458                  GNUNET_DISK_directory_test (sensor_dir, GNUNET_YES));
459   /* read all files in sensors directory */
460   GNUNET_DISK_directory_scan (sensor_dir, &reload_sensors_dir_cb, sensors);
461   LOG (GNUNET_ERROR_TYPE_INFO, "Loaded %d sensors from directory `%s'\n",
462        GNUNET_CONTAINER_multihashmap_size (sensors), sensor_dir);
463   return sensors;
464 }
465
466
467 /**
468  * Remove sensor execution from scheduler
469  *
470  * @param cls unused
471  * @param key hash of sensor name, key to hashmap
472  * @param value a `struct GNUNET_SENSOR_SensorInfo *`
473  * @return #GNUNET_YES if we should continue to
474  *         iterate,
475  *         #GNUNET_NO if not.
476  */
477 static int
478 destroy_sensor (void *cls, const struct GNUNET_HashCode *key, void *value)
479 {
480   struct GNUNET_SENSOR_SensorInfo *sensor = value;
481
482   if (GNUNET_SCHEDULER_NO_TASK != sensor->execution_task)
483   {
484     GNUNET_SCHEDULER_cancel (sensor->execution_task);
485     sensor->execution_task = GNUNET_SCHEDULER_NO_TASK;
486   }
487   if (NULL != sensor->gnunet_stat_get_handle)
488   {
489     GNUNET_STATISTICS_get_cancel (sensor->gnunet_stat_get_handle);
490     sensor->gnunet_stat_get_handle = NULL;
491   }
492   if (NULL != sensor->ext_cmd)
493   {
494     GNUNET_OS_command_stop (sensor->ext_cmd);
495     sensor->ext_cmd = NULL;
496   }
497   if (NULL != sensor->cfg)
498     GNUNET_CONFIGURATION_destroy (sensor->cfg);
499   if (NULL != sensor->name)
500     GNUNET_free (sensor->name);
501   if (NULL != sensor->def_file)
502     GNUNET_free (sensor->def_file);
503   if (NULL != sensor->description)
504     GNUNET_free (sensor->description);
505   if (NULL != sensor->category)
506     GNUNET_free (sensor->category);
507   if (NULL != sensor->capabilities)
508     GNUNET_free (sensor->capabilities);
509   if (NULL != sensor->gnunet_stat_service)
510     GNUNET_free (sensor->gnunet_stat_service);
511   if (NULL != sensor->gnunet_stat_name)
512     GNUNET_free (sensor->gnunet_stat_name);
513   if (NULL != sensor->ext_process)
514     GNUNET_free (sensor->ext_process);
515   if (NULL != sensor->ext_args)
516     GNUNET_free (sensor->ext_args);
517   if (NULL != sensor->collection_point)
518     GNUNET_free (sensor->collection_point);
519   GNUNET_free (sensor);
520   return GNUNET_YES;
521 }
522
523
524 /**
525  * Destroys a group of sensors in a hashmap and the hashmap itself
526  *
527  * @param sensors hashmap containing the sensors
528  */
529 void
530 GNUNET_SENSOR_destroy_sensors (struct GNUNET_CONTAINER_MultiHashMap *sensors)
531 {
532   LOG (GNUNET_ERROR_TYPE_DEBUG, "Destroying sensor list.\n");
533   GNUNET_CONTAINER_multihashmap_iterate (sensors, &destroy_sensor, NULL);
534   GNUNET_CONTAINER_multihashmap_destroy (sensors);
535 }