completed sensordashboard + fixes
[oweals/gnunet.git] / src / sensor / gnunet-service-sensor.c
1 /*
2      This file is part of GNUnet.
3      (C) 
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file sensor/gnunet-service-sensor.c
23  * @brief sensor service implementation
24  * @author Omar Tarabai
25  */
26 #include <inttypes.h>
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "sensor.h"
30 #include "gnunet_statistics_service.h"
31 #include "gnunet_peerstore_service.h"
32
33 /**
34  * Our configuration.
35  */
36 static const struct GNUNET_CONFIGURATION_Handle *cfg;
37
38 /**
39  * Hashmap of loaded sensor definitions
40  */
41 static struct GNUNET_CONTAINER_MultiHashMap *sensors;
42
43 /**
44  * Handle to statistics service
45  */
46 struct GNUNET_STATISTICS_Handle *statistics;
47
48 /**
49  * Handle to peerstore service
50  */
51 struct GNUNET_PEERSTORE_Handle *peerstore;
52
53 /**
54  * Service name
55  */
56 char *subsystem = "sensor";
57
58 /**
59  * My peer id
60  */
61 struct GNUNET_PeerIdentity peerid;
62
63 /**
64  * Disable a sensor
65  * Sensor will not run again unless
66  * explicitly enabled or reloaded
67  *
68  * @param sensor sensor information
69  */
70 static void set_sensor_enabled(struct SensorInfo *sensor, int state)
71 {
72   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
73       "Sensor `%s': Setting enabled to %d.\n",
74       sensor->name, state);
75   sensor->enabled = GNUNET_NO;
76   GNUNET_assert(NULL != sensor->cfg);
77   GNUNET_CONFIGURATION_set_value_string(sensor->cfg, sensor->name, "ENABLED",
78       (GNUNET_YES == state)?"YES":"NO");
79   GNUNET_CONFIGURATION_write(sensor->cfg, sensor->def_file);
80 }
81
82 /**
83  * Task run during shutdown.
84  *
85  * @param cls unused
86  * @param tc unused
87  */
88 static void
89 shutdown_task (void *cls,
90                const struct GNUNET_SCHEDULER_TaskContext *tc)
91 {
92   SENSOR_reporting_stop();
93   SENSOR_analysis_stop();
94   GNUNET_SENSOR_destroy_sensors (sensors);
95   if(NULL != statistics)
96   {
97     GNUNET_STATISTICS_destroy(statistics, GNUNET_YES);
98     statistics = NULL;
99   }
100   if(NULL != peerstore)
101   {
102     GNUNET_PEERSTORE_disconnect(peerstore);
103     peerstore = NULL;
104   }
105   GNUNET_SCHEDULER_shutdown();
106 }
107
108
109 /**
110  * A client disconnected.  Remove all of its data structure entries.
111  *
112  * @param cls closure, NULL
113  * @param client identification of the client
114  */
115 static void
116 handle_client_disconnect (void *cls,
117                           struct GNUNET_SERVER_Client
118                           * client)
119 {
120 }
121
122 /**
123  * Creates a structure with basic sensor info to be sent to a client
124  *
125  * @param sensor sensor information
126  * @return message ready to be sent to client
127  */
128 static struct SensorInfoMessage *
129 create_sensor_info_msg(struct SensorInfo *sensor)
130 {
131   struct SensorInfoMessage *msg;
132   uint16_t len;
133   size_t name_len;
134   size_t desc_len;
135   char *str_ptr;
136
137   name_len = strlen(sensor->name);
138   if(NULL == sensor->description)
139     desc_len = 0;
140   else
141     desc_len = strlen(sensor->description) + 1;
142   len = 0;
143   len += sizeof(struct SensorInfoMessage);
144   len += name_len;
145   len += desc_len;
146   msg = GNUNET_malloc(len);
147   msg->header.size = htons(len);
148   msg->header.type = htons(GNUNET_MESSAGE_TYPE_SENSOR_INFO);
149   msg->name_len = htons(name_len);
150   msg->description_len = htons(desc_len);
151   msg->version_major = htons(sensor->version_major);
152   msg->version_minor = htons(sensor->version_minor);
153   str_ptr = (char*) &msg[1];
154   memcpy(str_ptr, sensor->name, name_len);
155   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor name (%d): %.*s\n",
156         name_len, name_len, str_ptr);
157   str_ptr += name_len;
158   memcpy(str_ptr, sensor->description, desc_len);
159   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor description (%d): %.*s\n",
160           desc_len, desc_len, str_ptr);
161
162   return msg;
163 }
164
165 /**
166  * Handle GET SENSOR message.
167  *
168  * @param cls closure
169  * @param client identification of the client
170  * @param message the actual message
171  */
172 static void
173 handle_get_sensor (void *cls, struct GNUNET_SERVER_Client *client,
174             const struct GNUNET_MessageHeader *message)
175 {
176   struct GNUNET_SERVER_TransmitContext *tc;
177   char *sensorname;
178   size_t sensorname_len;
179   struct GNUNET_HashCode key;
180   struct SensorInfo *sensorinfo;
181   struct SensorInfoMessage *msg;
182
183   sensorname = (char *)&message[1];
184   sensorname_len = ntohs(message->size) - sizeof(struct GNUNET_MessageHeader);
185   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received for sensor (%d) `%.*s'\n",
186               "GET SENSOR", sensorname_len, sensorname_len, sensorname);
187   tc = GNUNET_SERVER_transmit_context_create (client);
188   GNUNET_CRYPTO_hash(sensorname, sensorname_len, &key);
189   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created key hash for requested sensor\n");
190   sensorinfo = (struct SensorInfo *)GNUNET_CONTAINER_multihashmap_get(sensors, &key);
191   if(NULL != sensorinfo)
192   {
193     msg = create_sensor_info_msg(sensorinfo);
194     GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
195     GNUNET_free(msg);
196   }
197   else
198     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Requested sensor `%.*s' was not found\n",
199         sensorname_len, sensorname);
200   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
201   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
202 }
203
204 /**
205  * Iterator for sensors and adds them to transmit context
206  *
207  * @param cls a 'struct GNUNET_SERVER_TransmitContext *'
208  * @param key hash of sensor name, key to hashmap
209  * @param value a 'struct SensorInfo *'
210  */
211 int add_sensor_to_tc(void *cls,
212     const struct GNUNET_HashCode *key, void *value)
213 {
214   struct GNUNET_SERVER_TransmitContext *tc = cls;
215   struct SensorInfo *sensorinfo = value;
216   struct SensorInfoMessage *msg;
217
218   msg = create_sensor_info_msg(sensorinfo);
219   GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
220
221   GNUNET_free(msg);
222
223   return GNUNET_YES;
224 }
225
226 /**
227  * Handle GET ALL SENSORS message.
228  *
229  * @param cls closure
230  * @param client identification of the client
231  * @param message the actual message
232  */
233 static void
234 handle_get_all_sensors (void *cls, struct GNUNET_SERVER_Client *client,
235             const struct GNUNET_MessageHeader *message)
236 {
237   struct GNUNET_SERVER_TransmitContext *tc;
238
239   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received.\n",
240                 "GET ALL SENSOR");
241   tc = GNUNET_SERVER_transmit_context_create (client);
242   GNUNET_CONTAINER_multihashmap_iterate(sensors, &add_sensor_to_tc, tc);
243   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
244   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
245 }
246
247 /**
248  * Do a series of checks to determine if sensor should execute
249  *
250  * @return #GNUNET_YES / #GNUNET_NO
251  */
252 static int
253 should_run_sensor(struct SensorInfo *sensorinfo)
254 {
255   struct GNUNET_TIME_Absolute now;
256
257   if(GNUNET_NO == sensorinfo->enabled)
258   {
259     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' is disabled, will not run\n", sensorinfo->name);
260     return GNUNET_NO;
261   }
262   now = GNUNET_TIME_absolute_get();
263   if(NULL != sensorinfo->start_time
264       && now.abs_value_us < sensorinfo->start_time->abs_value_us)
265   {
266     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Start time for sensor `%s' not reached yet, will not run\n", sensorinfo->name);
267     return GNUNET_NO;
268   }
269   if(NULL != sensorinfo->end_time
270       && now.abs_value_us >= sensorinfo->end_time->abs_value_us)
271   {
272     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' expired, disabling.\n", sensorinfo->name);
273     set_sensor_enabled(sensorinfo, GNUNET_NO);
274     return GNUNET_NO;
275   }
276   return GNUNET_YES;
277 }
278
279 /**
280  * Callback function to process statistic values
281  *
282  * @param cls 'struct SensorInfo *'
283  * @param subsystem name of subsystem that created the statistic
284  * @param name the name of the datum
285  * @param value the current value
286  * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
287  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
288  */
289 int sensor_statistics_iterator (void *cls,
290     const char *ss,
291     const char *name,
292     uint64_t value,
293     int is_persistent)
294 {
295   struct SensorInfo *sensorinfo = cls;
296   double dvalue = (double)value;
297   struct GNUNET_TIME_Absolute expiry;
298
299   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %" PRIu64 "\n", sensorinfo->name, value);
300   expiry = GNUNET_TIME_relative_to_absolute(sensorinfo->lifetime);
301   GNUNET_PEERSTORE_store(peerstore,
302       subsystem,
303       &peerid,
304       sensorinfo->name,
305       &dvalue,
306       sizeof(dvalue),
307       expiry,
308       GNUNET_PEERSTORE_STOREOPTION_MULTIPLE,
309       NULL,
310       NULL);
311   return GNUNET_SYSERR; /* We only want one value */
312 }
313
314 /**
315  * Continuation called after sensor gets all gnunet statistics values
316  *
317  * @param cls 'struct SensorInfo *'
318  * @param success #GNUNET_OK if statistics were
319  *        successfully obtained, #GNUNET_SYSERR if not.
320  */
321 void end_sensor_run_stat (void *cls, int success)
322 {
323   struct SensorInfo *sensorinfo = cls;
324
325   sensorinfo->gnunet_stat_get_handle = NULL;
326   sensorinfo->running = GNUNET_NO;
327 }
328
329 /**
330  * Tries to parse a received sensor value to its
331  * expected datatype
332  *
333  * @param value the string value received, should be null terminated
334  * @param sensor sensor information struct
335  * @param ret pointer to parsed value
336  * @return size of new parsed value, 0 for error
337  */
338 static size_t
339 parse_sensor_value (const char *value, struct SensorInfo* sensor, void **ret)
340 {
341   double *dval;
342   char *endptr;
343
344   *ret = NULL;
345   if ('\0' == *value)
346     return 0;
347   if(0 == strcmp("numeric", sensor->expected_datatype))
348   {
349     dval = GNUNET_new(double);
350     *dval = strtod(value, &endptr);
351     if(value == endptr)
352       return 0;
353    *ret = dval;
354    return sizeof(double);
355   }
356   if(0 == strcmp("string", sensor->expected_datatype))
357   {
358     *ret = GNUNET_strdup(value);
359     return strlen(value) + 1;
360   }
361   GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
362       _("Unknown value type expected by sensor, this should not happen.\n"));
363   return 0;
364 }
365
366 /**
367  * Callback for output of executed sensor process
368  *
369  * @param cls 'struct SensorInfo *'
370  * @param line line of output from a command, NULL for the end
371  */
372 void sensor_process_callback (void *cls, const char *line)
373 {
374   struct SensorInfo *sensorinfo = cls;
375   void *value;
376   size_t valsize;
377   struct GNUNET_TIME_Absolute expiry;
378
379   if(NULL == line)
380   {
381     GNUNET_OS_command_stop(sensorinfo->ext_cmd);
382     sensorinfo->ext_cmd = NULL;
383     sensorinfo->running = GNUNET_NO;
384     sensorinfo->ext_cmd_value_received = GNUNET_NO;
385     return;
386   }
387   if(GNUNET_YES == sensorinfo->ext_cmd_value_received)
388     return; /* We only want one *valid* value */
389   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %s\n", sensorinfo->name, line);
390   valsize = parse_sensor_value(line, sensorinfo, &value);
391   if (valsize == 0) /* invalid value, FIXME: should we disable the sensor now? */
392   {
393     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
394         _("Received an invalid value for sensor `%s': %s\n"),
395         sensorinfo->name, line);
396   }
397   else
398   {
399     sensorinfo->ext_cmd_value_received = GNUNET_YES;
400     expiry = GNUNET_TIME_relative_to_absolute(sensorinfo->lifetime);
401     GNUNET_PEERSTORE_store(peerstore,
402         subsystem,
403         &peerid,
404         sensorinfo->name,
405         value,
406         valsize,
407         expiry,
408         GNUNET_PEERSTORE_STOREOPTION_MULTIPLE,
409         NULL,
410         NULL);
411     GNUNET_free (value);
412   }
413 }
414
415 /**
416  * Checks if the given file is a path
417  *
418  * @return #GNUNET_YES / #GNUNET_NO
419  */
420 static int
421 is_path(char *filename)
422 {
423   size_t filename_len;
424   int i;
425
426   filename_len = strlen(filename);
427   for(i = 0; i < filename_len; i++)
428   {
429     if(DIR_SEPARATOR == filename[i])
430       return GNUNET_YES;
431   }
432   return GNUNET_NO;
433 }
434
435 /**
436  * Actual execution of a sensor
437  *
438  * @param cls 'struct SensorInfo'
439  * @param tc unsed
440  */
441 void
442 sensor_run (void *cls,
443     const struct GNUNET_SCHEDULER_TaskContext * tc)
444 {
445   struct SensorInfo *sensorinfo = cls;
446   int check_result;
447   char *sensors_dir;
448   char *process_path;
449
450   sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &sensor_run, sensorinfo);
451   if(GNUNET_YES == sensorinfo->running) //FIXME: should we try to kill?
452   {
453     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sensor `%s' running for too long, will try again next interval\n", sensorinfo->name);
454     return;
455   }
456   if(GNUNET_NO == should_run_sensor(sensorinfo))
457     return;
458   sensorinfo->running = GNUNET_YES;
459   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Starting the execution of sensor `%s'\n", sensorinfo->name);
460   if(0 == strcmp ("gnunet-statistics", sensorinfo->source))
461   {
462     sensorinfo->gnunet_stat_get_handle = GNUNET_STATISTICS_get(statistics,
463         sensorinfo->gnunet_stat_service,
464         sensorinfo->gnunet_stat_name,
465         sensorinfo->interval, //try to get values only for the interval of the sensor
466         &end_sensor_run_stat,
467         &sensor_statistics_iterator,
468         sensorinfo);
469   }
470   else if(0 == strcmp ("process", sensorinfo->source))
471   {
472     if(GNUNET_YES == is_path(sensorinfo->ext_process))
473     {
474       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
475           _("Sensor `%s': External process should not be a path, disabling sensor.\n"),
476           sensorinfo->name);
477       set_sensor_enabled(sensorinfo, GNUNET_NO);
478       return;
479     }
480     //check if the process exists in $PATH
481     process_path = GNUNET_strdup(sensorinfo->ext_process);
482     check_result =
483         GNUNET_OS_check_helper_binary(process_path, GNUNET_NO, NULL);
484     if(GNUNET_SYSERR == check_result)
485     {
486       //search in sensor directory
487       sensors_dir = GNUNET_SENSOR_get_sensor_dir ();
488       GNUNET_free(process_path);
489       GNUNET_asprintf(&process_path, "%s%s-files%s%s",
490           sensors_dir,
491           sensorinfo->name,
492           DIR_SEPARATOR_STR,
493           sensorinfo->ext_process);
494       GNUNET_free(sensors_dir);
495       check_result =
496         GNUNET_OS_check_helper_binary(process_path, GNUNET_NO, NULL);
497     }
498     if(GNUNET_SYSERR == check_result)
499     {
500       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
501           _("Sensor `%s' process `%s' problem: binary doesn't exist or not executable\n"),
502           sensorinfo->name,
503           sensorinfo->ext_process);
504       set_sensor_enabled(sensorinfo, GNUNET_NO);
505       sensorinfo->running = GNUNET_NO;
506       GNUNET_free(process_path);
507       return;
508     }
509     sensorinfo->ext_cmd_value_received = GNUNET_NO;
510     sensorinfo->ext_cmd = GNUNET_OS_command_run(&sensor_process_callback,
511         sensorinfo,
512         GNUNET_TIME_UNIT_FOREVER_REL,
513         process_path,
514         sensorinfo->ext_process,
515         sensorinfo->ext_args,
516         NULL);
517     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Process started for sensor `%s'\n", sensorinfo->name);
518     GNUNET_free(process_path);
519   }
520   else
521   {
522     sensorinfo->running = GNUNET_NO;
523     GNUNET_break(0); //shouldn't happen
524   }
525 }
526
527 /**
528  * Starts the execution of a sensor
529  *
530  * @param cls unused
531  * @param key hash of sensor name, key to hashmap (unused)
532  * @param value a 'struct SensorInfo *'
533  * @return #GNUNET_YES if we should continue to
534  *         iterate,
535  *         #GNUNET_NO if not.
536  */
537 int schedule_sensor(void *cls,
538     const struct GNUNET_HashCode *key, void *value)
539 {
540   struct SensorInfo *sensorinfo = value;
541
542   if(GNUNET_NO == should_run_sensor(sensorinfo))
543     return GNUNET_YES;
544   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Scheduling sensor `%s' to run after %" PRIu64 " microseconds\n",
545       sensorinfo->name, sensorinfo->interval.rel_value_us);
546   if(GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
547   {
548     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
549         _("Sensor `%s' execution task already set, this should not happen\n"), sensorinfo->name);
550     return GNUNET_NO;
551   }
552   sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &sensor_run, sensorinfo);
553   return GNUNET_YES;
554 }
555
556 /**
557  * Starts the execution of all enabled sensors
558  *
559  */
560 static void
561 schedule_all_sensors()
562 {
563   GNUNET_CONTAINER_multihashmap_iterate(sensors, &schedule_sensor, NULL);
564 }
565
566 /**
567  * Process statistics requests.
568  *
569  * @param cls closure
570  * @param server the initialized server
571  * @param c configuration to use
572  */
573 static void
574 run (void *cls,
575      struct GNUNET_SERVER_Handle *server,
576      const struct GNUNET_CONFIGURATION_Handle *c)
577 {
578   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
579     {&handle_get_sensor, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GET,
580      0},
581     {&handle_get_all_sensors, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GETALL,
582      sizeof (struct GNUNET_MessageHeader)},
583     {NULL, NULL, 0, 0}
584   };
585
586   cfg = c;
587   sensors = GNUNET_SENSOR_load_all_sensors ();
588   schedule_all_sensors();
589   SENSOR_analysis_start(c, sensors);
590   SENSOR_reporting_start(c, sensors);
591   statistics = GNUNET_STATISTICS_create("sensor", cfg);
592   GNUNET_CRYPTO_get_peer_identity(cfg, &peerid);
593   peerstore = GNUNET_PEERSTORE_connect(cfg);
594   GNUNET_SERVER_add_handlers (server, handlers);
595   GNUNET_SERVER_disconnect_notify (server, 
596                                    &handle_client_disconnect,
597                                    NULL);
598   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
599                                 &shutdown_task,
600                                 NULL);
601 }
602
603
604 /**
605  * The main function for the sensor service.
606  *
607  * @param argc number of arguments from the command line
608  * @param argv command line arguments
609  * @return 0 ok, 1 on error
610  */
611 int
612 main (int argc, char *const *argv)
613 {
614   return (GNUNET_OK ==
615           GNUNET_SERVICE_run (argc,
616                               argv,
617                               "sensor",
618                               GNUNET_SERVICE_OPTION_NONE,
619                               &run, NULL)) ? 0 : 1;
620 }
621
622 /* end of gnunet-service-sensor.c */