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