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