sensor: using data lifetime value
[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 Omar Tarabai
25  */
26 #include <inttypes.h>
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "sensor.h"
30 #include "gnunet_statistics_service.h"
31 #include "gnunet_peerstore_service.h"
32
33 /**
34  * Minimum sensor execution interval (in seconds)
35  */
36 #define MIN_INTERVAL 30
37
38 /**
39  * Structure containing sensor definition
40  */
41 struct SensorInfo
42 {
43
44   /**
45    * The configuration handle
46    * carrying sensor information
47    */
48   struct GNUNET_CONFIGURATION_Handle *cfg;
49
50   /*
51    * Sensor name
52    */
53   char *name;
54
55   /*
56    * Path to definition file
57    */
58   char *def_file;
59
60   /*
61    * First part of version number
62    */
63   uint16_t version_major;
64
65   /*
66    * Second part of version number
67    */
68   uint16_t version_minor;
69
70   /*
71    * Sensor description
72    */
73   char *description;
74
75   /*
76    * Sensor currently enabled
77    */
78   int enabled;
79
80   /*
81    * Category under which the sensor falls (e.g. tcp, datastore)
82    */
83   char *category;
84
85   /*
86    * When does the sensor become active
87    */
88   struct GNUNET_TIME_Absolute *start_time;
89
90   /*
91    * When does the sensor expire
92    */
93   struct GNUNET_TIME_Absolute *end_time;
94
95   /*
96    * Time interval to collect sensor information (e.g. every 1 min)
97    */
98   struct GNUNET_TIME_Relative interval;
99
100   /*
101    * Lifetime of an information sample after which it is deleted from storage
102    * If not supplied, will default to the interval value
103    */
104   struct GNUNET_TIME_Relative lifetime;
105
106   /*
107    * A set of required peer capabilities for the sensor to collect meaningful information (e.g. ipv6)
108    */
109   char *capabilities;
110
111   /*
112    * Either "gnunet-statistics" or external "process"
113    */
114   char *source;
115
116   /*
117    * Name of the GNUnet service that is the source for the gnunet-statistics entry
118    */
119   char *gnunet_stat_service;
120
121   /*
122    * Name of the gnunet-statistics entry
123    */
124   char *gnunet_stat_name;
125
126   /**
127    * Handle to statistics get request (OR GNUNET_SCHEDULER_NO_TASK)
128    */
129   struct GNUNET_STATISTICS_GetHandle *gnunet_stat_get_handle;
130
131   /*
132    * Name of the external process to be executed
133    */
134   char *ext_process;
135
136   /*
137    * Arguments to be passed to the external process
138    */
139   char *ext_args;
140
141   /*
142    * Handle to the external process
143    */
144   struct GNUNET_OS_CommandHandle *ext_cmd;
145
146   /*
147    * Did we already receive a value
148    * from the currently running external
149    * proccess ? #GNUNET_YES / #GNUNET_NO
150    */
151   int ext_cmd_value_received;
152
153   /*
154    * The output datatype to be expected
155    */
156   char *expected_datatype;
157
158   /*
159    * Peer-identity of peer running collection point
160    */
161   struct GNUNET_PeerIdentity *collection_point;
162
163   /*
164    * Time interval to send sensor information to collection point (e.g. every 30 mins)
165    */
166   struct GNUNET_TIME_Relative *collection_interval;
167
168   /*
169    * Flag specifying if value is to be communicated to the p2p network
170    */
171   int p2p_report;
172
173   /*
174    * Time interval to communicate value to the p2p network
175    */
176   struct GNUNET_TIME_Relative *p2p_interval;
177
178   /*
179    * Execution task (OR GNUNET_SCHEDULER_NO_TASK)
180    */
181   GNUNET_SCHEDULER_TaskIdentifier execution_task;
182
183   /*
184    * Is the sensor being executed
185    */
186   int running;
187
188 };
189
190 /**
191  * Our configuration.
192  */
193 static const struct GNUNET_CONFIGURATION_Handle *cfg;
194
195 /**
196  * Hashmap of loaded sensor definitions
197  */
198 struct GNUNET_CONTAINER_MultiHashMap *sensors;
199
200 /**
201  * Supported sources of sensor information
202  */
203 static const char *sources[] = { "gnunet-statistics", "process", NULL };
204
205 /**
206  * Supported datatypes of sensor information
207  */
208 static const char *datatypes[] = { "uint64", "double", "string", NULL };
209
210 /**
211  * Handle to statistics service
212  */
213 struct GNUNET_STATISTICS_Handle *statistics;
214
215 /**
216  * Handle to peerstore service
217  */
218 struct GNUNET_PEERSTORE_Handle *peerstore;
219
220 /**
221  * Service name
222  */
223 char *subsystem = "sensor";
224
225 /**
226  * My peer id
227  */
228 struct GNUNET_PeerIdentity peerid;
229
230 /**
231  * Remove sensor execution from scheduler
232  *
233  * @param cls unused
234  * @param key hash of sensor name, key to hashmap
235  * @param value a 'struct SensorInfo *'
236  * @return #GNUNET_YES if we should continue to
237  *         iterate,
238  *         #GNUNET_NO if not.
239  */
240 static int destroy_sensor(void *cls,
241     const struct GNUNET_HashCode *key, void *value)
242 {
243   struct SensorInfo *sensorinfo = value;
244
245   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Destroying sensor `%s'\n", sensorinfo->name);
246   if(GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
247   {
248     GNUNET_SCHEDULER_cancel(sensorinfo->execution_task);
249     sensorinfo->execution_task = GNUNET_SCHEDULER_NO_TASK;
250   }
251   if(NULL != sensorinfo->gnunet_stat_get_handle)
252   {
253     GNUNET_STATISTICS_get_cancel(sensorinfo->gnunet_stat_get_handle);
254     sensorinfo->gnunet_stat_get_handle = NULL;
255   }
256   if(NULL != sensorinfo->ext_cmd)
257   {
258     GNUNET_OS_command_stop(sensorinfo->ext_cmd);
259     sensorinfo->ext_cmd = NULL;
260   }
261   if(NULL != sensorinfo->cfg)
262     GNUNET_CONFIGURATION_destroy(sensorinfo->cfg);
263   if(NULL != sensorinfo->name)
264     GNUNET_free(sensorinfo->name);
265   if(NULL != sensorinfo->def_file)
266     GNUNET_free(sensorinfo->def_file);
267   if(NULL != sensorinfo->description)
268     GNUNET_free(sensorinfo->description);
269   if(NULL != sensorinfo->category)
270     GNUNET_free(sensorinfo->category);
271   if(NULL != sensorinfo->capabilities)
272     GNUNET_free(sensorinfo->capabilities);
273   if(NULL != sensorinfo->gnunet_stat_service)
274     GNUNET_free(sensorinfo->gnunet_stat_service);
275   if(NULL != sensorinfo->gnunet_stat_name)
276     GNUNET_free(sensorinfo->gnunet_stat_name);
277   if(NULL != sensorinfo->ext_process)
278     GNUNET_free(sensorinfo->ext_process);
279   if(NULL != sensorinfo->ext_args)
280     GNUNET_free(sensorinfo->ext_args);
281   GNUNET_free(sensorinfo);
282   return GNUNET_YES;
283 }
284
285 /**
286  * Disable a sensor
287  * Sensor will not run again unless
288  * explicitly enabled or reloaded
289  *
290  * @param sensor sensor information
291  */
292 static void set_sensor_enabled(struct SensorInfo *sensor, int state)
293 {
294   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
295       "Sensor `%s': Setting enabled to %d.\n",
296       sensor->name, state);
297   sensor->enabled = GNUNET_NO;
298   GNUNET_assert(NULL != sensor->cfg);
299   GNUNET_CONFIGURATION_set_value_string(sensor->cfg, sensor->name, "ENABLED",
300       (GNUNET_YES == state)?"YES":"NO");
301   GNUNET_CONFIGURATION_write(sensor->cfg, sensor->def_file);
302 }
303
304 /**
305  * Task run during shutdown.
306  *
307  * @param cls unused
308  * @param tc unused
309  */
310 static void
311 shutdown_task (void *cls,
312                const struct GNUNET_SCHEDULER_TaskContext *tc)
313 {
314   GNUNET_CONTAINER_multihashmap_iterate(sensors, &destroy_sensor, NULL);
315   GNUNET_CONTAINER_multihashmap_destroy(sensors);
316   if(NULL != statistics)
317   {
318     GNUNET_STATISTICS_destroy(statistics, GNUNET_YES);
319     statistics = NULL;
320   }
321   if(NULL != peerstore)
322   {
323     GNUNET_PEERSTORE_disconnect(peerstore);
324     peerstore = NULL;
325   }
326   GNUNET_SCHEDULER_shutdown();
327 }
328
329
330 /**
331  * A client disconnected.  Remove all of its data structure entries.
332  *
333  * @param cls closure, NULL
334  * @param client identification of the client
335  */
336 static void
337 handle_client_disconnect (void *cls,
338                           struct GNUNET_SERVER_Client
339                           * client)
340 {
341 }
342
343 /**
344  * Parses a version number string into major and minor
345  *
346  * @param version full version string
347  * @param major pointer to parsed major value
348  * @param minor pointer to parsed minor value
349  * @return #GNUNET_OK if parsing went ok, #GNUNET_SYSERROR in case of error
350  */
351 static int
352 version_parse(char *version, uint16_t *major, uint16_t *minor)
353 {
354   int majorval = 0;
355   int minorval = 0;
356
357   for(; isdigit(*version); version++)
358   {
359     majorval *= 10;
360     majorval += *version - '0';
361   }
362   if(*version != '.')
363     return GNUNET_SYSERR;
364   version++;
365   for(; isdigit(*version); version++)
366   {
367     minorval *= 10;
368     minorval += *version - '0';
369   }
370   if(*version != 0)
371     return GNUNET_SYSERR;
372   *major = majorval;
373   *minor = minorval;
374
375   return GNUNET_OK;
376 }
377
378 /**
379  * Load sensor definition from configuration
380  *
381  * @param cfg configuration handle
382  * @param sectionname configuration section containing definition
383  */
384 static struct SensorInfo *
385 load_sensor_from_cfg(struct GNUNET_CONFIGURATION_Handle *cfg, const char *sectionname)
386 {
387   struct SensorInfo *sensor;
388   char *version_str;
389   char *starttime_str;
390   char *endtime_str;
391   unsigned long long time_sec;
392
393   sensor = GNUNET_new(struct SensorInfo);
394   //name
395   sensor->name = GNUNET_strdup(sectionname);
396   //version
397   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "VERSION", &version_str))
398   {
399     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor version\n"));
400     GNUNET_free(sensor);
401     return NULL;
402   }
403   if(GNUNET_OK != version_parse(version_str, &(sensor->version_major), &(sensor->version_minor)))
404   {
405     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Invalid sensor version number, format should be major.minor\n"));
406     GNUNET_free(sensor);
407     GNUNET_free(version_str);
408     return NULL;
409   }
410   GNUNET_free(version_str);
411   //description
412   GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "DESCRIPTION", &sensor->description);
413   //category
414   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "CATEGORY", &sensor->category) ||
415         NULL == sensor->category)
416   {
417     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor category\n"));
418     GNUNET_free(sensor);
419     return NULL;
420   }
421   //enabled
422   if(GNUNET_NO == GNUNET_CONFIGURATION_get_value_yesno(cfg, sectionname, "ENABLED"))
423     sensor->enabled = GNUNET_NO;
424   else
425     sensor->enabled = GNUNET_YES;
426   //start time
427   sensor->start_time = NULL;
428   if(GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "START_TIME", &starttime_str))
429   {
430     GNUNET_STRINGS_fancy_time_to_absolute(starttime_str, sensor->start_time);
431     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Start time loaded: `%s'. Parsed: %d\n", starttime_str, (NULL != sensor->start_time));
432     GNUNET_free(starttime_str);
433   }
434   //end time
435   sensor->end_time = NULL;
436   if(GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "END_TIME", &endtime_str))
437   {
438     GNUNET_STRINGS_fancy_time_to_absolute(endtime_str, sensor->end_time);
439     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "End time loaded: `%s'. Parsed: %d\n", endtime_str, (NULL != sensor->end_time));
440     GNUNET_free(endtime_str);
441   }
442   //interval
443   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_number(cfg, sectionname, "INTERVAL", &time_sec))
444   {
445     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor run interval\n"));
446     GNUNET_free(sensor);
447     return NULL;
448   }
449   if(time_sec < MIN_INTERVAL)
450   {
451     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Sensor run interval too low (%" PRIu64 " < %d)\n"),
452         time_sec, MIN_INTERVAL);
453     GNUNET_free(sensor);
454     return NULL;
455   }
456   sensor->interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, time_sec);
457   //lifetime
458   if(GNUNET_OK == GNUNET_CONFIGURATION_get_value_number(cfg, sectionname, "LIFETIME", &time_sec))
459   {
460     sensor->lifetime = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, time_sec);
461   }
462   else
463     sensor->lifetime = sensor->interval;
464   //capabilities TODO
465   //source
466   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_choice(cfg, sectionname, "SOURCE", sources, (const char **)&sensor->source))
467   {
468     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor source\n"));
469     GNUNET_free(sensor);
470     return NULL;
471   }
472   if(sources[0] == sensor->source) //gnunet-statistics
473   {
474     if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "GNUNET_STAT_SERVICE", &sensor->gnunet_stat_service) ||
475         GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "GNUNET_STAT_NAME", &sensor->gnunet_stat_name))
476     {
477       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor gnunet-statistics source information\n"));
478       GNUNET_free(sensor);
479       return NULL;
480     }
481     sensor->gnunet_stat_get_handle = NULL;
482   }
483   else if(sources[1] == sensor->source) //process
484   {
485     if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "EXT_PROCESS", &sensor->ext_process))
486     {
487       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor process name\n"));
488       GNUNET_free(sensor);
489       return NULL;
490     }
491     GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "EXT_ARGS", &sensor->ext_args);
492   }
493   //expected datatype
494   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_choice(cfg, sectionname, "EXPECTED_DATATYPE", datatypes, (const char **)&sensor->expected_datatype))
495   {
496     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor expected datatype\n"));
497     GNUNET_free(sensor);
498     return NULL;
499   }
500   if(sources[0] == sensor->source && datatypes[0] != sensor->expected_datatype)
501   {
502     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Invalid expected datatype, gnunet-statistics returns uint64 values\n"));
503     GNUNET_free(sensor);
504     return NULL;
505   }
506   //TODO: reporting mechanism
507   //execution task
508   sensor->execution_task = GNUNET_SCHEDULER_NO_TASK;
509   //running
510   sensor->running = GNUNET_NO;
511
512   return sensor;
513 }
514
515 /**
516  * Load sensor definition from file
517  *
518  * @param filename full path to file containing sensor definition
519  */
520 static struct SensorInfo *
521 load_sensor_from_file(const char *filename)
522 {
523   struct GNUNET_CONFIGURATION_Handle *sensorcfg;
524   const char *filebasename;
525   struct SensorInfo *sensor;
526
527   //test file
528   if(GNUNET_YES != GNUNET_DISK_file_test(filename))
529   {
530     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to access sensor file: %s\n"), filename);
531     return NULL;
532   }
533   //load file as configuration
534   sensorcfg = GNUNET_CONFIGURATION_create();
535   if(GNUNET_SYSERR == GNUNET_CONFIGURATION_parse(sensorcfg, filename))
536   {
537     GNUNET_CONFIGURATION_destroy(sensorcfg);
538     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to load sensor definition: %s\n"), filename);
539     return NULL;
540   }
541   //configuration section should be the same as filename
542   filebasename = GNUNET_STRINGS_get_short_name(filename);
543   sensor = load_sensor_from_cfg(sensorcfg, filebasename);
544   if(NULL == sensor)
545   {
546     GNUNET_CONFIGURATION_destroy(sensorcfg);
547     return NULL;
548   }
549   sensor->def_file = GNUNET_strdup(filename);
550   sensor->cfg = sensorcfg;
551
552   return sensor;
553 }
554
555 /**
556  * Compares version numbers of two sensors
557  *
558  * @param s1 first sensor
559  * @param s2 second sensor
560  * @return 1: s1 > s2, 0: s1 == s2, -1: s1 < s2
561  */
562 static int
563 sensor_version_compare(struct SensorInfo *s1, struct SensorInfo *s2)
564 {
565   if(s1->version_major == s2->version_major)
566     return (s1->version_minor < s2->version_minor) ? -1 : (s1->version_minor > s2->version_minor);
567   else
568     return (s1->version_major < s2->version_major) ? -1 : (s1->version_major > s2->version_major);
569 }
570
571 /**
572  * Adds a new sensor to given hashmap.
573  * If the same name exist, compares versions and update if old.
574  *
575  * @param sensor Sensor structure to add
576  * @param map Hashmap to add to
577  * @return #GNUNET_YES if added, #GNUNET_NO if not added which is not necessarily an error
578  */
579 static int
580 add_sensor_to_hashmap(struct SensorInfo *sensor, struct GNUNET_CONTAINER_MultiHashMap *map)
581 {
582   struct GNUNET_HashCode key;
583   struct SensorInfo *existing;
584
585   GNUNET_CRYPTO_hash(sensor->name, strlen(sensor->name), &key);
586   existing = GNUNET_CONTAINER_multihashmap_get(map, &key);
587   if(NULL != existing) //sensor with same name already exists
588   {
589     if(sensor_version_compare(existing, sensor) >= 0) //same or newer version already exist
590     {
591       GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Sensor `%s' already exists with same or newer version\n"), sensor->name);
592       return GNUNET_NO;
593     }
594     else
595     {
596       GNUNET_CONTAINER_multihashmap_remove(map, &key, existing); //remove the old version
597       GNUNET_free(existing);
598       GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Upgrading sensor `%s' to a newer version\n", sensor->name);
599     }
600   }
601   if(GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put(map, &key, sensor, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
602   {
603     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error adding new sensor `%s' to global hashmap, this should not happen\n"), sensor->name);
604     return GNUNET_NO;
605   }
606
607   return GNUNET_YES;
608 }
609
610 /**
611  * Iterating over files in sensors directory
612  *
613  * @param cls closure
614  * @param filename complete filename (absolute path)
615  * @return #GNUNET_OK to continue to iterate
616  */
617 static int
618 reload_sensors_dir_cb(void *cls, const char *filename)
619 {
620   struct SensorInfo *sensor;
621
622   if(GNUNET_YES != GNUNET_DISK_file_test(filename))
623     return GNUNET_OK;
624   sensor = load_sensor_from_file(filename);
625   if(NULL == sensor)
626   {
627     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error loading sensor from file: %s\n"), filename);
628     return GNUNET_OK;
629   }
630   if(GNUNET_YES == add_sensor_to_hashmap(sensor, sensors))
631     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, _("Sensor `%s' added to global hashmap\n"), sensor->name);
632   else
633     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, ("Could not add sensor `%s' to global hashmap\n"), sensor->name);
634
635   return GNUNET_OK;
636 }
637
638 /*
639  * Get path to the directory containing the sensor definition files
640  *
641  * @return sensor files directory
642  */
643 static char *
644 get_sensor_dir()
645 {
646   char* datadir;
647   char* sensordir;
648
649   datadir = GNUNET_OS_installation_get_path(GNUNET_OS_IPK_DATADIR);
650   GNUNET_asprintf(&sensordir, "%ssensors%s",
651       datadir, DIR_SEPARATOR_STR);
652   GNUNET_free(datadir);
653
654   return sensordir;
655 }
656
657 /**
658  * Reads sensor definitions from data files
659  *
660  */
661 static void
662 reload_sensors()
663 {
664   char* sensordir;
665
666   sensordir = get_sensor_dir();
667   GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Reloading sensor definitions from directory `%s'\n"), sensordir);
668   GNUNET_assert(GNUNET_YES == GNUNET_DISK_directory_test(sensordir, GNUNET_YES));
669
670   //read all files in sensors directory
671   GNUNET_DISK_directory_scan(sensordir, &reload_sensors_dir_cb, NULL);
672   GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Loaded %d sensors from directory `%s'\n"),
673       GNUNET_CONTAINER_multihashmap_size(sensors), sensordir);
674   GNUNET_free(sensordir);
675 }
676
677 /**
678  * Creates a structure with basic sensor info to be sent to a client
679  *
680  * @param sensor sensor information
681  * @return message ready to be sent to client
682  */
683 static struct SensorInfoMessage *
684 create_sensor_info_msg(struct SensorInfo *sensor)
685 {
686   struct SensorInfoMessage *msg;
687   uint16_t len;
688   size_t name_len;
689   size_t desc_len;
690   char *str_ptr;
691
692   name_len = strlen(sensor->name);
693   if(NULL == sensor->description)
694     desc_len = 0;
695   else
696     desc_len = strlen(sensor->description) + 1;
697   len = 0;
698   len += sizeof(struct SensorInfoMessage);
699   len += name_len;
700   len += desc_len;
701   msg = GNUNET_malloc(len);
702   msg->header.size = htons(len);
703   msg->header.type = htons(GNUNET_MESSAGE_TYPE_SENSOR_INFO);
704   msg->name_len = htons(name_len);
705   msg->description_len = htons(desc_len);
706   msg->version_major = htons(sensor->version_major);
707   msg->version_minor = htons(sensor->version_minor);
708   str_ptr = (char*) &msg[1];
709   memcpy(str_ptr, sensor->name, name_len);
710   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor name (%d): %.*s\n",
711         name_len, name_len, str_ptr);
712   str_ptr += name_len;
713   memcpy(str_ptr, sensor->description, desc_len);
714   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor description (%d): %.*s\n",
715           desc_len, desc_len, str_ptr);
716
717   return msg;
718 }
719
720 /**
721  * Handle GET SENSOR message.
722  *
723  * @param cls closure
724  * @param client identification of the client
725  * @param message the actual message
726  */
727 static void
728 handle_get_sensor (void *cls, struct GNUNET_SERVER_Client *client,
729             const struct GNUNET_MessageHeader *message)
730 {
731   struct GNUNET_SERVER_TransmitContext *tc;
732   char *sensorname;
733   size_t sensorname_len;
734   struct GNUNET_HashCode key;
735   struct SensorInfo *sensorinfo;
736   struct SensorInfoMessage *msg;
737
738   sensorname = (char *)&message[1];
739   sensorname_len = ntohs(message->size) - sizeof(struct GNUNET_MessageHeader);
740   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received for sensor (%d) `%.*s'\n",
741               "GET SENSOR", sensorname_len, sensorname_len, sensorname);
742   tc = GNUNET_SERVER_transmit_context_create (client);
743   GNUNET_CRYPTO_hash(sensorname, sensorname_len, &key);
744   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created key hash for requested sensor\n");
745   sensorinfo = (struct SensorInfo *)GNUNET_CONTAINER_multihashmap_get(sensors, &key);
746   if(NULL != sensorinfo)
747   {
748     msg = create_sensor_info_msg(sensorinfo);
749     GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
750     GNUNET_free(msg);
751   }
752   else
753     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Requested sensor `%.*s' was not found\n",
754         sensorname_len, sensorname);
755   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
756   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
757 }
758
759 /**
760  * Iterator for sensors and adds them to transmit context
761  *
762  * @param cls a 'struct GNUNET_SERVER_TransmitContext *'
763  * @param key hash of sensor name, key to hashmap
764  * @param value a 'struct SensorInfo *'
765  */
766 int add_sensor_to_tc(void *cls,
767     const struct GNUNET_HashCode *key, void *value)
768 {
769   struct GNUNET_SERVER_TransmitContext *tc = cls;
770   struct SensorInfo *sensorinfo = value;
771   struct SensorInfoMessage *msg;
772
773   msg = create_sensor_info_msg(sensorinfo);
774   GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
775
776   GNUNET_free(msg);
777
778   return GNUNET_YES;
779 }
780
781 /**
782  * Handle GET ALL SENSORS message.
783  *
784  * @param cls closure
785  * @param client identification of the client
786  * @param message the actual message
787  */
788 static void
789 handle_get_all_sensors (void *cls, struct GNUNET_SERVER_Client *client,
790             const struct GNUNET_MessageHeader *message)
791 {
792   struct GNUNET_SERVER_TransmitContext *tc;
793
794   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received.\n",
795                 "GET ALL SENSOR");
796   tc = GNUNET_SERVER_transmit_context_create (client);
797   GNUNET_CONTAINER_multihashmap_iterate(sensors, &add_sensor_to_tc, tc);
798   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
799   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
800 }
801
802 /**
803  * Do a series of checks to determine if sensor should execute
804  *
805  * @return #GNUNET_YES / #GNUNET_NO
806  */
807 static int
808 should_run_sensor(struct SensorInfo *sensorinfo)
809 {
810   struct GNUNET_TIME_Absolute now;
811
812   if(GNUNET_NO == sensorinfo->enabled)
813   {
814     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' is disabled, will not run\n", sensorinfo->name);
815     return GNUNET_NO;
816   }
817   now = GNUNET_TIME_absolute_get();
818   if(NULL != sensorinfo->start_time
819       && now.abs_value_us < sensorinfo->start_time->abs_value_us)
820   {
821     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Start time for sensor `%s' not reached yet, will not run\n", sensorinfo->name);
822     return GNUNET_NO;
823   }
824   if(NULL != sensorinfo->end_time
825       && now.abs_value_us >= sensorinfo->end_time->abs_value_us)
826   {
827     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' expired, disabling.\n", sensorinfo->name);
828     set_sensor_enabled(sensorinfo, GNUNET_NO);
829     return GNUNET_NO;
830   }
831   return GNUNET_YES;
832 }
833
834 /**
835  * Callback function to process statistic values
836  *
837  * @param cls 'struct SensorInfo *'
838  * @param subsystem name of subsystem that created the statistic
839  * @param name the name of the datum
840  * @param value the current value
841  * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
842  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
843  */
844 int sensor_statistics_iterator (void *cls,
845     const char *ss,
846     const char *name,
847     uint64_t value,
848     int is_persistent)
849 {
850   struct SensorInfo *sensorinfo = cls;
851   struct GNUNET_TIME_Absolute expiry;
852
853   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %" PRIu64 "\n", sensorinfo->name, value);
854   expiry = GNUNET_TIME_relative_to_absolute(sensorinfo->lifetime);
855   GNUNET_PEERSTORE_store(peerstore,
856       subsystem,
857       &peerid,
858       sensorinfo->name,
859       &value,
860       sizeof(value),
861       expiry,
862       GNUNET_PEERSTORE_STOREOPTION_MULTIPLE,
863       NULL,
864       NULL);
865   return GNUNET_SYSERR; /* We only want one value */
866 }
867
868 /**
869  * Continuation called after sensor gets all gnunet statistics values
870  *
871  * @param cls 'struct SensorInfo *'
872  * @param success #GNUNET_OK if statistics were
873  *        successfully obtained, #GNUNET_SYSERR if not.
874  */
875 void end_sensor_run_stat (void *cls, int success)
876 {
877   struct SensorInfo *sensorinfo = cls;
878
879   sensorinfo->gnunet_stat_get_handle = NULL;
880   sensorinfo->running = GNUNET_NO;
881 }
882
883 /**
884  * Tries to parse a received sensor value to its
885  * expected datatype
886  *
887  * @param value the string value received, should be null terminated
888  * @param sensor sensor information struct
889  * @param ret pointer to parsed value
890  * @return size of new parsed value, 0 for error
891  */
892 static size_t
893 parse_sensor_value (const char *value, struct SensorInfo* sensor, void **ret)
894 {
895   uint64_t *ullval;
896   double *dval;
897   char *endptr;
898
899   *ret = NULL;
900   if ('\0' == *value)
901     return 0;
902   //"uint64", "double", "string"
903   if (0 == strcmp("uint64", sensor->expected_datatype))
904   {
905     ullval = GNUNET_new(uint64_t);
906     *ullval = strtoull(value, &endptr, 10);
907     if ('\0' != *endptr &&
908         '\n' != *endptr) /* Invalid string */
909       return 0;
910     *ret = ullval;
911     return sizeof(uint64_t);
912   }
913   if(0 == strcmp("double", sensor->expected_datatype))
914   {
915     dval = GNUNET_new(double);
916     *dval = strtod(value, &endptr);
917     if(value == endptr)
918       return 0;
919    *ret = dval;
920    return sizeof(double);
921   }
922   if(0 == strcmp("string", sensor->expected_datatype))
923   {
924     *ret = GNUNET_strdup(value);
925     return strlen(value) + 1;
926   }
927   GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
928       _("Unknown value type expected by sensor, this should not happen.\n"));
929   return 0;
930 }
931
932 /**
933  * Callback for output of executed sensor process
934  *
935  * @param cls 'struct SensorInfo *'
936  * @param line line of output from a command, NULL for the end
937  */
938 void sensor_process_callback (void *cls, const char *line)
939 {
940   struct SensorInfo *sensorinfo = cls;
941   void *value;
942   size_t valsize;
943   struct GNUNET_TIME_Absolute expiry;
944
945   if(NULL == line)
946   {
947     GNUNET_OS_command_stop(sensorinfo->ext_cmd);
948     sensorinfo->ext_cmd = NULL;
949     sensorinfo->running = GNUNET_NO;
950     sensorinfo->ext_cmd_value_received = GNUNET_NO;
951     return;
952   }
953   if(GNUNET_YES == sensorinfo->ext_cmd_value_received)
954     return; /* We only want one *valid* value */
955   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %s\n", sensorinfo->name, line);
956   valsize = parse_sensor_value(line, sensorinfo, &value);
957   if (valsize == 0) /* invalid value, FIXME: should we disable the sensor now? */
958   {
959     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
960         _("Received an invalid value for sensor `%s': %s\n"),
961         sensorinfo->name, line);
962   }
963   else
964   {
965     sensorinfo->ext_cmd_value_received = GNUNET_YES;
966     expiry = GNUNET_TIME_relative_to_absolute(sensorinfo->lifetime);
967     GNUNET_PEERSTORE_store(peerstore,
968         subsystem,
969         &peerid,
970         sensorinfo->name,
971         value,
972         valsize,
973         expiry,
974         GNUNET_PEERSTORE_STOREOPTION_MULTIPLE,
975         NULL,
976         NULL);
977   }
978 }
979
980 /**
981  * Checks if the given file is a path
982  *
983  * @return #GNUNET_YES / #GNUNET_NO
984  */
985 static int
986 is_path(char *filename)
987 {
988   size_t filename_len;
989   int i;
990
991   filename_len = strlen(filename);
992   for(i = 0; i < filename_len; i++)
993   {
994     if(DIR_SEPARATOR == filename[i])
995       return GNUNET_YES;
996   }
997   return GNUNET_NO;
998 }
999
1000 /**
1001  * Actual execution of a sensor
1002  *
1003  * @param cls 'struct SensorInfo'
1004  * @param tc unsed
1005  */
1006 void
1007 sensor_run (void *cls,
1008     const struct GNUNET_SCHEDULER_TaskContext * tc)
1009 {
1010   struct SensorInfo *sensorinfo = cls;
1011   int check_result;
1012   char *sensors_dir;
1013   char *process_path;
1014
1015   sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &sensor_run, sensorinfo);
1016   if(GNUNET_YES == sensorinfo->running) //FIXME: should we try to kill?
1017   {
1018     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sensor `%s' running for too long, will try again next interval\n", sensorinfo->name);
1019     return;
1020   }
1021   if(GNUNET_NO == should_run_sensor(sensorinfo))
1022     return;
1023   sensorinfo->running = GNUNET_YES;
1024   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Starting the execution of sensor `%s'\n", sensorinfo->name);
1025   if(sources[0] == sensorinfo->source) //gnunet-statistics
1026   {
1027     sensorinfo->gnunet_stat_get_handle = GNUNET_STATISTICS_get(statistics,
1028         sensorinfo->gnunet_stat_service,
1029         sensorinfo->gnunet_stat_name,
1030         sensorinfo->interval, //try to get values only for the interval of the sensor
1031         &end_sensor_run_stat,
1032         &sensor_statistics_iterator,
1033         sensorinfo);
1034   }
1035   else if(sources[1] == sensorinfo->source)
1036   {
1037     if(GNUNET_YES == is_path(sensorinfo->ext_process))
1038     {
1039       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1040           _("Sensor `%s': External process should not be a path, disabling sensor.\n"),
1041           sensorinfo->name);
1042       set_sensor_enabled(sensorinfo, GNUNET_NO);
1043       return;
1044     }
1045     //check if the process exists in $PATH
1046     process_path = GNUNET_strdup(sensorinfo->ext_process);
1047     check_result =
1048         GNUNET_OS_check_helper_binary(process_path, GNUNET_NO, NULL);
1049     if(GNUNET_SYSERR == check_result)
1050     {
1051       //search in sensor directory
1052       sensors_dir = get_sensor_dir();
1053       GNUNET_free(process_path);
1054       GNUNET_asprintf(&process_path, "%s%s-files%s%s",
1055           sensors_dir,
1056           sensorinfo->name,
1057           DIR_SEPARATOR_STR,
1058           sensorinfo->ext_process);
1059       GNUNET_free(sensors_dir);
1060       check_result =
1061         GNUNET_OS_check_helper_binary(process_path, GNUNET_NO, NULL);
1062     }
1063     if(GNUNET_SYSERR == check_result)
1064     {
1065       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1066           _("Sensor `%s' process `%s' problem: binary doesn't exist or not executable\n"),
1067           sensorinfo->name,
1068           sensorinfo->ext_process);
1069       set_sensor_enabled(sensorinfo, GNUNET_NO);
1070       sensorinfo->running = GNUNET_NO;
1071       GNUNET_free(process_path);
1072       return;
1073     }
1074     sensorinfo->ext_cmd_value_received = GNUNET_NO;
1075     sensorinfo->ext_cmd = GNUNET_OS_command_run(&sensor_process_callback,
1076         sensorinfo,
1077         GNUNET_TIME_UNIT_FOREVER_REL,
1078         process_path,
1079         sensorinfo->ext_process,
1080         sensorinfo->ext_args,
1081         NULL);
1082     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Process started for sensor `%s'\n", sensorinfo->name);
1083     GNUNET_free(process_path);
1084   }
1085   else
1086   {
1087     sensorinfo->running = GNUNET_NO;
1088     GNUNET_break(0); //shouldn't happen
1089   }
1090 }
1091
1092 /**
1093  * Starts the execution of a sensor
1094  *
1095  * @param cls unused
1096  * @param key hash of sensor name, key to hashmap (unused)
1097  * @param value a 'struct SensorInfo *'
1098  * @return #GNUNET_YES if we should continue to
1099  *         iterate,
1100  *         #GNUNET_NO if not.
1101  */
1102 int schedule_sensor(void *cls,
1103     const struct GNUNET_HashCode *key, void *value)
1104 {
1105   struct SensorInfo *sensorinfo = value;
1106
1107   if(GNUNET_NO == should_run_sensor(sensorinfo))
1108     return GNUNET_YES;
1109   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Scheduling sensor `%s' to run after %" PRIu64 " microseconds\n",
1110       sensorinfo->name, sensorinfo->interval.rel_value_us);
1111   if(GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
1112   {
1113     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1114         _("Sensor `%s' execution task already set, this should not happen\n"), sensorinfo->name);
1115     return GNUNET_NO;
1116   }
1117   sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &sensor_run, sensorinfo);
1118   return GNUNET_YES;
1119 }
1120
1121 /**
1122  * Starts the execution of all enabled sensors
1123  *
1124  */
1125 static void
1126 schedule_all_sensors()
1127 {
1128   GNUNET_CONTAINER_multihashmap_iterate(sensors, &schedule_sensor, NULL);
1129 }
1130
1131 /**
1132  * Process statistics requests.
1133  *
1134  * @param cls closure
1135  * @param server the initialized server
1136  * @param c configuration to use
1137  */
1138 static void
1139 run (void *cls,
1140      struct GNUNET_SERVER_Handle *server,
1141      const struct GNUNET_CONFIGURATION_Handle *c)
1142 {
1143   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1144     {&handle_get_sensor, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GET,
1145      0},
1146     {&handle_get_all_sensors, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GETALL,
1147      sizeof (struct GNUNET_MessageHeader)},
1148     {NULL, NULL, 0, 0}
1149   };
1150
1151   cfg = c;
1152   sensors = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
1153   reload_sensors();
1154   schedule_all_sensors();
1155   statistics = GNUNET_STATISTICS_create("sensor", cfg);
1156   GNUNET_CRYPTO_get_peer_identity(cfg, &peerid);
1157   peerstore = GNUNET_PEERSTORE_connect(cfg);
1158   GNUNET_SERVER_add_handlers (server, handlers);
1159   GNUNET_SERVER_disconnect_notify (server, 
1160                                    &handle_client_disconnect,
1161                                    NULL);
1162   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1163                                 &shutdown_task,
1164                                 NULL);
1165 }
1166
1167
1168 /**
1169  * The main function for the sensor service.
1170  *
1171  * @param argc number of arguments from the command line
1172  * @param argv command line arguments
1173  * @return 0 ok, 1 on error
1174  */
1175 int
1176 main (int argc, char *const *argv)
1177 {
1178   return (GNUNET_OK ==
1179           GNUNET_SERVICE_run (argc,
1180                               argv,
1181                               "sensor",
1182                               GNUNET_SERVICE_OPTION_NONE,
1183                               &run, NULL)) ? 0 : 1;
1184 }
1185
1186 /* end of gnunet-service-sensor.c */