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