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
31 /**
32  * Minimum sensor execution interval (in seconds)
33  */
34 #define MIN_INTERVAL 30
35
36 /**
37  * Structure containing sensor definition
38  */
39 struct SensorInfo
40 {
41
42   /*
43    * Sensor name
44    */
45   char *name;
46
47   /*
48    * Path to definition file
49    */
50   char *def_file;
51
52   /*
53    * First part of version number
54    */
55   uint16_t version_major;
56
57   /*
58    * Second part of version number
59    */
60   uint16_t version_minor;
61
62   /*
63    * Sensor description
64    */
65   char *description;
66
67   /*
68    * Sensor currently enabled
69    */
70   int enabled;
71
72   /*
73    * Category under which the sensor falls (e.g. tcp, datastore)
74    */
75   char *category;
76
77   /*
78    * When does the sensor become active
79    */
80   struct GNUNET_TIME_Absolute *start_time;
81
82   /*
83    * When does the sensor expire
84    */
85   struct GNUNET_TIME_Absolute *end_time;
86
87   /*
88    * Time interval to collect sensor information (e.g. every 1 min)
89    */
90   struct GNUNET_TIME_Relative interval;
91
92   /*
93    * Lifetime of an information sample after which it is deleted from storage
94    */
95   struct GNUNET_TIME_Relative lifetime;
96
97   /*
98    * A set of required peer capabilities for the sensor to collect meaningful information (e.g. ipv6)
99    */
100   char *capabilities;
101
102   /*
103    * Either "gnunet-statistics" or external "process"
104    */
105   char *source;
106
107   /*
108    * Name of the GNUnet service that is the source for the gnunet-statistics entry
109    */
110   char *gnunet_stat_service;
111
112   /*
113    * Name of the gnunet-statistics entry
114    */
115   char *gnunet_stat_name;
116
117   /**
118    * Handle to statistics get request (OR GNUNET_SCHEDULER_NO_TASK)
119    */
120   struct GNUNET_STATISTICS_GetHandle *gnunet_stat_get_handle;
121
122   /*
123    * Name of the external process to be executed
124    */
125   char *ext_process;
126
127   /*
128    * Arguments to be passed to the external process
129    */
130   char *ext_args;
131
132   /*
133    * The output datatype to be expected
134    */
135   char *expected_datatype;
136
137   /*
138    * Peer-identity of peer running collection point
139    */
140   struct GNUNET_PeerIdentity *collection_point;
141
142   /*
143    * Time interval to send sensor information to collection point (e.g. every 30 mins)
144    */
145   struct GNUNET_TIME_Relative *collection_interval;
146
147   /*
148    * Flag specifying if value is to be communicated to the p2p network
149    */
150   int p2p_report;
151
152   /*
153    * Time interval to communicate value to the p2p network
154    */
155   struct GNUNET_TIME_Relative *p2p_interval;
156
157   /**
158    * Execution task (OR GNUNET_SCHEDULER_NO_TASK)
159    */
160   GNUNET_SCHEDULER_TaskIdentifier execution_task;
161
162 };
163
164 /**
165  * Our configuration.
166  */
167 static const struct GNUNET_CONFIGURATION_Handle *cfg;
168
169 /**
170  * Hashmap of loaded sensor definitions
171  */
172 struct GNUNET_CONTAINER_MultiHashMap *sensors;
173
174 /**
175  * Supported sources of sensor information
176  */
177 static const char *sources[] = { "gnunet-statistics", "process", NULL };
178
179 /**
180  * Supported datatypes of sensor information
181  */
182 static const char *datatypes[] = { "uint64", "double", "string", NULL };
183
184 /**
185  * Handle to statistics service
186  */
187 struct GNUNET_STATISTICS_Handle *statistics;
188
189 /**
190  * Remove sensor execution from scheduler
191  *
192  * @param cls unused
193  * @param key hash of sensor name, key to hashmap
194  * @param value a 'struct SensorInfo *'
195  * @return #GNUNET_YES if we should continue to
196  *         iterate,
197  *         #GNUNET_NO if not.
198  */
199 int unschedule_sensor(void *cls,
200     const struct GNUNET_HashCode *key, void *value)
201 {
202   struct SensorInfo *sensorinfo = value;
203
204   if(NULL != sensorinfo->gnunet_stat_get_handle)
205   {
206     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Canceling a statistics get request for sensor `%s'\n", sensorinfo->name);
207     GNUNET_STATISTICS_get_cancel(sensorinfo->gnunet_stat_get_handle);
208   }
209   if(GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
210   {
211     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Unscheduling sensor `%s'\n", sensorinfo->name);
212     GNUNET_SCHEDULER_cancel(sensorinfo->execution_task);
213   }
214   return GNUNET_YES;
215 }
216
217 /**
218  * Task run during shutdown.
219  *
220  * @param cls unused
221  * @param tc unused
222  */
223 static void
224 shutdown_task (void *cls,
225                const struct GNUNET_SCHEDULER_TaskContext *tc)
226 {
227   GNUNET_CONTAINER_multihashmap_iterate(sensors, &unschedule_sensor, NULL);
228   if(NULL != statistics)
229     GNUNET_STATISTICS_destroy(statistics, GNUNET_YES);
230   GNUNET_SCHEDULER_shutdown();
231 }
232
233
234 /**
235  * A client disconnected.  Remove all of its data structure entries.
236  *
237  * @param cls closure, NULL
238  * @param client identification of the client
239  */
240 static void
241 handle_client_disconnect (void *cls,
242                           struct GNUNET_SERVER_Client
243                           * client)
244 {
245 }
246
247 /**
248  * Parses a version number string into major and minor
249  *
250  * @param version full version string
251  * @param major pointer to parsed major value
252  * @param minor pointer to parsed minor value
253  * @return #GNUNET_OK if parsing went ok, #GNUNET_SYSERROR in case of error
254  */
255 static int
256 version_parse(char *version, uint16_t *major, uint16_t *minor)
257 {
258   int majorval = 0;
259   int minorval = 0;
260
261   for(; isdigit(*version); version++)
262   {
263     majorval *= 10;
264     majorval += *version - '0';
265   }
266   if(*version != '.')
267     return GNUNET_SYSERR;
268   version++;
269   for(; isdigit(*version); version++)
270   {
271     minorval *= 10;
272     minorval += *version - '0';
273   }
274   if(*version != 0)
275     return GNUNET_SYSERR;
276   *major = majorval;
277   *minor = minorval;
278
279   return GNUNET_OK;
280 }
281
282 /**
283  * Load sensor definition from configuration
284  *
285  * @param cfg configuration handle
286  * @param sectionname configuration section containing definition
287  */
288 static struct SensorInfo *
289 load_sensor_from_cfg(struct GNUNET_CONFIGURATION_Handle *cfg, const char *sectionname)
290 {
291   struct SensorInfo *sensor;
292   char *version_str;
293   char *starttime_str;
294   char *endtime_str;
295   unsigned long long time_sec;
296
297   sensor = GNUNET_new(struct SensorInfo);
298   //name
299   sensor->name = GNUNET_strdup(sectionname);
300   //version
301   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "VERSION", &version_str))
302   {
303     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor version\n"));
304     GNUNET_free(sensor);
305     return NULL;
306   }
307   if(GNUNET_OK != version_parse(version_str, &(sensor->version_major), &(sensor->version_minor)))
308   {
309     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Invalid sensor version number, format should be major.minor\n"));
310     GNUNET_free(sensor);
311     GNUNET_free(version_str);
312     return NULL;
313   }
314   GNUNET_free(version_str);
315   //description
316   GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "DESCRIPTION", &sensor->description);
317   //category
318   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "CATEGORY", &sensor->category) ||
319         NULL == sensor->category)
320   {
321     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor category\n"));
322     GNUNET_free(sensor);
323     return NULL;
324   }
325   //enabled
326   if(GNUNET_NO == GNUNET_CONFIGURATION_get_value_yesno(cfg, sectionname, "ENABLED"))
327     sensor->enabled = GNUNET_NO;
328   else
329     sensor->enabled = GNUNET_YES;
330   //start time
331   sensor->start_time = NULL;
332   if(GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "START_TIME", &starttime_str))
333   {
334     GNUNET_STRINGS_fancy_time_to_absolute(starttime_str, sensor->start_time);
335     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Start time loaded: `%s'. Parsed: %d\n", starttime_str, (NULL != sensor->start_time));
336     GNUNET_free(starttime_str);
337   }
338   //end time
339   sensor->end_time = NULL;
340   if(GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "END_TIME", &endtime_str))
341   {
342     GNUNET_STRINGS_fancy_time_to_absolute(endtime_str, sensor->end_time);
343     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "End time loaded: `%s'. Parsed: %d\n", endtime_str, (NULL != sensor->end_time));
344     GNUNET_free(endtime_str);
345   }
346   //interval
347   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_number(cfg, sectionname, "INTERVAL", &time_sec))
348   {
349     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor run interval\n"));
350     GNUNET_free(sensor);
351     return NULL;
352   }
353   if(time_sec < MIN_INTERVAL)
354   {
355     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Sensor run interval too low (%" PRIu64 " < %d)\n"),
356         time_sec, MIN_INTERVAL);
357     GNUNET_free(sensor);
358     return NULL;
359   }
360   sensor->interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, time_sec);
361   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Interval loaded: %" PRIu64 "\n", sensor->interval.rel_value_us);
362   //lifetime
363   if(GNUNET_OK == GNUNET_CONFIGURATION_get_value_number(cfg, sectionname, "LIFETIME", &time_sec))
364   {
365     sensor->lifetime = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, time_sec);
366     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Lifetime loaded: %" PRIu64 "\n", sensor->lifetime.rel_value_us);
367   }
368   else
369     sensor->lifetime = GNUNET_TIME_UNIT_FOREVER_REL;
370   //capabilities TODO
371   //source
372   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_choice(cfg, sectionname, "SOURCE", sources, &sensor->source))
373   {
374     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor source\n"));
375     GNUNET_free(sensor);
376     return NULL;
377   }
378   if(sources[0] == sensor->source) //gnunet-statistics
379   {
380     if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "GNUNET_STAT_SERVICE", &sensor->gnunet_stat_service) ||
381         GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "GNUNET_STAT_NAME", &sensor->gnunet_stat_name))
382     {
383       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor gnunet-statistics source information\n"));
384       GNUNET_free(sensor);
385       return NULL;
386     }
387     sensor->gnunet_stat_get_handle = NULL;
388   }
389   else if(sources[1] == sensor->source) //process
390   {
391     if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "EXT_PROCESS", &sensor->ext_process))
392     {
393       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor process name\n"));
394       GNUNET_free(sensor);
395       return NULL;
396     }
397     GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "EXT_ARGS", &sensor->ext_args);
398   }
399   //expected datatype
400   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_choice(cfg, sectionname, "EXPECTED_DATATYPE", datatypes, &sensor->expected_datatype))
401   {
402     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor expected datatype\n"));
403     GNUNET_free(sensor);
404     return NULL;
405   }
406   if(sources[0] == sensor->source && datatypes[0] != sensor->expected_datatype)
407   {
408     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Invalid expected datatype, gnunet-statistics returns uint64 values\n"));
409     GNUNET_free(sensor);
410     return NULL;
411   }
412   //TODO: reporting mechanism
413   //execution task
414   sensor->execution_task = GNUNET_SCHEDULER_NO_TASK;
415
416   return sensor;
417 }
418
419 /**
420  * Load sensor definition from file
421  *
422  * @param filename full path to file containing sensor definition
423  */
424 static struct SensorInfo *
425 load_sensor_from_file(const char *filename)
426 {
427   struct GNUNET_CONFIGURATION_Handle *sensorcfg;
428   const char *filebasename;
429   struct SensorInfo *sensor;
430
431   //test file
432   if(GNUNET_YES != GNUNET_DISK_file_test(filename))
433   {
434     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to access sensor file: %s\n"), filename);
435     return NULL;
436   }
437   //load file as configuration
438   sensorcfg = GNUNET_CONFIGURATION_create();
439   if(GNUNET_SYSERR == GNUNET_CONFIGURATION_parse(sensorcfg, filename))
440   {
441     GNUNET_CONFIGURATION_destroy(sensorcfg);
442     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to load sensor definition: %s\n"), filename);
443     return NULL;
444   }
445   //configuration section should be the same as filename
446   filebasename = GNUNET_STRINGS_get_short_name(filename);
447   sensor = load_sensor_from_cfg(sensorcfg, filebasename);
448   sensor->def_file = GNUNET_strdup(filename);
449
450   GNUNET_CONFIGURATION_destroy(sensorcfg);
451
452   return sensor;
453 }
454
455 /**
456  * Compares version numbers of two sensors
457  *
458  * @param s1 first sensor
459  * @param s2 second sensor
460  * @return 1: s1 > s2, 0: s1 == s2, -1: s1 < s2
461  */
462 static int
463 sensor_version_compare(struct SensorInfo *s1, struct SensorInfo *s2)
464 {
465   if(s1->version_major == s2->version_major)
466     return (s1->version_minor < s2->version_minor) ? -1 : (s1->version_minor > s2->version_minor);
467   else
468     return (s1->version_major < s2->version_major) ? -1 : (s1->version_major > s2->version_major);
469 }
470
471 /**
472  * Adds a new sensor to given hashmap.
473  * If the same name exist, compares versions and update if old.
474  *
475  * @param sensor Sensor structure to add
476  * @param map Hashmap to add to
477  * @return #GNUNET_YES if added, #GNUNET_NO if not added which is not necessarily an error
478  */
479 static int
480 add_sensor_to_hashmap(struct SensorInfo *sensor, struct GNUNET_CONTAINER_MultiHashMap *map)
481 {
482   struct GNUNET_HashCode key;
483   struct SensorInfo *existing;
484
485   GNUNET_CRYPTO_hash(sensor->name, strlen(sensor->name), &key);
486   existing = GNUNET_CONTAINER_multihashmap_get(map, &key);
487   if(NULL != existing) //sensor with same name already exists
488   {
489     if(sensor_version_compare(existing, sensor) >= 0) //same or newer version already exist
490     {
491       GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Sensor `%s' already exists with same or newer version\n"), sensor->name);
492       return GNUNET_NO;
493     }
494     else
495     {
496       GNUNET_CONTAINER_multihashmap_remove(map, &key, existing); //remove the old version
497       GNUNET_free(existing);
498       GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Upgrading sensor `%s' to a newer version\n"), sensor->name);
499     }
500   }
501   if(GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put(map, &key, sensor, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
502   {
503     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error adding new sensor `%s' to global hashmap, this should not happen\n"), sensor->name);
504     return GNUNET_NO;
505   }
506
507   return GNUNET_YES;
508 }
509
510 /**
511  * Iterating over files in sensors directory
512  *
513  * @param cls closure
514  * @param filename complete filename (absolute path)
515  * @return #GNUNET_OK to continue to iterate,
516  *  #GNUNET_NO to stop iteration with no error,
517  *  #GNUNET_SYSERR to abort iteration with error!
518  */
519 static int
520 reload_sensors_dir_cb(void *cls, const char *filename)
521 {
522   struct SensorInfo *sensor;
523
524   sensor = load_sensor_from_file(filename);
525   if(NULL == sensor)
526   {
527     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error loading sensor from file: %s\n"), filename);
528     return GNUNET_OK;
529   }
530   if(GNUNET_YES == add_sensor_to_hashmap(sensor, sensors))
531     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, _("Sensor `%s' added to global hashmap\n"), sensor->name);
532   else
533     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, ("Could not add sensor `%s' to global hashmap\n"), sensor->name);
534
535   return GNUNET_OK;
536 }
537
538 /*
539  * Get path to the directory containing the sensor definition files
540  *
541  * @return sensor files directory
542  */
543 static char *
544 get_sensor_dir()
545 {
546   char* datadir;
547   char* sensordir;
548
549   datadir = GNUNET_OS_installation_get_path(GNUNET_OS_IPK_DATADIR);
550   GNUNET_asprintf(&sensordir, "%ssensors%s",
551       datadir, DIR_SEPARATOR_STR);
552
553   return sensordir;
554 }
555
556 /**
557  * Reads sensor definitions from data files
558  *
559  */
560 static void
561 reload_sensors()
562 {
563   char* sensordir;
564   int filesfound;
565
566   sensordir = get_sensor_dir();
567   GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Reloading sensor definitions from directory `%s'\n"), sensordir);
568   GNUNET_assert(GNUNET_YES == GNUNET_DISK_directory_test(sensordir, GNUNET_YES));
569
570   //read all files in sensors directory
571   filesfound = GNUNET_DISK_directory_scan(sensordir, &reload_sensors_dir_cb, NULL);
572   GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Loaded %d/%d sensors from directory `%s'\n"),
573       GNUNET_CONTAINER_multihashmap_size(sensors), filesfound, sensordir);
574 }
575
576 /**
577  * Creates a structure with basic sensor info to be sent to a client
578  *
579  * @param sensor sensor information
580  * @return message ready to be sent to client
581  */
582 static struct SensorInfoMessage *
583 create_sensor_info_msg(struct SensorInfo *sensor)
584 {
585   struct SensorInfoMessage *msg;
586   uint16_t len;
587   size_t name_len;
588   size_t desc_len;
589   char *str_ptr;
590
591   name_len = strlen(sensor->name);
592   if(NULL == sensor->description)
593     desc_len = 0;
594   else
595     desc_len = strlen(sensor->description);
596   len = 0;
597   len += sizeof(struct SensorInfoMessage);
598   len += name_len;
599   len += desc_len;
600   msg = GNUNET_malloc(len);
601   msg->header.size = htons(len);
602   msg->header.type = htons(GNUNET_MESSAGE_TYPE_SENSOR_INFO);
603   msg->name_len = htons(name_len);
604   msg->description_len = htons(desc_len);
605   msg->version_major = htons(sensor->version_major);
606   msg->version_minor = htons(sensor->version_minor);
607   str_ptr = (char*) &msg[1];
608   memcpy(str_ptr, sensor->name, name_len);
609   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor name (%d): %.*s\n",
610         name_len, name_len, str_ptr);
611   str_ptr += name_len;
612   memcpy(str_ptr, sensor->description, desc_len);
613   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor description (%d): %.*s\n",
614           desc_len, desc_len, str_ptr);
615
616   return msg;
617 }
618
619 /**
620  * Handle GET SENSOR message.
621  *
622  * @param cls closure
623  * @param client identification of the client
624  * @param message the actual message
625  */
626 static void
627 handle_get_sensor (void *cls, struct GNUNET_SERVER_Client *client,
628             const struct GNUNET_MessageHeader *message)
629 {
630   struct GNUNET_SERVER_TransmitContext *tc;
631   char *sensorname;
632   size_t sensorname_len;
633   struct GNUNET_HashCode key;
634   struct SensorInfo *sensorinfo;
635   struct SensorInfoMessage *msg;
636
637   sensorname = (char *)&message[1];
638   sensorname_len = ntohs(message->size) - sizeof(struct GNUNET_MessageHeader);
639   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received for sensor (%d) `%.*s'\n",
640               "GET SENSOR", sensorname_len, sensorname_len, sensorname);
641   tc = GNUNET_SERVER_transmit_context_create (client);
642   GNUNET_CRYPTO_hash(sensorname, sensorname_len, &key);
643   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created key hash for requested sensor\n");
644   sensorinfo = (struct SensorInfo *)GNUNET_CONTAINER_multihashmap_get(sensors, &key);
645   if(NULL != sensorinfo)
646   {
647     msg = create_sensor_info_msg(sensorinfo);
648     GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
649     GNUNET_free(msg);
650   }
651   else
652     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Requested sensor `%.*s' was not found\n",
653         sensorname_len, sensorname);
654   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
655   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
656 }
657
658 /**
659  * Iterator for sensors and adds them to transmit context
660  *
661  * @param cls a 'struct GNUNET_SERVER_TransmitContext *'
662  * @param key hash of sensor name, key to hashmap
663  * @param value a 'struct SensorInfo *'
664  */
665 int add_sensor_to_tc(void *cls,
666     const struct GNUNET_HashCode *key, void *value)
667 {
668   struct GNUNET_SERVER_TransmitContext *tc = cls;
669   struct SensorInfo *sensorinfo = value;
670   struct SensorInfoMessage *msg;
671
672   msg = create_sensor_info_msg(sensorinfo);
673   GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
674
675   GNUNET_free(msg);
676
677   return GNUNET_YES;
678 }
679
680 /**
681  * Handle GET ALL SENSORS message.
682  *
683  * @param cls closure
684  * @param client identification of the client
685  * @param message the actual message
686  */
687 static void
688 handle_get_all_sensors (void *cls, struct GNUNET_SERVER_Client *client,
689             const struct GNUNET_MessageHeader *message)
690 {
691   struct GNUNET_SERVER_TransmitContext *tc;
692
693   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received.\n",
694                 "GET ALL SENSOR");
695   tc = GNUNET_SERVER_transmit_context_create (client);
696   GNUNET_CONTAINER_multihashmap_iterate(sensors, &add_sensor_to_tc, tc);
697   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
698   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
699 }
700
701 /**
702  * Do a series of checks to determine if sensor should execute
703  *
704  * @return #GNUNET_YES / #GNUNET_NO
705  */
706 static int
707 should_run_sensor(struct SensorInfo *sensorinfo)
708 {
709   struct GNUNET_TIME_Absolute now;
710
711   if(GNUNET_NO == sensorinfo->enabled)
712   {
713     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' is disabled, will not run\n", sensorinfo->name);
714     return GNUNET_NO;
715   }
716   now = GNUNET_TIME_absolute_get();
717   if(NULL != sensorinfo->start_time
718       && now.abs_value_us < sensorinfo->start_time->abs_value_us)
719   {
720     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Start time for sensor `%s' not reached yet, will not run\n", sensorinfo->name);
721     return GNUNET_NO;
722   }
723   if(NULL != sensorinfo->end_time
724       && now.abs_value_us >= sensorinfo->end_time->abs_value_us)
725   {
726     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "End time for sensor `%s' passed, will not run\n", sensorinfo->name);
727     return GNUNET_NO;
728   }
729   return GNUNET_YES;
730 }
731
732 /**
733  * Callback function to process statistic values
734  *
735  * @param cls 'struct SensorInfo *'
736  * @param subsystem name of subsystem that created the statistic
737  * @param name the name of the datum
738  * @param value the current value
739  * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
740  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
741  */
742 int sensor_statistics_iterator (void *cls,
743     const char *subsystem,
744     const char *name,
745     uint64_t value,
746     int is_persistent)
747 {
748   struct SensorInfo *sensorinfo = cls;
749
750   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %" PRIu64 "\n", sensorinfo->name, value);
751   return GNUNET_OK;
752 }
753
754 /**
755  * Actual execution of a sensor
756  *
757  * @param cls 'struct SensorInfo'
758  * @param tc unsed
759  */
760 void
761 run_sensor (void *cls,
762     const struct GNUNET_SCHEDULER_TaskContext * tc)
763 {
764   struct SensorInfo *sensorinfo = cls;
765
766   sensorinfo->execution_task = GNUNET_SCHEDULER_NO_TASK;
767   if(GNUNET_NO == should_run_sensor(sensorinfo))
768     return;
769   sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &run_sensor, sensorinfo);
770   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Starting the execution of sensor `%s'\n", sensorinfo->name);
771   if(sources[0] == sensorinfo->source) //gnunet-statistics
772   {
773     if(NULL == statistics)
774     {
775       statistics = GNUNET_STATISTICS_create("sensor", cfg);
776     }
777     sensorinfo->gnunet_stat_get_handle = GNUNET_STATISTICS_get(statistics,
778         sensorinfo->gnunet_stat_service,
779         sensorinfo->gnunet_stat_name,
780         GNUNET_TIME_UNIT_FOREVER_REL,
781         NULL,
782         &sensor_statistics_iterator,
783         sensorinfo);
784   }
785 }
786
787 /**
788  * Starts the execution of a sensor
789  *
790  * @param cls unused
791  * @param key hash of sensor name, key to hashmap
792  * @param value a 'struct SensorInfo *'
793  * @return #GNUNET_YES if we should continue to
794  *         iterate,
795  *         #GNUNET_NO if not.
796  */
797 int schedule_sensor(void *cls,
798     const struct GNUNET_HashCode *key, void *value)
799 {
800   struct SensorInfo *sensorinfo = value;
801
802   if(GNUNET_NO == should_run_sensor(sensorinfo))
803     return GNUNET_YES;
804   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Scheduling sensor `%s' to run after %" PRIu64 " microseconds\n",
805       sensorinfo->name, sensorinfo->interval.rel_value_us);
806   if(GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
807   {
808     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Sensor `%s' execution task already set, this should not happen\n", sensorinfo->name);
809     return GNUNET_NO;
810   }
811   sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &run_sensor, sensorinfo);
812   return GNUNET_YES;
813 }
814
815 /**
816  * Starts the execution of all enabled sensors
817  *
818  */
819 static void
820 schedule_all_sensors()
821 {
822   GNUNET_CONTAINER_multihashmap_iterate(sensors, &schedule_sensor, NULL);
823 }
824
825 /**
826  * Process statistics requests.
827  *
828  * @param cls closure
829  * @param server the initialized server
830  * @param c configuration to use
831  */
832 static void
833 run (void *cls,
834      struct GNUNET_SERVER_Handle *server,
835      const struct GNUNET_CONFIGURATION_Handle *c)
836 {
837   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
838     {&handle_get_sensor, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GET,
839      0},
840     {&handle_get_all_sensors, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GETALL,
841      sizeof (struct GNUNET_MessageHeader)},
842     {NULL, NULL, 0, 0}
843   };
844
845   cfg = c;
846   sensors = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
847   reload_sensors();
848   schedule_all_sensors();
849   GNUNET_SERVER_add_handlers (server, handlers);
850   GNUNET_SERVER_disconnect_notify (server, 
851                                    &handle_client_disconnect,
852                                    NULL);
853   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
854                                 &shutdown_task,
855                                 NULL);
856 }
857
858
859 /**
860  * The main function for the sensor service.
861  *
862  * @param argc number of arguments from the command line
863  * @param argv command line arguments
864  * @return 0 ok, 1 on error
865  */
866 int
867 main (int argc, char *const *argv)
868 {
869   return (GNUNET_OK ==
870           GNUNET_SERVICE_run (argc,
871                               argv,
872                               "sensor",
873                               GNUNET_SERVICE_OPTION_NONE,
874                               &run, NULL)) ? 0 : 1;
875 }
876
877 /* end of gnunet-service-sensor.c */