warning fix
[oweals/gnunet.git] / src / sensor / gnunet-service-sensor.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/gnunet-service-sensor.c
23  * @brief sensor service implementation
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "sensor.h"
29
30 /**
31  * Structure containing sensor definition
32  */
33 struct SensorInfo
34 {
35
36   /*
37    * Sensor name
38    */
39   char *name;
40
41   /*
42    * Path to definition file
43    */
44   char *def_file;
45
46   /*
47    * First part of version number
48    */
49   uint16_t version_major;
50
51   /*
52    * Second part of version number
53    */
54   uint16_t version_minor;
55
56   /*
57    * Sensor description
58    */
59   char *description;
60
61   /*
62    * Category under which the sensor falls (e.g. tcp, datastore)
63    */
64   char *category;
65
66   /*
67    * When does the sensor become active
68    */
69   struct GNUNET_TIME_Absolute *start_time;
70
71   /*
72    * When does the sensor expire
73    */
74   struct GNUNET_TIME_Absolute *end_time;
75
76   /*
77    * Time interval to collect sensor information (e.g. every 1 min)
78    */
79   struct GNUNET_TIME_Relative *interval;
80
81   /*
82    * Lifetime of an information sample after which it is deleted from storage
83    */
84   struct GNUNET_TIME_Relative *lifetime;
85
86   /*
87    * A set of required peer capabilities for the sensor to collect meaningful information (e.g. ipv6)
88    */
89   char *capabilities;
90
91   /*
92    * Either "gnunet-statistics" or external "process"
93    */
94   char *source;
95
96   /*
97    * Name of the GNUnet service that is the source for the gnunet-statistics entry
98    */
99   char *gnunet_stat_service;
100
101   /*
102    * Name of the gnunet-statistics entry
103    */
104   char *gnunet_stat_name;
105
106   /*
107    * Name of the external process to be executed
108    */
109   char *ext_process;
110
111   /*
112    * Arguments to be passed to the external process
113    */
114   char *ext_args;
115
116   /*
117    * The output datatype to be expected
118    */
119   char *expected_datatype;
120
121   /*
122    * Peer-identity of peer running collection point
123    */
124   struct GNUNET_PeerIdentity *collection_point;
125
126   /*
127    * Time interval to send sensor information to collection point (e.g. every 30 mins)
128    */
129   struct GNUNET_TIME_Relative *collection_interval;
130
131   /*
132    * Flag specifying if value is to be communicated to the p2p network
133    */
134   int p2p_report;
135
136   /*
137    * Time interval to communicate value to the p2p network
138    */
139   struct GNUNET_TIME_Relative *p2p_interval;
140
141 };
142
143 /**
144  * Our configuration.
145  */
146 static const struct GNUNET_CONFIGURATION_Handle *cfg;
147
148 /**
149  * Hashmap of loaded sensor definitions
150  */
151 struct GNUNET_CONTAINER_MultiHashMap *sensors;
152
153 /**
154  * Task run during shutdown.
155  *
156  * @param cls unused
157  * @param tc unused
158  */
159 static void
160 shutdown_task (void *cls,
161                const struct GNUNET_SCHEDULER_TaskContext *tc)
162 {
163 }
164
165
166 /**
167  * A client disconnected.  Remove all of its data structure entries.
168  *
169  * @param cls closure, NULL
170  * @param client identification of the client
171  */
172 static void
173 handle_client_disconnect (void *cls,
174                           struct GNUNET_SERVER_Client
175                           * client)
176 {
177 }
178
179 /**
180  * Parses a version number string into major and minor
181  *
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
186  */
187 static int
188 version_parse(char *version, uint16_t *major, uint16_t *minor)
189 {
190   int majorval = 0;
191   int minorval = 0;
192
193   for(; isdigit(*version); version++)
194   {
195     majorval *= 10;
196     majorval += *version - '0';
197   }
198   if(*version != '.')
199     return GNUNET_SYSERR;
200   version++;
201   for(; isdigit(*version); version++)
202   {
203     minorval *= 10;
204     minorval += *version - '0';
205   }
206   if(*version != 0)
207     return GNUNET_SYSERR;
208   *major = majorval;
209   *minor = minorval;
210
211   return GNUNET_OK;
212 }
213
214 /**
215  * Load sensor definition from configuration
216  *
217  * @param cfg configuration handle
218  * @param sectionname configuration section containing definition
219  */
220 static struct SensorInfo *
221 load_sensor_from_cfg(struct GNUNET_CONFIGURATION_Handle *cfg, const char *sectionname)
222 {
223   struct SensorInfo *sensor;
224   char *versionstr;
225
226   sensor = GNUNET_new(struct SensorInfo);
227   //name
228   sensor->name = GNUNET_strdup(sectionname);
229   //version
230   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "VERSION", &versionstr) ||
231       NULL == versionstr)
232   {
233     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor version\n"));
234     GNUNET_free(sensor);
235     return NULL;
236   }
237   if(GNUNET_OK != version_parse(versionstr, &(sensor->version_major), &(sensor->version_minor)))
238   {
239     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Invalid sensor version number, format should be major.minor\n"));
240     GNUNET_free(sensor);
241     return NULL;
242   }
243   //description
244   GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "DESCRIPTION", &sensor->description);
245   //category
246   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "CATEGORY", &sensor->category) ||
247         NULL == sensor->category)
248   {
249     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor category\n"));
250     GNUNET_free(sensor);
251     return NULL;
252   }
253
254   return sensor;
255 }
256
257 /**
258  * Load sensor definition from file
259  *
260  * @param filename full path to file containing sensor definition
261  */
262 static struct SensorInfo *
263 load_sensor_from_file(const char *filename)
264 {
265   struct GNUNET_CONFIGURATION_Handle *sensorcfg;
266   const char *filebasename;
267   struct SensorInfo *sensor;
268
269   //test file
270   if(GNUNET_YES != GNUNET_DISK_file_test(filename))
271   {
272     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to access sensor file: %s\n"), filename);
273     return NULL;
274   }
275   //load file as configuration
276   sensorcfg = GNUNET_CONFIGURATION_create();
277   if(GNUNET_SYSERR == GNUNET_CONFIGURATION_parse(sensorcfg, filename))
278   {
279     GNUNET_CONFIGURATION_destroy(sensorcfg);
280     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to load sensor definition: %s\n"), filename);
281     return NULL;
282   }
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);
287
288   GNUNET_CONFIGURATION_destroy(sensorcfg);
289
290   return sensor;
291 }
292
293 /**
294  * Compares version numbers of two sensors
295  *
296  * @param s1 first sensor
297  * @param s2 second sensor
298  * @return 1: s1 > s2, 0: s1 == s2, -1: s1 < s2
299  */
300 static int
301 sensor_version_compare(struct SensorInfo *s1, struct SensorInfo *s2)
302 {
303   if(s1->version_major == s2->version_major)
304     return (s1->version_minor < s2->version_minor) ? -1 : (s1->version_minor > s2->version_minor);
305   else
306     return (s1->version_major < s2->version_major) ? -1 : (s1->version_major > s2->version_major);
307 }
308
309 /**
310  * Adds a new sensor to given hashmap.
311  * If the same name exist, compares versions and update if old.
312  *
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
316  */
317 static int
318 add_sensor_to_hashmap(struct SensorInfo *sensor, struct GNUNET_CONTAINER_MultiHashMap *map)
319 {
320   struct GNUNET_HashCode key;
321   struct SensorInfo *existing;
322
323   GNUNET_CRYPTO_hash(sensor->name, sizeof(sensor->name), &key);
324   existing = GNUNET_CONTAINER_multihashmap_get(map, &key);
325   if(NULL != existing) //sensor with same name already exists
326   {
327     if(sensor_version_compare(existing, sensor) >= 0) //same or newer version already exist
328     {
329       GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Sensor `%s' already exists with same or newer version\n"), sensor->name);
330       return GNUNET_NO;
331     }
332     else
333     {
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);
336     }
337   }
338   if(GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put(map, &key, sensor, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
339   {
340     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error adding new sensor `%s' to global hashmap, this should not happen\n"), sensor->name);
341     return GNUNET_NO;
342   }
343
344   return GNUNET_YES;
345 }
346
347 /**
348  * Iterating over files in sensors directory
349  *
350  * @param cls closure
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!
355  */
356 static int
357 reload_sensors_dir_cb(void *cls, const char *filename)
358 {
359   struct SensorInfo *sensor;
360
361   sensor = load_sensor_from_file(filename);
362   if(NULL == sensor)
363   {
364     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error loading sensor from file: %s\n"), filename);
365     return GNUNET_OK;
366   }
367   if(GNUNET_YES == add_sensor_to_hashmap(sensor, sensors))
368     GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Sensor `%s' added to global hashmap\n"), sensor->name);
369   else
370     GNUNET_log(GNUNET_ERROR_TYPE_INFO, ("Could not add sensor `%s' to global hashmap\n"), sensor->name);
371
372   return GNUNET_OK;
373 }
374
375 /*
376  * Get path to the directory containing the sensor definition files
377  *
378  * @return sensor files directory
379  */
380 static char *
381 get_sensor_dir()
382 {
383   char* datadir;
384   char* sensordir;
385
386   datadir = GNUNET_OS_installation_get_path(GNUNET_OS_IPK_DATADIR);
387   GNUNET_asprintf(&sensordir, "%ssensors%s",
388       datadir, DIR_SEPARATOR_STR);
389
390   return sensordir;
391 }
392
393 /**
394  * Reads sensor definitions from data files
395  *
396  */
397 static void
398 reload_sensors()
399 {
400   char* sensordir;
401   int filesfound;
402
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));
406
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);
411 }
412
413 /**
414  * Creates a structure with basic sensor info to be sent to a client
415  *
416  * @parm sensor sensor information
417  * @return message ready to be sent to client
418  */
419 static struct SensorInfoMessage *
420 create_sensor_info_msg(struct SensorInfo *sensor)
421 {
422   struct SensorInfoMessage *msg;
423   uint16_t len;
424   size_t name_len;
425   size_t desc_len;
426
427   name_len = strlen(sensor->name);
428   if(NULL == sensor->description)
429     desc_len = 0;
430   else
431     desc_len = strlen(sensor->description);
432   len = 0;
433   len += sizeof(struct SensorInfoMessage);
434   len += name_len;
435   len += desc_len;
436   msg = GNUNET_malloc(len);
437   msg->header.size = htons(len);
438   msg->header.type = htons(GNUNET_MESSAGE_TYPE_SENSOR_INFO);
439   msg->name_len = htons(name_len);
440   msg->description_len = htons(desc_len);
441   msg->version_major = htons(sensor->version_major);
442   msg->version_minor = htons(sensor->version_minor);
443   memcpy(&msg[1], sensor->name, name_len);
444   memcpy((&msg[1]) + name_len, sensor->description, desc_len);
445
446   return msg;
447 }
448
449 /**
450  * Handle GET SENSOR message.
451  *
452  * @param cls closure
453  * @param client identification of the client
454  * @param message the actual message
455  */
456 static void
457 handle_get_sensor (void *cls, struct GNUNET_SERVER_Client *client,
458             const struct GNUNET_MessageHeader *message)
459 {
460   struct GNUNET_SERVER_TransmitContext *tc;
461   char *sensorname;
462   size_t sensorname_len;
463   struct GNUNET_HashCode key;
464   struct SensorInfo *sensorinfo;
465   struct SensorInfoMessage *msg;
466
467   sensorname = (char *)&message[1];
468   sensorname_len = message->size - sizeof(struct GNUNET_MessageHeader);
469   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received for sensor `%.*s\n",
470               "GET SENSOR", sensorname_len, sensorname);
471   tc = GNUNET_SERVER_transmit_context_create (client);
472   GNUNET_CRYPTO_hash(sensorname, sensorname_len, &key);
473   sensorinfo = (struct SensorInfo *)GNUNET_CONTAINER_multihashmap_get(sensors, &key);
474   msg = create_sensor_info_msg(sensorinfo);
475   GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
476   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
477
478   GNUNET_free(msg);
479 }
480
481 /**
482  * Iterator for sensors and adds them to transmit context
483  *
484  * @param cls a 'struct GNUNET_SERVER_TransmitContext *'
485  * @param key hash of sensor name, key to hashmap
486  * @param value a 'struct SensorInfo *'
487  */
488 int add_sensor_to_tc(void *cls,
489     const struct GNUNET_HashCode *key, void *value)
490 {
491   struct GNUNET_SERVER_TransmitContext *tc = cls;
492   struct SensorInfo *sensorinfo = value;
493   struct SensorInfoMessage *msg;
494
495   msg = create_sensor_info_msg(sensorinfo);
496   GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
497
498   GNUNET_free(msg);
499
500   return GNUNET_YES;
501 }
502
503 /**
504  * Handle GET ALL SENSORS message.
505  *
506  * @param cls closure
507  * @param client identification of the client
508  * @param message the actual message
509  */
510 static void
511 handle_get_all_sensors (void *cls, struct GNUNET_SERVER_Client *client,
512             const struct GNUNET_MessageHeader *message)
513 {
514   struct GNUNET_SERVER_TransmitContext *tc;
515
516   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received.\n",
517                 "GET ALL SENSOR");
518   tc = GNUNET_SERVER_transmit_context_create (client);
519   GNUNET_CONTAINER_multihashmap_iterate(sensors, &add_sensor_to_tc, tc);
520   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
521   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
522 }
523
524 /**
525  * Process statistics requests.
526  *
527  * @param cls closure
528  * @param server the initialized server
529  * @param c configuration to use
530  */
531 static void
532 run (void *cls,
533      struct GNUNET_SERVER_Handle *server,
534      const struct GNUNET_CONFIGURATION_Handle *c)
535 {
536   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
537     {&handle_get_sensor, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GET,
538      sizeof (struct GNUNET_MessageHeader)},
539     {&handle_get_all_sensors, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GETALL,
540      0},
541     {NULL, NULL, 0, 0}
542   };
543
544   cfg = c;
545   sensors = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
546   reload_sensors();
547   GNUNET_SERVER_add_handlers (server, handlers);
548   GNUNET_SERVER_disconnect_notify (server, 
549                                    &handle_client_disconnect,
550                                    NULL);
551   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
552                                 &shutdown_task,
553                                 NULL);
554 }
555
556
557 /**
558  * The main function for the sensor service.
559  *
560  * @param argc number of arguments from the command line
561  * @param argv command line arguments
562  * @return 0 ok, 1 on error
563  */
564 int
565 main (int argc, char *const *argv)
566 {
567   return (GNUNET_OK ==
568           GNUNET_SERVICE_run (argc,
569                               argv,
570                               "sensor",
571                               GNUNET_SERVICE_OPTION_NONE,
572                               &run, NULL)) ? 0 : 1;
573 }
574
575 /* end of gnunet-service-sensor.c */