sensor: separate monitoring functions
[oweals/gnunet.git] / src / sensor / gnunet-service-sensor_monitoring.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_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,
123                 "Sensor `%s' is disabled, will not run\n", 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",
167               sensorinfo->name, 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, struct GNUNET_SENSOR_SensorInfo *sensor,
204                     void **ret)
205 {
206   double *dval;
207   char *endptr;
208
209   *ret = NULL;
210   if ('\0' == *value)
211     return 0;
212   if (0 == strcmp ("numeric", sensor->expected_datatype))
213   {
214     dval = GNUNET_new (double);
215
216     *dval = strtod (value, &endptr);
217     if (value == endptr)
218       return 0;
219     *ret = dval;
220     return sizeof (double);
221   }
222   if (0 == strcmp ("string", sensor->expected_datatype))
223   {
224     *ret = GNUNET_strdup (value);
225     return strlen (value) + 1;
226   }
227   LOG (GNUNET_ERROR_TYPE_ERROR,
228               _
229               ("Unknown value type expected by sensor, this should not happen.\n"));
230   return 0;
231 }
232
233
234 /**
235  * Callback for output of executed sensor process
236  *
237  * @param cls `struct GNUNET_SENSOR_SensorInfo *`
238  * @param line line of output from a command, NULL for the end
239  */
240 static void
241 sensor_process_callback (void *cls, const char *line)
242 {
243   struct GNUNET_SENSOR_SensorInfo *sensorinfo = cls;
244   void *value;
245   size_t valsize;
246   struct GNUNET_TIME_Absolute expiry;
247
248   if (NULL == line)
249   {
250     GNUNET_OS_command_stop (sensorinfo->ext_cmd);
251     sensorinfo->ext_cmd = NULL;
252     sensorinfo->running = GNUNET_NO;
253     sensorinfo->ext_cmd_value_received = GNUNET_NO;
254     return;
255   }
256   if (GNUNET_YES == sensorinfo->ext_cmd_value_received)
257     return;                     /* We only want one *valid* value */
258   LOG (GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %s\n",
259               sensorinfo->name, line);
260   valsize = parse_sensor_value (line, sensorinfo, &value);
261   if (valsize == 0)             /* invalid value, FIXME: should we disable the sensor now? */
262   {
263     LOG (GNUNET_ERROR_TYPE_ERROR,
264                 _("Received an invalid value for sensor `%s': %s\n"),
265                 sensorinfo->name, line);
266   }
267   else
268   {
269     sensorinfo->ext_cmd_value_received = GNUNET_YES;
270     expiry = GNUNET_TIME_relative_to_absolute (sensorinfo->lifetime);
271     GNUNET_PEERSTORE_store (peerstore, "sensor", &peerid, sensorinfo->name,
272                             value, valsize, expiry,
273                             GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, NULL, NULL);
274     GNUNET_free (value);
275   }
276 }
277
278
279 /**
280  * Checks if the given file is a path
281  *
282  * @return #GNUNET_YES / #GNUNET_NO
283  */
284 static int
285 is_path (char *filename)
286 {
287   size_t filename_len;
288   int i;
289
290   filename_len = strlen (filename);
291   for (i = 0; i < filename_len; i++)
292   {
293     if (DIR_SEPARATOR == filename[i])
294       return GNUNET_YES;
295   }
296   return GNUNET_NO;
297 }
298
299
300 /**
301  * Actual execution of a sensor
302  *
303  * @param cls 'struct SensorInfo'
304  * @param tc unsed
305  */
306 static void
307 sensor_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
308 {
309   struct GNUNET_SENSOR_SensorInfo *sensorinfo = cls;
310   int check_result;
311   char *process_path;
312
313   sensorinfo->execution_task =
314       GNUNET_SCHEDULER_add_delayed (sensorinfo->interval, &sensor_run,
315                                     sensorinfo);
316   if (GNUNET_YES == sensorinfo->running)        //FIXME: should we try to kill?
317   {
318     LOG (GNUNET_ERROR_TYPE_WARNING,
319                 "Sensor `%s' running for too long, will try again next interval\n",
320                 sensorinfo->name);
321     return;
322   }
323   if (GNUNET_NO == should_run_sensor (sensorinfo))
324     return;
325   sensorinfo->running = GNUNET_YES;
326   LOG (GNUNET_ERROR_TYPE_DEBUG,
327               "Starting the execution of sensor `%s'\n", sensorinfo->name);
328   if (0 == strcmp ("gnunet-statistics", sensorinfo->source))
329   {
330     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
331                                                                 &end_sensor_run_stat,
332                                                                 &sensor_statistics_iterator,
333                                                                 sensorinfo);
334   }
335   else if (0 == strcmp ("process", sensorinfo->source))
336   {
337     if (GNUNET_YES == is_path (sensorinfo->ext_process))
338     {
339       LOG (GNUNET_ERROR_TYPE_ERROR,
340                   _
341                   ("Sensor `%s': External process should not be a path, disabling sensor.\n"),
342                   sensorinfo->name);
343       set_sensor_enabled (sensorinfo, GNUNET_NO);
344       return;
345     }
346     //check if the process exists in $PATH
347     process_path = GNUNET_strdup (sensorinfo->ext_process);
348     check_result =
349         GNUNET_OS_check_helper_binary (process_path, GNUNET_NO, NULL);
350     if (GNUNET_SYSERR == check_result)
351     {
352       //search in sensor directory
353       GNUNET_free (process_path);
354       GNUNET_asprintf (&process_path, "%s%s-files%s%s", sensor_dir,
355                        sensorinfo->name, DIR_SEPARATOR_STR,
356                        sensorinfo->ext_process);
357       GNUNET_free (sensor_dir);
358       check_result =
359           GNUNET_OS_check_helper_binary (process_path, GNUNET_NO, NULL);
360     }
361     if (GNUNET_SYSERR == check_result)
362     {
363       LOG (GNUNET_ERROR_TYPE_ERROR,
364                   _
365                   ("Sensor `%s' process `%s' problem: binary doesn't exist or not executable\n"),
366                   sensorinfo->name, sensorinfo->ext_process);
367       set_sensor_enabled (sensorinfo, GNUNET_NO);
368       sensorinfo->running = GNUNET_NO;
369       GNUNET_free (process_path);
370       return;
371     }
372     sensorinfo->ext_cmd_value_received = GNUNET_NO;
373     sensorinfo->ext_cmd =
374         GNUNET_OS_command_run (&sensor_process_callback, sensorinfo,
375                                GNUNET_TIME_UNIT_FOREVER_REL, process_path,
376                                sensorinfo->ext_process, sensorinfo->ext_args,
377                                NULL);
378     LOG (GNUNET_ERROR_TYPE_DEBUG, "Process started for sensor `%s'\n",
379                 sensorinfo->name);
380     GNUNET_free (process_path);
381   }
382   else
383   {
384     sensorinfo->running = GNUNET_NO;
385     GNUNET_break (0);           /* shouldn't happen */
386   }
387 }
388
389
390 /**
391  * Starts the execution of a sensor
392  *
393  * @param cls unused
394  * @param key hash of sensor name, key to hashmap (unused)
395  * @param value a `struct GNUNET_SENSOR_SensorInfo *`
396  * @return #GNUNET_YES if we should continue to
397  *         iterate,
398  *         #GNUNET_NO if not.
399  */
400 static int
401 schedule_sensor (void *cls, const struct GNUNET_HashCode *key, void *value)
402 {
403   struct GNUNET_SENSOR_SensorInfo *sensorinfo = value;
404
405   if (GNUNET_NO == should_run_sensor (sensorinfo))
406     return GNUNET_YES;
407   LOG (GNUNET_ERROR_TYPE_DEBUG,
408               "Scheduling sensor `%s' to run after %" PRIu64 " microseconds\n",
409               sensorinfo->name, sensorinfo->interval.rel_value_us);
410   if (GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
411   {
412     LOG (GNUNET_ERROR_TYPE_ERROR,
413                 _
414                 ("Sensor `%s' execution task already set, this should not happen\n"),
415                 sensorinfo->name);
416     return GNUNET_NO;
417   }
418   sensorinfo->execution_task =
419       GNUNET_SCHEDULER_add_delayed (sensorinfo->interval, &sensor_run,
420                                     sensorinfo);
421   return GNUNET_YES;
422 }
423
424
425 /**
426  * Starts the execution of all enabled sensors
427  */
428 static void
429 schedule_all_sensors ()
430 {
431   GNUNET_CONTAINER_multihashmap_iterate (sensors, &schedule_sensor, NULL);
432 }
433
434
435 /**
436  * Start the sensor monitoring module
437  *
438  * @param c our service configuration
439  * @param sensors multihashmap of loaded sensors
440  * @return #GNUNET_OK if started successfully, #GNUNET_SYSERR otherwise
441  */
442 int
443 SENSOR_monitoring_start (const struct GNUNET_CONFIGURATION_Handle *c,
444                        struct GNUNET_CONTAINER_MultiHashMap *s)
445 {
446   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting sensor reporting module.\n");
447   GNUNET_assert (NULL != s);
448   sensors = s;
449   cfg = c;
450   statistics = GNUNET_STATISTICS_create ("sensor", cfg);
451   if (GNUNET_OK !=
452         GNUNET_CONFIGURATION_get_value_filename (cfg, "SENSOR", "SENSOR_DIR",
453                                                  &sensor_dir))
454   {
455     sensor_dir = GNUNET_SENSOR_get_default_sensor_dir ();
456   }
457   if (NULL == statistics)
458   {
459     SENSOR_monitoring_stop ();
460     return GNUNET_SYSERR;
461   }
462   peerstore = GNUNET_PEERSTORE_connect (cfg);
463   if (NULL == peerstore)
464   {
465     SENSOR_monitoring_stop ();
466     return GNUNET_SYSERR;
467   }
468   GNUNET_CRYPTO_get_peer_identity (cfg, &peerid);
469   schedule_all_sensors ();
470   return GNUNET_OK;
471 }
472
473 /* end of gnunet-service-sensor_analysis.c */