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