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/gnunet-service-sensor.c
23 * @brief sensor service implementation
24 * @author Omar Tarabai
27 #include "gnunet_util_lib.h"
31 * Structure containing sensor definition
42 * Path to definition file
47 * First part of version number
49 uint16_t version_major;
52 * Second part of version number
54 uint16_t version_minor;
62 * Category under which the sensor falls (e.g. tcp, datastore)
67 * When does the sensor become active
69 struct GNUNET_TIME_Absolute *start_time;
72 * When does the sensor expire
74 struct GNUNET_TIME_Absolute *end_time;
77 * Time interval to collect sensor information (e.g. every 1 min)
79 struct GNUNET_TIME_Relative *interval;
82 * Lifetime of an information sample after which it is deleted from storage
84 struct GNUNET_TIME_Relative *lifetime;
87 * A set of required peer capabilities for the sensor to collect meaningful information (e.g. ipv6)
92 * Either "gnunet-statistics" or external "process"
97 * Name of the GNUnet service that is the source for the gnunet-statistics entry
99 char *gnunet_stat_service;
102 * Name of the gnunet-statistics entry
104 char *gnunet_stat_name;
107 * Name of the external process to be executed
112 * Arguments to be passed to the external process
117 * The output datatype to be expected
119 char *expected_datatype;
122 * Peer-identity of peer running collection point
124 struct GNUNET_PeerIdentity *collection_point;
127 * Time interval to send sensor information to collection point (e.g. every 30 mins)
129 struct GNUNET_TIME_Relative *collection_interval;
132 * Flag specifying if value is to be communicated to the p2p network
137 * Time interval to communicate value to the p2p network
139 struct GNUNET_TIME_Relative *p2p_interval;
146 static const struct GNUNET_CONFIGURATION_Handle *cfg;
149 * Hashmap of loaded sensor definitions
151 struct GNUNET_CONTAINER_MultiHashMap *sensors;
154 * Task run during shutdown.
160 shutdown_task (void *cls,
161 const struct GNUNET_SCHEDULER_TaskContext *tc)
167 * A client disconnected. Remove all of its data structure entries.
169 * @param cls closure, NULL
170 * @param client identification of the client
173 handle_client_disconnect (void *cls,
174 struct GNUNET_SERVER_Client
180 * Parses a version number string into major and minor
182 * @param version full version string
183 * @param major pointer to parsed major value
184 * @param minor pointer to parsed minor value
185 * @return #GNUNET_OK if parsing went ok, #GNUNET_SYSERROR in case of error
188 version_parse(char *version, uint16_t *major, uint16_t *minor)
193 for(; isdigit(*version); version++)
196 majorval += *version - '0';
199 return GNUNET_SYSERR;
201 for(; isdigit(*version); version++)
204 minorval += *version - '0';
207 return GNUNET_SYSERR;
215 * Load sensor definition from configuration
217 * @param cfg configuration handle
218 * @param sectionname configuration section containing definition
220 static struct SensorInfo *
221 load_sensor_from_cfg(struct GNUNET_CONFIGURATION_Handle *cfg, const char *sectionname)
223 struct SensorInfo *sensor;
226 sensor = GNUNET_new(struct SensorInfo);
228 sensor->name = GNUNET_strdup(sectionname);
230 if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "VERSION", &versionstr) ||
233 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor version\n"));
237 if(GNUNET_OK != version_parse(versionstr, &(sensor->version_major), &(sensor->version_minor)))
239 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Invalid sensor version number, format should be major.minor\n"));
244 GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "DESCRIPTION", &sensor->description);
246 if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "CATEGORY", &sensor->category) ||
247 NULL == sensor->category)
249 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor category\n"));
258 * Load sensor definition from file
260 * @param filename full path to file containing sensor definition
262 static struct SensorInfo *
263 load_sensor_from_file(const char *filename)
265 struct GNUNET_CONFIGURATION_Handle *sensorcfg;
266 const char *filebasename;
267 struct SensorInfo *sensor;
270 if(GNUNET_YES != GNUNET_DISK_file_test(filename))
272 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to access sensor file: %s\n"), filename);
275 //load file as configuration
276 sensorcfg = GNUNET_CONFIGURATION_create();
277 if(GNUNET_SYSERR == GNUNET_CONFIGURATION_parse(sensorcfg, filename))
279 GNUNET_CONFIGURATION_destroy(sensorcfg);
280 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to load sensor definition: %s\n"), filename);
283 //configuration section should be the same as filename
284 filebasename = GNUNET_STRINGS_get_short_name(filename);
285 sensor = load_sensor_from_cfg(sensorcfg, filebasename);
286 sensor->def_file = GNUNET_strdup(filename);
288 GNUNET_CONFIGURATION_destroy(sensorcfg);
294 * Compares version numbers of two sensors
296 * @param s1 first sensor
297 * @param s2 second sensor
298 * @return 1: s1 > s2, 0: s1 == s2, -1: s1 < s2
301 sensor_version_compare(struct SensorInfo *s1, struct SensorInfo *s2)
303 if(s1->version_major == s2->version_major)
304 return (s1->version_minor < s2->version_minor) ? -1 : (s1->version_minor > s2->version_minor);
306 return (s1->version_major < s2->version_major) ? -1 : (s1->version_major > s2->version_major);
310 * Adds a new sensor to given hashmap.
311 * If the same name exist, compares versions and update if old.
313 * @param sensor Sensor structure to add
314 * @param map Hashmap to add to
315 * @return #GNUNET_YES if added, #GNUNET_NO if not added which is not necessarily an error
318 add_sensor_to_hashmap(struct SensorInfo *sensor, struct GNUNET_CONTAINER_MultiHashMap *map)
320 struct GNUNET_HashCode key;
321 struct SensorInfo *existing;
323 GNUNET_CRYPTO_hash(sensor->name, strlen(sensor->name), &key);
324 existing = GNUNET_CONTAINER_multihashmap_get(map, &key);
325 if(NULL != existing) //sensor with same name already exists
327 if(sensor_version_compare(existing, sensor) >= 0) //same or newer version already exist
329 GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Sensor `%s' already exists with same or newer version\n"), sensor->name);
334 GNUNET_CONTAINER_multihashmap_remove(map, &key, existing); //remove the old version
335 GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Upgrading sensor `%s' to a newer version\n"), sensor->name);
338 if(GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put(map, &key, sensor, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
340 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error adding new sensor `%s' to global hashmap, this should not happen\n"), sensor->name);
348 * Iterating over files in sensors directory
351 * @param filename complete filename (absolute path)
352 * @return #GNUNET_OK to continue to iterate,
353 * #GNUNET_NO to stop iteration with no error,
354 * #GNUNET_SYSERR to abort iteration with error!
357 reload_sensors_dir_cb(void *cls, const char *filename)
359 struct SensorInfo *sensor;
361 sensor = load_sensor_from_file(filename);
364 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error loading sensor from file: %s\n"), filename);
367 if(GNUNET_YES == add_sensor_to_hashmap(sensor, sensors))
368 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, _("Sensor `%s' added to global hashmap\n"), sensor->name);
370 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, ("Could not add sensor `%s' to global hashmap\n"), sensor->name);
376 * Get path to the directory containing the sensor definition files
378 * @return sensor files directory
386 datadir = GNUNET_OS_installation_get_path(GNUNET_OS_IPK_DATADIR);
387 GNUNET_asprintf(&sensordir, "%ssensors%s",
388 datadir, DIR_SEPARATOR_STR);
394 * Reads sensor definitions from data files
403 sensordir = get_sensor_dir();
404 GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Reloading sensor definitions from directory `%s'\n"), sensordir);
405 GNUNET_assert(GNUNET_YES == GNUNET_DISK_directory_test(sensordir, GNUNET_YES));
407 //read all files in sensors directory
408 filesfound = GNUNET_DISK_directory_scan(sensordir, &reload_sensors_dir_cb, NULL);
409 GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Loaded %d/%d sensors from directory `%s'\n"),
410 GNUNET_CONTAINER_multihashmap_size(sensors), filesfound, sensordir);
414 * Creates a structure with basic sensor info to be sent to a client
416 * @param sensor sensor information
417 * @return message ready to be sent to client
419 static struct SensorInfoMessage *
420 create_sensor_info_msg(struct SensorInfo *sensor)
422 struct SensorInfoMessage *msg;
428 name_len = strlen(sensor->name);
429 if(NULL == sensor->description)
432 desc_len = strlen(sensor->description);
434 len += sizeof(struct SensorInfoMessage);
437 msg = GNUNET_malloc(len);
438 msg->header.size = htons(len);
439 msg->header.type = htons(GNUNET_MESSAGE_TYPE_SENSOR_INFO);
440 msg->name_len = htons(name_len);
441 msg->description_len = htons(desc_len);
442 msg->version_major = htons(sensor->version_major);
443 msg->version_minor = htons(sensor->version_minor);
444 str_ptr = (char*) &msg[1];
445 memcpy(str_ptr, sensor->name, name_len);
446 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor name (%d): %.*s\n",
447 name_len, name_len, str_ptr);
449 memcpy(str_ptr, sensor->description, desc_len);
450 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor description (%d): %.*s\n",
451 desc_len, desc_len, str_ptr);
457 * Handle GET SENSOR message.
460 * @param client identification of the client
461 * @param message the actual message
464 handle_get_sensor (void *cls, struct GNUNET_SERVER_Client *client,
465 const struct GNUNET_MessageHeader *message)
467 struct GNUNET_SERVER_TransmitContext *tc;
469 size_t sensorname_len;
470 struct GNUNET_HashCode key;
471 struct SensorInfo *sensorinfo;
472 struct SensorInfoMessage *msg;
474 sensorname = (char *)&message[1];
475 sensorname_len = ntohs(message->size) - sizeof(struct GNUNET_MessageHeader);
476 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received for sensor (%d) `%.*s'\n",
477 "GET SENSOR", sensorname_len, sensorname_len, sensorname);
478 tc = GNUNET_SERVER_transmit_context_create (client);
479 GNUNET_CRYPTO_hash(sensorname, sensorname_len, &key);
480 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created key hash for requested sensor\n");
481 sensorinfo = (struct SensorInfo *)GNUNET_CONTAINER_multihashmap_get(sensors, &key);
482 if(NULL != sensorinfo)
484 msg = create_sensor_info_msg(sensorinfo);
485 GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
489 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Requested sensor `%.*s' was not found\n",
490 sensorname_len, sensorname);
491 GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
492 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
496 * Iterator for sensors and adds them to transmit context
498 * @param cls a 'struct GNUNET_SERVER_TransmitContext *'
499 * @param key hash of sensor name, key to hashmap
500 * @param value a 'struct SensorInfo *'
502 int add_sensor_to_tc(void *cls,
503 const struct GNUNET_HashCode *key, void *value)
505 struct GNUNET_SERVER_TransmitContext *tc = cls;
506 struct SensorInfo *sensorinfo = value;
507 struct SensorInfoMessage *msg;
509 msg = create_sensor_info_msg(sensorinfo);
510 GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
518 * Handle GET ALL SENSORS message.
521 * @param client identification of the client
522 * @param message the actual message
525 handle_get_all_sensors (void *cls, struct GNUNET_SERVER_Client *client,
526 const struct GNUNET_MessageHeader *message)
528 struct GNUNET_SERVER_TransmitContext *tc;
530 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received.\n",
532 tc = GNUNET_SERVER_transmit_context_create (client);
533 GNUNET_CONTAINER_multihashmap_iterate(sensors, &add_sensor_to_tc, tc);
534 GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
535 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
539 * Process statistics requests.
542 * @param server the initialized server
543 * @param c configuration to use
547 struct GNUNET_SERVER_Handle *server,
548 const struct GNUNET_CONFIGURATION_Handle *c)
550 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
551 {&handle_get_sensor, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GET,
553 {&handle_get_all_sensors, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GETALL,
554 sizeof (struct GNUNET_MessageHeader)},
559 sensors = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
561 GNUNET_SERVER_add_handlers (server, handlers);
562 GNUNET_SERVER_disconnect_notify (server,
563 &handle_client_disconnect,
565 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
572 * The main function for the sensor service.
574 * @param argc number of arguments from the command line
575 * @param argv command line arguments
576 * @return 0 ok, 1 on error
579 main (int argc, char *const *argv)
582 GNUNET_SERVICE_run (argc,
585 GNUNET_SERVICE_OPTION_NONE,
586 &run, NULL)) ? 0 : 1;
589 /* end of gnunet-service-sensor.c */