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