sensor: fixes for proof-of-work, test passes now
[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       GNUNET_free (sensor_dir);
357       check_result =
358           GNUNET_OS_check_helper_binary (process_path, GNUNET_NO, NULL);
359     }
360     if (GNUNET_SYSERR == check_result)
361     {
362       LOG (GNUNET_ERROR_TYPE_ERROR,
363            _
364            ("Sensor `%s' process `%s' problem: binary doesn't exist or not executable\n"),
365            sensorinfo->name, sensorinfo->ext_process);
366       set_sensor_enabled (sensorinfo, GNUNET_NO);
367       sensorinfo->running = GNUNET_NO;
368       GNUNET_free (process_path);
369       return;
370     }
371     sensorinfo->ext_cmd_value_received = GNUNET_NO;
372     sensorinfo->ext_cmd =
373         GNUNET_OS_command_run (&sensor_process_callback, sensorinfo,
374                                GNUNET_TIME_UNIT_FOREVER_REL, process_path,
375                                sensorinfo->ext_process, sensorinfo->ext_args,
376                                NULL);
377     LOG (GNUNET_ERROR_TYPE_DEBUG, "Process started for sensor `%s'\n",
378          sensorinfo->name);
379     GNUNET_free (process_path);
380   }
381   else
382   {
383     sensorinfo->running = GNUNET_NO;
384     GNUNET_break (0);           /* shouldn't happen */
385   }
386 }
387
388
389 /**
390  * Starts the execution of a sensor
391  *
392  * @param cls unused
393  * @param key hash of sensor name, key to hashmap (unused)
394  * @param value a `struct GNUNET_SENSOR_SensorInfo *`
395  * @return #GNUNET_YES if we should continue to
396  *         iterate,
397  *         #GNUNET_NO if not.
398  */
399 static int
400 schedule_sensor (void *cls, const struct GNUNET_HashCode *key, void *value)
401 {
402   struct GNUNET_SENSOR_SensorInfo *sensorinfo = value;
403
404   if (GNUNET_NO == should_run_sensor (sensorinfo))
405     return GNUNET_YES;
406   LOG (GNUNET_ERROR_TYPE_DEBUG,
407        "Scheduling sensor `%s' to run after %" PRIu64 " microseconds\n",
408        sensorinfo->name, sensorinfo->interval.rel_value_us);
409   if (GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
410   {
411     LOG (GNUNET_ERROR_TYPE_ERROR,
412          _("Sensor `%s' execution task already set, this should not happen\n"),
413          sensorinfo->name);
414     return GNUNET_NO;
415   }
416   sensorinfo->execution_task =
417       GNUNET_SCHEDULER_add_delayed (sensorinfo->interval, &sensor_run,
418                                     sensorinfo);
419   return GNUNET_YES;
420 }
421
422
423 /**
424  * Starts the execution of all enabled sensors
425  */
426 static void
427 schedule_all_sensors ()
428 {
429   GNUNET_CONTAINER_multihashmap_iterate (sensors, &schedule_sensor, NULL);
430 }
431
432
433 /**
434  * Start the sensor monitoring module
435  *
436  * @param c our service configuration
437  * @param sensors multihashmap of loaded sensors
438  * @return #GNUNET_OK if started successfully, #GNUNET_SYSERR otherwise
439  */
440 int
441 SENSOR_monitoring_start (const struct GNUNET_CONFIGURATION_Handle *c,
442                          struct GNUNET_CONTAINER_MultiHashMap *s)
443 {
444   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting sensor reporting module.\n");
445   GNUNET_assert (NULL != s);
446   sensors = s;
447   cfg = c;
448   statistics = GNUNET_STATISTICS_create ("sensor", cfg);
449   if (GNUNET_OK !=
450       GNUNET_CONFIGURATION_get_value_filename (cfg, "SENSOR", "SENSOR_DIR",
451                                                &sensor_dir))
452   {
453     sensor_dir = GNUNET_SENSOR_get_default_sensor_dir ();
454   }
455   if (NULL == statistics)
456   {
457     SENSOR_monitoring_stop ();
458     return GNUNET_SYSERR;
459   }
460   peerstore = GNUNET_PEERSTORE_connect (cfg);
461   if (NULL == peerstore)
462   {
463     SENSOR_monitoring_stop ();
464     return GNUNET_SYSERR;
465   }
466   GNUNET_CRYPTO_get_peer_identity (cfg, &peerid);
467   schedule_all_sensors ();
468   return GNUNET_OK;
469 }
470
471 /* end of gnunet-service-sensor_analysis.c */