sensor: proof-of-work and signing library functions
[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  * 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     GNUNET_free (starttime_str);
154   }
155   //end time
156   sensor->end_time = NULL;
157   if (GNUNET_OK ==
158       GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "END_TIME",
159                                              &endtime_str))
160   {
161     GNUNET_STRINGS_fancy_time_to_absolute (endtime_str, sensor->end_time);
162     GNUNET_free (endtime_str);
163   }
164   //interval
165   if (GNUNET_OK !=
166       GNUNET_CONFIGURATION_get_value_number (cfg, sectionname, "INTERVAL",
167                                              &time_sec))
168   {
169     LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor run interval\n"));
170     GNUNET_free (sensor);
171     return NULL;
172   }
173   if (time_sec < MIN_INTERVAL)
174   {
175     LOG (GNUNET_ERROR_TYPE_ERROR,
176          _("Sensor run interval too low (%" PRIu64 " < %d)\n"), time_sec,
177          MIN_INTERVAL);
178     GNUNET_free (sensor);
179     return NULL;
180   }
181   sensor->interval =
182       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, time_sec);
183   //lifetime
184   if (GNUNET_OK ==
185       GNUNET_CONFIGURATION_get_value_number (cfg, sectionname, "LIFETIME",
186                                              &time_sec))
187   {
188     sensor->lifetime =
189         GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, time_sec);
190     if (sensor->lifetime.rel_value_us < sensor->interval.rel_value_us)
191       LOG (GNUNET_ERROR_TYPE_WARNING,
192            "Lifetime of sensor data is preferred to be higher than interval for sensor `%s'.\n",
193            sensor->name);
194   }
195   else
196     sensor->lifetime = GNUNET_TIME_UNIT_FOREVER_REL;
197   //capabilities TODO
198   //source
199   if (GNUNET_OK !=
200       GNUNET_CONFIGURATION_get_value_choice (cfg, sectionname, "SOURCE",
201                                              sources,
202                                              (const char **) &sensor->source))
203   {
204     LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor source\n"));
205     GNUNET_free (sensor);
206     return NULL;
207   }
208   if (sources[0] == sensor->source)     //gnunet-statistics
209   {
210     if (GNUNET_OK !=
211         GNUNET_CONFIGURATION_get_value_string (cfg, sectionname,
212                                                "GNUNET_STAT_SERVICE",
213                                                &sensor->gnunet_stat_service) ||
214         GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, sectionname,
215                                                             "GNUNET_STAT_NAME",
216                                                             &sensor->gnunet_stat_name))
217     {
218       LOG (GNUNET_ERROR_TYPE_ERROR,
219            _("Error reading sensor gnunet-statistics source information\n"));
220       GNUNET_free (sensor);
221       return NULL;
222     }
223     sensor->gnunet_stat_get_handle = NULL;
224   }
225   else if (sources[1] == sensor->source)        //process
226   {
227     if (GNUNET_OK !=
228         GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "EXT_PROCESS",
229                                                &sensor->ext_process))
230     {
231       LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor process name\n"));
232       GNUNET_free (sensor);
233       return NULL;
234     }
235     GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "EXT_ARGS",
236                                            &sensor->ext_args);
237   }
238   //expected datatype
239   if (GNUNET_OK !=
240       GNUNET_CONFIGURATION_get_value_choice (cfg, sectionname,
241                                              "EXPECTED_DATATYPE", datatypes,
242                                              (const char **)
243                                              &sensor->expected_datatype))
244   {
245     LOG (GNUNET_ERROR_TYPE_ERROR,
246          _("Error reading sensor expected datatype\n"));
247     GNUNET_free (sensor);
248     return NULL;
249   }
250   if (sources[0] == sensor->source && datatypes[0] != sensor->expected_datatype)
251   {
252     LOG (GNUNET_ERROR_TYPE_ERROR,
253          _
254          ("Invalid expected datatype, gnunet-statistics returns uint64 values\n"));
255     GNUNET_free (sensor);
256     return NULL;
257   }
258   //reporting
259   sensor->collection_point = NULL;
260   sensor->report_values = GNUNET_NO;
261   sensor->report_anomalies = GNUNET_NO;
262   if (GNUNET_OK ==
263       GNUNET_CONFIGURATION_get_value_string (cfg, sectionname,
264                                              "COLLECTION_POINT", &dummy))
265   {
266     if (GNUNET_OK ==
267         GNUNET_CRYPTO_eddsa_public_key_from_string (dummy, strlen (dummy),
268                                                     &public_key))
269     {
270       sensor->collection_point = GNUNET_new (struct GNUNET_PeerIdentity);
271
272       sensor->collection_point->public_key = public_key;
273       if (GNUNET_OK ==
274           GNUNET_CONFIGURATION_get_value_number (cfg, sectionname,
275                                                  "VALUE_COLLECTION_INTERVAL",
276                                                  &time_sec))
277       {
278         sensor->report_values = GNUNET_YES;
279         sensor->value_reporting_interval =
280             GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, time_sec);
281       }
282       if (GNUNET_YES ==
283           GNUNET_CONFIGURATION_get_value_yesno (cfg, sectionname,
284                                                 "REPORT_ANOMALIES"))
285         sensor->report_anomalies = GNUNET_YES;
286     }
287     GNUNET_free (dummy);
288   }
289   //execution task
290   sensor->execution_task = GNUNET_SCHEDULER_NO_TASK;
291   //running
292   sensor->running = GNUNET_NO;
293   return sensor;
294 }
295
296
297 /**
298  * Load sensor definition from file
299  *
300  * @param filename full path to file containing sensor definition
301  */
302 static struct GNUNET_SENSOR_SensorInfo *
303 load_sensor_from_file (const char *filename)
304 {
305   struct GNUNET_CONFIGURATION_Handle *sensorcfg;
306   const char *filebasename;
307   struct GNUNET_SENSOR_SensorInfo *sensor;
308
309   //test file
310   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
311   {
312     LOG (GNUNET_ERROR_TYPE_ERROR, _("Failed to access sensor file: %s\n"),
313          filename);
314     return NULL;
315   }
316   //load file as configuration
317   sensorcfg = GNUNET_CONFIGURATION_create ();
318   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_parse (sensorcfg, filename))
319   {
320     GNUNET_CONFIGURATION_destroy (sensorcfg);
321     LOG (GNUNET_ERROR_TYPE_ERROR, _("Failed to load sensor definition: %s\n"),
322          filename);
323     return NULL;
324   }
325   //configuration section should be the same as filename
326   filebasename = GNUNET_STRINGS_get_short_name (filename);
327   sensor = load_sensor_from_cfg (sensorcfg, filebasename);
328   if (NULL == sensor)
329   {
330     GNUNET_CONFIGURATION_destroy (sensorcfg);
331     return NULL;
332   }
333   sensor->def_file = GNUNET_strdup (filename);
334   sensor->cfg = sensorcfg;
335   return sensor;
336 }
337
338
339 /**
340  * Given two version numbers as major and minor, compare them.
341  *
342  * @param v1_major First part of first version number
343  * @param v1_minor Second part of first version number
344  * @param v2_major First part of second version number
345  * @param v2_minor Second part of second version number
346  */
347 int
348 GNUNET_SENSOR_version_compare (uint16_t v1_major, uint16_t v1_minor,
349                                uint16_t v2_major, uint16_t v2_minor)
350 {
351   if (v1_major == v2_major)
352     return (v1_minor < v2_minor) ? -1 : (v1_minor > v2_minor);
353   else
354     return (v1_major < v2_major) ? -1 : (v1_major > v2_major);
355 }
356
357
358 /**
359  * Adds a new sensor to given hashmap.
360  * If the same name exist, compares versions and update if old.
361  *
362  * @param sensor Sensor structure to add
363  * @param map Hashmap to add to
364  * @return #GNUNET_YES if added
365  *         #GNUNET_NO if not added which is not necessarily an error
366  */
367 static int
368 add_sensor_to_hashmap (struct GNUNET_SENSOR_SensorInfo *sensor,
369                        struct GNUNET_CONTAINER_MultiHashMap *map)
370 {
371   struct GNUNET_HashCode key;
372   struct GNUNET_SENSOR_SensorInfo *existing;
373
374   GNUNET_CRYPTO_hash (sensor->name, strlen (sensor->name) + 1, &key);
375   existing = GNUNET_CONTAINER_multihashmap_get (map, &key);
376   if (NULL != existing)         //sensor with same name already exists
377   {
378     if (GNUNET_SENSOR_version_compare
379         (existing->version_major, existing->version_minor,
380          sensor->version_major, sensor->version_minor) >= 0)
381     {
382       LOG (GNUNET_ERROR_TYPE_INFO,
383            _("Sensor `%s' already exists with same or newer version\n"),
384            sensor->name);
385       return GNUNET_NO;
386     }
387     else
388     {
389       GNUNET_CONTAINER_multihashmap_remove (map, &key, existing);       //remove the old version
390       GNUNET_free (existing);
391       LOG (GNUNET_ERROR_TYPE_INFO, "Upgrading sensor `%s' to a newer version\n",
392            sensor->name);
393     }
394   }
395   if (GNUNET_SYSERR ==
396       GNUNET_CONTAINER_multihashmap_put (map, &key, sensor,
397                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
398   {
399     LOG (GNUNET_ERROR_TYPE_ERROR,
400          _("Error adding new sensor `%s' to global hashmap.\n"), sensor->name);
401     return GNUNET_NO;
402   }
403   return GNUNET_YES;
404 }
405
406
407 /**
408  * Iterating over files in sensors directory
409  *
410  * @param cls closure
411  * @param filename complete filename (absolute path)
412  * @return #GNUNET_OK to continue to iterate
413  */
414 static int
415 reload_sensors_dir_cb (void *cls, const char *filename)
416 {
417   struct GNUNET_CONTAINER_MultiHashMap *sensors = cls;
418   struct GNUNET_SENSOR_SensorInfo *sensor;
419
420   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
421     return GNUNET_OK;
422   sensor = load_sensor_from_file (filename);
423   if (NULL == sensor)
424   {
425     LOG (GNUNET_ERROR_TYPE_ERROR, _("Error loading sensor from file: %s\n"),
426          filename);
427     return GNUNET_OK;
428   }
429   if (GNUNET_YES != add_sensor_to_hashmap (sensor, sensors))
430     LOG (GNUNET_ERROR_TYPE_WARNING,
431          "Could not add sensor `%s' to global hashmap\n", sensor->name);
432   return GNUNET_OK;
433 }
434
435
436 /**
437  * Get path to the default directory containing the sensor definition files with
438  * a trailing directory separator.
439  *
440  * @return Default sensor files directory full path
441  */
442 char *
443 GNUNET_SENSOR_get_default_sensor_dir ()
444 {
445   char *datadir;
446   char *sensordir;
447
448   datadir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
449   GNUNET_asprintf (&sensordir, "%ssensors%s", datadir, DIR_SEPARATOR_STR);
450   GNUNET_free (datadir);
451   return sensordir;
452 }
453
454
455 /**
456  * Reads sensor definitions from given sensor directory.
457  *
458  * @param sensordir Path to sensor directory.
459  * @return a multihashmap of loaded sensors
460  */
461 struct GNUNET_CONTAINER_MultiHashMap *
462 GNUNET_SENSOR_load_all_sensors (char *sensor_dir)
463 {
464   struct GNUNET_CONTAINER_MultiHashMap *sensors;
465
466   GNUNET_assert (NULL != sensor_dir);
467   sensors = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
468   LOG (GNUNET_ERROR_TYPE_INFO,
469        "Loading sensor definitions from directory `%s'\n", sensor_dir);
470   GNUNET_assert (GNUNET_YES ==
471                  GNUNET_DISK_directory_test (sensor_dir, GNUNET_YES));
472   /* read all files in sensors directory */
473   GNUNET_DISK_directory_scan (sensor_dir, &reload_sensors_dir_cb, sensors);
474   LOG (GNUNET_ERROR_TYPE_INFO, "Loaded %d sensors from directory `%s'\n",
475        GNUNET_CONTAINER_multihashmap_size (sensors), sensor_dir);
476   return sensors;
477 }
478
479
480 /**
481  * Remove sensor execution from scheduler
482  *
483  * @param cls unused
484  * @param key hash of sensor name, key to hashmap
485  * @param value a `struct GNUNET_SENSOR_SensorInfo *`
486  * @return #GNUNET_YES if we should continue to
487  *         iterate,
488  *         #GNUNET_NO if not.
489  */
490 static int
491 destroy_sensor (void *cls, const struct GNUNET_HashCode *key, void *value)
492 {
493   struct GNUNET_SENSOR_SensorInfo *sensor = value;
494
495   if (GNUNET_SCHEDULER_NO_TASK != sensor->execution_task)
496   {
497     GNUNET_SCHEDULER_cancel (sensor->execution_task);
498     sensor->execution_task = GNUNET_SCHEDULER_NO_TASK;
499   }
500   if (NULL != sensor->gnunet_stat_get_handle)
501   {
502     GNUNET_STATISTICS_get_cancel (sensor->gnunet_stat_get_handle);
503     sensor->gnunet_stat_get_handle = NULL;
504   }
505   if (NULL != sensor->ext_cmd)
506   {
507     GNUNET_OS_command_stop (sensor->ext_cmd);
508     sensor->ext_cmd = NULL;
509   }
510   if (NULL != sensor->cfg)
511     GNUNET_CONFIGURATION_destroy (sensor->cfg);
512   if (NULL != sensor->name)
513     GNUNET_free (sensor->name);
514   if (NULL != sensor->def_file)
515     GNUNET_free (sensor->def_file);
516   if (NULL != sensor->description)
517     GNUNET_free (sensor->description);
518   if (NULL != sensor->category)
519     GNUNET_free (sensor->category);
520   if (NULL != sensor->capabilities)
521     GNUNET_free (sensor->capabilities);
522   if (NULL != sensor->gnunet_stat_service)
523     GNUNET_free (sensor->gnunet_stat_service);
524   if (NULL != sensor->gnunet_stat_name)
525     GNUNET_free (sensor->gnunet_stat_name);
526   if (NULL != sensor->ext_process)
527     GNUNET_free (sensor->ext_process);
528   if (NULL != sensor->ext_args)
529     GNUNET_free (sensor->ext_args);
530   if (NULL != sensor->collection_point)
531     GNUNET_free (sensor->collection_point);
532   GNUNET_free (sensor);
533   return GNUNET_YES;
534 }
535
536
537 /**
538  * Destroys a group of sensors in a hashmap and the hashmap itself
539  *
540  * @param sensors hashmap containing the sensors
541  */
542 void
543 GNUNET_SENSOR_destroy_sensors (struct GNUNET_CONTAINER_MultiHashMap *sensors)
544 {
545   LOG (GNUNET_ERROR_TYPE_DEBUG, "Destroying sensor list.\n");
546   GNUNET_CONTAINER_multihashmap_iterate (sensors, &destroy_sensor, NULL);
547   GNUNET_CONTAINER_multihashmap_destroy (sensors);
548 }