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