-keep track of messages passed to mq
[oweals/gnunet.git] / src / sensor / gnunet-service-sensor_monitoring.c
1 /*
2      This file is part of GNUnet.
3      Copyright (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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file sensor/gnunet-service-sensor_analysis.c
23  * @brief sensor service analysis functionality
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 #define LOG(kind,...) GNUNET_log_from (kind, "sensor-monitoring",__VA_ARGS__)
34
35 /**
36  * Our configuration.
37  */
38 static const struct GNUNET_CONFIGURATION_Handle *cfg;
39
40 /**
41  * Hashmap of loaded sensor definitions
42  */
43 static struct GNUNET_CONTAINER_MultiHashMap *sensors;
44
45 /**
46  * Path to sensor definitions directory
47  */
48 static char *sensor_dir;
49
50 /**
51  * Handle to statistics service
52  */
53 static struct GNUNET_STATISTICS_Handle *statistics;
54
55 /**
56  * Handle to peerstore service
57  */
58 static struct GNUNET_PEERSTORE_Handle *peerstore;
59
60 /**
61  * My peer id
62  */
63 static struct GNUNET_PeerIdentity peerid;
64
65
66 /**
67  * Stop the sensor monitoring module
68  */
69 void
70 SENSOR_monitoring_stop ()
71 {
72   if (NULL != statistics)
73   {
74     GNUNET_STATISTICS_destroy (statistics, GNUNET_YES);
75     statistics = NULL;
76   }
77   if (NULL != peerstore)
78   {
79     GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_YES);
80     peerstore = NULL;
81   }
82   if (NULL != sensor_dir)
83   {
84     GNUNET_free (sensor_dir);
85     sensor_dir = NULL;
86   }
87 }
88
89
90 /**
91  * Change the state of the sensor.
92  * Write the change to file to make it persistent.
93  *
94  * @param sensor sensor info struct
95  * @param state new enabled state: #GNUNET_YES / #GNUNET_NO
96  */
97 static void
98 set_sensor_enabled (struct GNUNET_SENSOR_SensorInfo *sensor, int state)
99 {
100   LOG (GNUNET_ERROR_TYPE_DEBUG, "Sensor `%s': Setting enabled to %d.\n",
101        sensor->name, state);
102   sensor->enabled = GNUNET_NO;
103   GNUNET_assert (NULL != sensor->cfg);
104   GNUNET_CONFIGURATION_set_value_string (sensor->cfg, sensor->name, "ENABLED",
105                                          (GNUNET_YES == state) ? "YES" : "NO");
106   GNUNET_CONFIGURATION_write (sensor->cfg, sensor->def_file);
107 }
108
109
110 /**
111  * Do a series of checks to determine if sensor should execute
112  *
113  * @return #GNUNET_YES / #GNUNET_NO
114  */
115 static int
116 should_run_sensor (struct GNUNET_SENSOR_SensorInfo *sensorinfo)
117 {
118   struct GNUNET_TIME_Absolute now;
119
120   if (GNUNET_NO == sensorinfo->enabled)
121   {
122     LOG (GNUNET_ERROR_TYPE_INFO, "Sensor `%s' is disabled, will not run\n",
123          sensorinfo->name);
124     return GNUNET_NO;
125   }
126   now = GNUNET_TIME_absolute_get ();
127   if (NULL != sensorinfo->start_time &&
128       now.abs_value_us < sensorinfo->start_time->abs_value_us)
129   {
130     LOG (GNUNET_ERROR_TYPE_INFO,
131          "Start time for sensor `%s' not reached yet, will not run\n",
132          sensorinfo->name);
133     return GNUNET_NO;
134   }
135   if (NULL != sensorinfo->end_time &&
136       now.abs_value_us >= sensorinfo->end_time->abs_value_us)
137   {
138     LOG (GNUNET_ERROR_TYPE_INFO, "Sensor `%s' expired, disabling.\n",
139          sensorinfo->name);
140     set_sensor_enabled (sensorinfo, GNUNET_NO);
141     return GNUNET_NO;
142   }
143   return GNUNET_YES;
144 }
145
146
147 /**
148  * Callback function to process statistic values
149  *
150  * @param cls `struct GNUNET_SENSOR_SensorInfo *`
151  * @param ss name of subsystem that created the statistic
152  * @param name the name of the datum
153  * @param value the current value
154  * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
155  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
156  */
157 static int
158 sensor_statistics_iterator (void *cls, const char *ss, const char *name,
159                             uint64_t value, int is_persistent)
160 {
161   struct GNUNET_SENSOR_SensorInfo *sensorinfo = cls;
162   double dvalue = (double) value;
163   struct GNUNET_TIME_Absolute expiry;
164
165   LOG (GNUNET_ERROR_TYPE_INFO,
166        "Received a value for sensor `%s': %" PRIu64 "\n", sensorinfo->name,
167        value);
168   expiry = GNUNET_TIME_relative_to_absolute (sensorinfo->lifetime);
169   GNUNET_PEERSTORE_store (peerstore, "sensor", &peerid, sensorinfo->name,
170                           &dvalue, sizeof (dvalue), expiry,
171                           GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, NULL, NULL);
172   return GNUNET_SYSERR;         /* We only want one value */
173 }
174
175
176 /**
177  * Continuation called after sensor gets all gnunet statistics values
178  *
179  * @param cls `struct GNUNET_SENSOR_SensorInfo *`
180  * @param success #GNUNET_OK if statistics were
181  *        successfully obtained, #GNUNET_SYSERR if not.
182  */
183 static void
184 end_sensor_run_stat (void *cls, int success)
185 {
186   struct GNUNET_SENSOR_SensorInfo *sensorinfo = cls;
187
188   sensorinfo->gnunet_stat_get_handle = NULL;
189   sensorinfo->running = GNUNET_NO;
190 }
191
192
193 /**
194  * Tries to parse a received sensor value to its
195  * expected datatype
196  *
197  * @param value the string value received, should be null terminated
198  * @param sensor sensor information struct
199  * @param ret pointer to parsed value
200  * @return size of new parsed value, 0 for error
201  */
202 static size_t
203 parse_sensor_value (const char *value,
204                     struct GNUNET_SENSOR_SensorInfo *sensor,
205                     void **ret)
206 {
207   double *dval;
208   char *endptr;
209
210   *ret = NULL;
211   if ('\0' == *value)
212     return 0;
213   if (0 == strcmp ("numeric", sensor->expected_datatype))
214   {
215     dval = GNUNET_new (double);
216
217     *dval = strtod (value, &endptr);
218     if (value == endptr)
219     {
220       GNUNET_free (dval);
221       *ret = NULL;
222       return 0;
223     }
224     *ret = dval;
225     return sizeof (double);
226   }
227   if (0 == strcmp ("string", sensor->expected_datatype))
228   {
229     *ret = GNUNET_strdup (value);
230     return strlen (value) + 1;
231   }
232   LOG (GNUNET_ERROR_TYPE_ERROR,
233        _("Unknown value type expected by sensor, this should not happen.\n"));
234   return 0;
235 }
236
237
238 /**
239  * Callback for output of executed sensor process
240  *
241  * @param cls `struct GNUNET_SENSOR_SensorInfo *`
242  * @param line line of output from a command, NULL for the end
243  */
244 static void
245 sensor_process_callback (void *cls, const char *line)
246 {
247   struct GNUNET_SENSOR_SensorInfo *sensorinfo = cls;
248   void *value;
249   size_t valsize;
250   struct GNUNET_TIME_Absolute expiry;
251
252   if (NULL == line)
253   {
254     GNUNET_OS_command_stop (sensorinfo->ext_cmd);
255     sensorinfo->ext_cmd = NULL;
256     sensorinfo->running = GNUNET_NO;
257     sensorinfo->ext_cmd_value_received = GNUNET_NO;
258     return;
259   }
260   if (GNUNET_YES == sensorinfo->ext_cmd_value_received)
261     return;                     /* We only want one *valid* value */
262   LOG (GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %s\n",
263        sensorinfo->name, line);
264   valsize = parse_sensor_value (line, sensorinfo, &value);
265   if (valsize == 0)             /* invalid value, FIXME: should we disable the sensor now? */
266   {
267     LOG (GNUNET_ERROR_TYPE_ERROR,
268          _("Received an invalid value for sensor `%s': %s\n"), sensorinfo->name,
269          line);
270   }
271   else
272   {
273     sensorinfo->ext_cmd_value_received = GNUNET_YES;
274     expiry = GNUNET_TIME_relative_to_absolute (sensorinfo->lifetime);
275     GNUNET_PEERSTORE_store (peerstore, "sensor", &peerid, sensorinfo->name,
276                             value, valsize, expiry,
277                             GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, NULL, NULL);
278     GNUNET_free (value);
279   }
280 }
281
282
283 /**
284  * Checks if the given file is a path
285  *
286  * @return #GNUNET_YES / #GNUNET_NO
287  */
288 static int
289 is_path (char *filename)
290 {
291   size_t filename_len;
292   int i;
293
294   filename_len = strlen (filename);
295   for (i = 0; i < filename_len; i++)
296   {
297     if (DIR_SEPARATOR == filename[i])
298       return GNUNET_YES;
299   }
300   return GNUNET_NO;
301 }
302
303
304 /**
305  * Actual execution of a sensor
306  *
307  * @param cls 'struct SensorInfo'
308  * @param tc unsed
309  */
310 static void
311 sensor_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
312 {
313   struct GNUNET_SENSOR_SensorInfo *sensorinfo = cls;
314   int check_result;
315   char *process_path;
316
317   sensorinfo->execution_task =
318       GNUNET_SCHEDULER_add_delayed (sensorinfo->interval, &sensor_run,
319                                     sensorinfo);
320   if (GNUNET_YES == sensorinfo->running)        //FIXME: should we try to kill?
321   {
322     LOG (GNUNET_ERROR_TYPE_WARNING,
323          "Sensor `%s' running for too long, will try again next interval\n",
324          sensorinfo->name);
325     return;
326   }
327   if (GNUNET_NO == should_run_sensor (sensorinfo))
328     return;
329   sensorinfo->running = GNUNET_YES;
330   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting the execution of sensor `%s'\n",
331        sensorinfo->name);
332   if (0 == strcmp ("gnunet-statistics", sensorinfo->source))
333   {
334     sensorinfo->gnunet_stat_get_handle = GNUNET_STATISTICS_get (statistics, sensorinfo->gnunet_stat_service, sensorinfo->gnunet_stat_name, sensorinfo->interval,        //try to get values only for the interval of the sensor
335                                                                 &end_sensor_run_stat,
336                                                                 &sensor_statistics_iterator,
337                                                                 sensorinfo);
338     if (NULL == sensorinfo->gnunet_stat_get_handle)
339       sensorinfo->running = GNUNET_NO;
340   }
341   else if (0 == strcmp ("process", sensorinfo->source))
342   {
343     if (GNUNET_YES == is_path (sensorinfo->ext_process))
344     {
345       LOG (GNUNET_ERROR_TYPE_ERROR,
346            _
347            ("Sensor `%s': External process should not be a path, disabling sensor.\n"),
348            sensorinfo->name);
349       set_sensor_enabled (sensorinfo, GNUNET_NO);
350       return;
351     }
352     //check if the process exists in $PATH
353     process_path = GNUNET_strdup (sensorinfo->ext_process);
354     check_result =
355         GNUNET_OS_check_helper_binary (process_path, GNUNET_NO, NULL);
356     if (GNUNET_SYSERR == check_result)
357     {
358       //search in sensor directory
359       GNUNET_free (process_path);
360       GNUNET_asprintf (&process_path, "%s%s-files%s%s", sensor_dir,
361                        sensorinfo->name, DIR_SEPARATOR_STR,
362                        sensorinfo->ext_process);
363       check_result =
364           GNUNET_OS_check_helper_binary (process_path, GNUNET_NO, NULL);
365     }
366     if (GNUNET_SYSERR == check_result)
367     {
368       LOG (GNUNET_ERROR_TYPE_ERROR,
369            _
370            ("Sensor `%s' process `%s' problem: binary doesn't exist or not executable\n"),
371            sensorinfo->name, sensorinfo->ext_process);
372       set_sensor_enabled (sensorinfo, GNUNET_NO);
373       sensorinfo->running = GNUNET_NO;
374       GNUNET_free (process_path);
375       return;
376     }
377     sensorinfo->ext_cmd_value_received = GNUNET_NO;
378     sensorinfo->ext_cmd =
379         GNUNET_OS_command_run (&sensor_process_callback, sensorinfo,
380                                GNUNET_TIME_UNIT_FOREVER_REL, process_path,
381                                sensorinfo->ext_process, sensorinfo->ext_args,
382                                NULL);
383     LOG (GNUNET_ERROR_TYPE_DEBUG, "Process started for sensor `%s'\n",
384          sensorinfo->name);
385     GNUNET_free (process_path);
386   }
387   else
388   {
389     sensorinfo->running = GNUNET_NO;
390     GNUNET_break (0);           /* shouldn't happen */
391   }
392 }
393
394
395 /**
396  * Starts the execution of a sensor
397  *
398  * @param cls unused
399  * @param key hash of sensor name, key to hashmap (unused)
400  * @param value a `struct GNUNET_SENSOR_SensorInfo *`
401  * @return #GNUNET_YES if we should continue to
402  *         iterate,
403  *         #GNUNET_NO if not.
404  */
405 static int
406 schedule_sensor (void *cls, const struct GNUNET_HashCode *key, void *value)
407 {
408   struct GNUNET_SENSOR_SensorInfo *sensorinfo = value;
409
410   if (GNUNET_NO == should_run_sensor (sensorinfo))
411     return GNUNET_YES;
412   LOG (GNUNET_ERROR_TYPE_DEBUG,
413        "Scheduling sensor `%s' to run after %" PRIu64 " microseconds\n",
414        sensorinfo->name, sensorinfo->interval.rel_value_us);
415   if (NULL != sensorinfo->execution_task)
416   {
417     LOG (GNUNET_ERROR_TYPE_ERROR,
418          _("Sensor `%s' execution task already set, this should not happen\n"),
419          sensorinfo->name);
420     return GNUNET_NO;
421   }
422   sensorinfo->execution_task =
423       GNUNET_SCHEDULER_add_delayed (sensorinfo->interval, &sensor_run,
424                                     sensorinfo);
425   return GNUNET_YES;
426 }
427
428
429 /**
430  * Starts the execution of all enabled sensors
431  */
432 static void
433 schedule_all_sensors ()
434 {
435   GNUNET_CONTAINER_multihashmap_iterate (sensors, &schedule_sensor, NULL);
436 }
437
438
439 /**
440  * Start the sensor monitoring module
441  *
442  * @param c our service configuration
443  * @param sensors multihashmap of loaded sensors
444  * @return #GNUNET_OK if started successfully, #GNUNET_SYSERR otherwise
445  */
446 int
447 SENSOR_monitoring_start (const struct GNUNET_CONFIGURATION_Handle *c,
448                          struct GNUNET_CONTAINER_MultiHashMap *s)
449 {
450   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting sensor reporting module.\n");
451   GNUNET_assert (NULL != s);
452   sensors = s;
453   cfg = c;
454   statistics = GNUNET_STATISTICS_create ("sensor", cfg);
455   if (GNUNET_OK !=
456       GNUNET_CONFIGURATION_get_value_filename (cfg, "SENSOR", "SENSOR_DIR",
457                                                &sensor_dir))
458   {
459     sensor_dir = GNUNET_SENSOR_get_default_sensor_dir ();
460   }
461   if (NULL == statistics)
462   {
463     SENSOR_monitoring_stop ();
464     return GNUNET_SYSERR;
465   }
466   peerstore = GNUNET_PEERSTORE_connect (cfg);
467   if (NULL == peerstore)
468   {
469     SENSOR_monitoring_stop ();
470     return GNUNET_SYSERR;
471   }
472   GNUNET_CRYPTO_get_peer_identity (cfg, &peerid);
473   schedule_all_sensors ();
474   return GNUNET_OK;
475 }
476
477 /* end of gnunet-service-sensor_analysis.c */