2 This file is part of GNUnet.
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.
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.
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.
22 * @file sensor/sensor_util_lib.c
23 * @brief senor utilities
24 * @author Omar Tarabai
28 #include "gnunet_util_lib.h"
29 #include "gnunet_sensor_util_lib.h"
30 #include "gnunet_statistics_service.h"
32 #define LOG(kind,...) GNUNET_log_from (kind, "sensor-util",__VA_ARGS__)
35 * Minimum sensor execution interval (in seconds)
37 #define MIN_INTERVAL 30
40 * Supported sources of sensor information
42 static const char *sources[] = { "gnunet-statistics", "process", NULL };
45 * Supported datatypes of sensor information
47 static const char *datatypes[] = { "numeric", "string", NULL };
50 * Parses a version number string into major and minor
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
58 version_parse (char *version, uint16_t * major, uint16_t * minor)
63 for (; isdigit (*version); version++)
66 majorval += *version - '0';
71 for (; isdigit (*version); version++)
74 minorval += *version - '0';
86 * Load sensor definition from configuration
88 * @param cfg configuration handle
89 * @param sectionname configuration section containing definition
91 static struct GNUNET_SENSOR_SensorInfo *
92 load_sensor_from_cfg (struct GNUNET_CONFIGURATION_Handle *cfg,
93 const char *sectionname)
95 struct GNUNET_SENSOR_SensorInfo *sensor;
99 unsigned long long time_sec;
101 struct GNUNET_CRYPTO_EddsaPublicKey public_key;
103 sensor = GNUNET_new (struct GNUNET_SENSOR_SensorInfo);
106 sensor->name = GNUNET_strdup (sectionname);
109 GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "VERSION",
112 LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor version\n"));
113 GNUNET_free (sensor);
117 version_parse (version_str, &(sensor->version_major),
118 &(sensor->version_minor)))
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);
126 GNUNET_free (version_str);
128 GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "DESCRIPTION",
129 &sensor->description);
132 GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "CATEGORY",
133 &sensor->category) ||
134 NULL == sensor->category)
136 LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor category\n"));
137 GNUNET_free (sensor);
142 GNUNET_CONFIGURATION_get_value_yesno (cfg, sectionname, "ENABLED"))
143 sensor->enabled = GNUNET_NO;
145 sensor->enabled = GNUNET_YES;
147 sensor->start_time = NULL;
149 GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "START_TIME",
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);
158 sensor->end_time = NULL;
160 GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "END_TIME",
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);
170 GNUNET_CONFIGURATION_get_value_number (cfg, sectionname, "INTERVAL",
173 LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor run interval\n"));
174 GNUNET_free (sensor);
177 if (time_sec < MIN_INTERVAL)
179 LOG (GNUNET_ERROR_TYPE_ERROR,
180 _("Sensor run interval too low (%" PRIu64 " < %d)\n"), time_sec,
182 GNUNET_free (sensor);
186 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, time_sec);
189 GNUNET_CONFIGURATION_get_value_number (cfg, sectionname, "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",
200 sensor->lifetime = GNUNET_TIME_UNIT_FOREVER_REL;
204 GNUNET_CONFIGURATION_get_value_choice (cfg, sectionname, "SOURCE",
206 (const char **) &sensor->source))
208 LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor source\n"));
209 GNUNET_free (sensor);
212 if (sources[0] == sensor->source) //gnunet-statistics
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,
220 &sensor->gnunet_stat_name))
222 LOG (GNUNET_ERROR_TYPE_ERROR,
223 _("Error reading sensor gnunet-statistics source information\n"));
224 GNUNET_free (sensor);
227 sensor->gnunet_stat_get_handle = NULL;
229 else if (sources[1] == sensor->source) //process
232 GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "EXT_PROCESS",
233 &sensor->ext_process))
235 LOG (GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor process name\n"));
236 GNUNET_free (sensor);
239 GNUNET_CONFIGURATION_get_value_string (cfg, sectionname, "EXT_ARGS",
244 GNUNET_CONFIGURATION_get_value_choice (cfg, sectionname,
245 "EXPECTED_DATATYPE", datatypes,
247 &sensor->expected_datatype))
249 LOG (GNUNET_ERROR_TYPE_ERROR,
250 _("Error reading sensor expected datatype\n"));
251 GNUNET_free (sensor);
254 if (sources[0] == sensor->source && datatypes[0] != sensor->expected_datatype)
256 LOG (GNUNET_ERROR_TYPE_ERROR,
258 ("Invalid expected datatype, gnunet-statistics returns uint64 values\n"));
259 GNUNET_free (sensor);
262 //reporting mechanism
263 sensor->collection_point = NULL;
265 GNUNET_CONFIGURATION_get_value_string (cfg, sectionname,
266 "COLLECTION_POINT", &dummy))
269 GNUNET_CONFIGURATION_get_value_number (cfg, sectionname,
270 "COLLECTION_INTERVAL",
273 LOG (GNUNET_ERROR_TYPE_ERROR,
274 _("Error reading sensor collection interval\n"));
278 sensor->collection_interval =
279 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, time_sec);
281 GNUNET_CRYPTO_eddsa_public_key_from_string (dummy, strlen (dummy),
284 sensor->collection_point = GNUNET_new (struct GNUNET_PeerIdentity);
286 sensor->collection_point->public_key = public_key;
291 sensor->p2p_report = GNUNET_NO;
293 GNUNET_CONFIGURATION_get_value_yesno (cfg, sectionname, "P2P_REPORT"))
296 GNUNET_CONFIGURATION_get_value_number (cfg, sectionname, "P2P_INTERVAL",
299 LOG (GNUNET_ERROR_TYPE_ERROR,
300 _("Error reading sensor p2p reporting interval\n"));
304 sensor->p2p_interval =
305 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, time_sec);
306 sensor->p2p_report = GNUNET_YES;
310 sensor->execution_task = GNUNET_SCHEDULER_NO_TASK;
312 sensor->running = GNUNET_NO;
319 * Load sensor definition from file
321 * @param filename full path to file containing sensor definition
323 static struct GNUNET_SENSOR_SensorInfo *
324 load_sensor_from_file (const char *filename)
326 struct GNUNET_CONFIGURATION_Handle *sensorcfg;
327 const char *filebasename;
328 struct GNUNET_SENSOR_SensorInfo *sensor;
331 if (GNUNET_YES != GNUNET_DISK_file_test (filename))
333 LOG (GNUNET_ERROR_TYPE_ERROR, _("Failed to access sensor file: %s\n"),
337 //load file as configuration
338 sensorcfg = GNUNET_CONFIGURATION_create ();
339 if (GNUNET_SYSERR == GNUNET_CONFIGURATION_parse (sensorcfg, filename))
341 GNUNET_CONFIGURATION_destroy (sensorcfg);
342 LOG (GNUNET_ERROR_TYPE_ERROR, _("Failed to load sensor definition: %s\n"),
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);
351 GNUNET_CONFIGURATION_destroy (sensorcfg);
354 sensor->def_file = GNUNET_strdup (filename);
355 sensor->cfg = sensorcfg;
361 * Given two version numbers as major and minor, compare them.
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
369 GNUNET_SENSOR_version_compare (uint16_t v1_major, uint16_t v1_minor,
370 uint16_t v2_major, uint16_t v2_minor)
372 if (v1_major == v2_major)
373 return (v1_minor < v2_minor) ? -1 : (v1_minor > v2_minor);
375 return (v1_major < v2_major) ? -1 : (v1_major > v2_major);
380 * Adds a new sensor to given hashmap.
381 * If the same name exist, compares versions and update if old.
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
389 add_sensor_to_hashmap (struct GNUNET_SENSOR_SensorInfo *sensor,
390 struct GNUNET_CONTAINER_MultiHashMap *map)
392 struct GNUNET_HashCode key;
393 struct GNUNET_SENSOR_SensorInfo *existing;
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
399 if (GNUNET_SENSOR_version_compare
400 (existing->version_major, existing->version_minor,
401 sensor->version_major, sensor->version_minor) >= 0)
403 LOG (GNUNET_ERROR_TYPE_INFO,
404 _("Sensor `%s' already exists with same or newer version\n"),
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",
417 GNUNET_CONTAINER_multihashmap_put (map, &key, sensor,
418 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
420 LOG (GNUNET_ERROR_TYPE_ERROR,
421 _("Error adding new sensor `%s' to global hashmap.\n"), sensor->name);
429 * Iterating over files in sensors directory
432 * @param filename complete filename (absolute path)
433 * @return #GNUNET_OK to continue to iterate
436 reload_sensors_dir_cb (void *cls, const char *filename)
438 struct GNUNET_CONTAINER_MultiHashMap *sensors = cls;
439 struct GNUNET_SENSOR_SensorInfo *sensor;
441 if (GNUNET_YES != GNUNET_DISK_file_test (filename))
443 sensor = load_sensor_from_file (filename);
446 LOG (GNUNET_ERROR_TYPE_ERROR, _("Error loading sensor from file: %s\n"),
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);
458 * Get path to the directory containing the sensor definition files with a
459 * trailing directory separator.
461 * @return sensor files directory full path
464 GNUNET_SENSOR_get_sensor_dir ()
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);
477 * Reads sensor definitions from local data files
479 * @return a multihashmap of loaded sensors
481 struct GNUNET_CONTAINER_MultiHashMap *
482 GNUNET_SENSOR_load_all_sensors ()
485 struct GNUNET_CONTAINER_MultiHashMap *sensors;
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));
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);
504 * Remove sensor execution from scheduler
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
514 destroy_sensor (void *cls, const struct GNUNET_HashCode *key, void *value)
516 struct GNUNET_SENSOR_SensorInfo *sensor = value;
518 if (GNUNET_SCHEDULER_NO_TASK != sensor->execution_task)
520 GNUNET_SCHEDULER_cancel (sensor->execution_task);
521 sensor->execution_task = GNUNET_SCHEDULER_NO_TASK;
523 if (NULL != sensor->gnunet_stat_get_handle)
525 GNUNET_STATISTICS_get_cancel (sensor->gnunet_stat_get_handle);
526 sensor->gnunet_stat_get_handle = NULL;
528 if (NULL != sensor->ext_cmd)
530 GNUNET_OS_command_stop (sensor->ext_cmd);
531 sensor->ext_cmd = NULL;
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);
561 * Destroys a group of sensors in a hashmap and the hashmap itself
563 * @param sensors hashmap containing the sensors
566 GNUNET_SENSOR_destroy_sensors (struct GNUNET_CONTAINER_MultiHashMap *sensors)
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);