sensor: towards anomaly reporting
[oweals/gnunet.git] / src / sensor / gnunet-service-sensor_analysis.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 "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "sensor.h"
29 #include "gnunet_peerstore_service.h"
30 #include "gnunet_sensor_model_plugin.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "sensor-analysis",__VA_ARGS__)
33
34 /**
35  * Carries information about the analysis model
36  * corresponding to one sensor
37  */
38 struct SensorModel
39 {
40
41   /**
42    * DLL
43    */
44   struct SensorModel *prev;
45
46   /**
47    * DLL
48    */
49   struct SensorModel *next;
50
51   /**
52    * Pointer to sensor info structure
53    */
54   struct GNUNET_SENSOR_SensorInfo *sensor;
55
56   /**
57    * Watcher of sensor values
58    */
59   struct GNUNET_PEERSTORE_WatchContext *wc;
60
61   /**
62    * State of sensor. #GNUNET_YES if anomalous, #GNUNET_NO otherwise.
63    */
64   int anomalous;
65
66   /**
67    * Number of anomalous readings (positive) received in a row.
68    */
69   int positive_count;
70
71   /**
72    * Number of non-anomalous (negative) readings received in a row.
73    */
74   int negative_count;
75
76   /**
77    * Closure for model plugin.
78    * Usually, the instance of the model created for this sensor.
79    */
80   void *cls;
81
82 };
83
84 /**
85  * Our configuration.
86  */
87 static const struct GNUNET_CONFIGURATION_Handle *cfg;
88
89 /*
90  * Model library name
91  */
92 static char *model_lib_name;
93
94 /**
95  * Model handle
96  */
97 static struct GNUNET_SENSOR_ModelFunctions *model_api;
98
99 /**
100  * Handle to peerstore service
101  */
102 static struct GNUNET_PEERSTORE_Handle *peerstore;
103
104 /**
105  * Head of DLL of created models
106  */
107 static struct SensorModel *models_head;
108
109 /**
110  * Tail of DLL of created models
111  */
112 static struct SensorModel *models_tail;
113
114 /**
115  * My peer id
116  */
117 struct GNUNET_PeerIdentity peerid;
118
119 /**
120  * How many subsequent values required to flip anomaly label.
121  * E.g. After 3 subsequent anomaly reports, status change to anomalous.
122  */
123 unsigned long long confirmation_count;
124
125 /**
126  * Destroy a created model
127  */
128 static void
129 destroy_sensor_model (struct SensorModel *sensor_model)
130 {
131   GNUNET_assert (NULL != sensor_model);
132   LOG (GNUNET_ERROR_TYPE_DEBUG, "Destroying sensor model for `%s'.\n",
133        sensor_model->sensor->name);
134   if (NULL != sensor_model->wc)
135   {
136     GNUNET_PEERSTORE_watch_cancel (sensor_model->wc);
137     sensor_model->wc = NULL;
138   }
139   if (NULL != sensor_model->cls)
140   {
141     model_api->destroy_model (sensor_model->cls);
142     sensor_model->cls = NULL;
143   }
144   GNUNET_free (sensor_model);
145   sensor_model = NULL;
146 }
147
148
149 /**
150  * Stop the sensor analysis module
151  */
152 void
153 SENSOR_analysis_stop ()
154 {
155   struct SensorModel *sm;
156
157   LOG (GNUNET_ERROR_TYPE_DEBUG, "Stopping sensor analysis module.\n");
158   while (NULL != models_head)
159   {
160     sm = models_head;
161     GNUNET_CONTAINER_DLL_remove (models_head, models_tail, sm);
162     destroy_sensor_model (sm);
163   }
164   if (NULL != peerstore)
165   {
166     GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_YES);
167     peerstore = NULL;
168   }
169   if (NULL != model_api)
170   {
171     GNUNET_break (NULL == GNUNET_PLUGIN_unload (model_lib_name, model_api));
172     GNUNET_free (model_lib_name);
173     model_lib_name = NULL;
174   }
175 }
176
177
178 /**
179  * Sensor value watch callback
180  *
181  * @param cls Sensor model struct
182  * @param record Received record from peerstore, should contain new sensor value
183  * @param emsg Error message from peerstore if any, NULL if no errors
184  * @return #GNUNET_YES
185  */
186 static int
187 sensor_watcher (void *cls, struct GNUNET_PEERSTORE_Record *record, char *emsg)
188 {
189   struct SensorModel *model = cls;
190   double *val;
191   int anomalous;
192
193   LOG (GNUNET_ERROR_TYPE_DEBUG,
194        "Received a sensor value, will feed to sensor model.\n");
195   if (sizeof (double) != record->value_size)
196   {
197     LOG (GNUNET_ERROR_TYPE_ERROR, _("Received an invalid sensor value."));
198     return GNUNET_YES;
199   }
200   val = (double *) (record->value);
201   anomalous = model_api->feed_model (model->cls, *val);
202   if (GNUNET_YES == anomalous)
203   {
204     model->positive_count++;
205     model->negative_count = 0;
206     if (GNUNET_NO == model->anomalous &&
207         model->positive_count >= confirmation_count)
208     {
209       model->anomalous = GNUNET_YES;
210       LOG (GNUNET_ERROR_TYPE_WARNING,
211            "Anomaly state started for sensor `%s'.\n", model->sensor->name);
212       GNUNET_PEERSTORE_store (peerstore, "senosr-analysis", &peerid,
213                               model->sensor->name, &model->anomalous,
214                               sizeof (model->anomalous),
215                               GNUNET_TIME_absolute_get (),
216                               GNUNET_PEERSTORE_STOREOPTION_REPLACE, NULL, NULL);
217     }
218   }
219   else
220   {
221     model->negative_count++;
222     model->positive_count = 0;
223     if (GNUNET_YES == model->anomalous &&
224         model->negative_count >= confirmation_count)
225     {
226       model->anomalous = GNUNET_NO;
227       LOG (GNUNET_ERROR_TYPE_INFO, "Anomaly state stopped for sensor `%s'.\n",
228            model->sensor->name);
229       GNUNET_PEERSTORE_store (peerstore, "senosr-analysis", &peerid,
230                               model->sensor->name, &model->anomalous,
231                               sizeof (model->anomalous),
232                               GNUNET_TIME_absolute_get (),
233                               GNUNET_PEERSTORE_STOREOPTION_REPLACE, NULL, NULL);
234     }
235   }
236   return GNUNET_YES;
237 }
238
239
240 /**
241  * Iterator for defined sensors
242  * Creates sensor model for numeric sensors
243  *
244  * @param cls unused
245  * @param key unused
246  * @param value a 'struct GNUNET_SENSOR_SensorInfo *' with sensor information
247  * @return #GNUNET_YES to continue iterations
248  */
249 static int
250 init_sensor_model (void *cls, const struct GNUNET_HashCode *key, void *value)
251 {
252   struct GNUNET_SENSOR_SensorInfo *sensor = value;
253   struct SensorModel *sensor_model;
254
255   if (0 != strcmp ("numeric", sensor->expected_datatype))
256     return GNUNET_YES;
257   sensor_model = GNUNET_new (struct SensorModel);
258   sensor_model->sensor = sensor;
259   sensor_model->wc =
260       GNUNET_PEERSTORE_watch (peerstore, "sensor", &peerid, sensor->name,
261                               &sensor_watcher, sensor_model);
262   sensor_model->anomalous = GNUNET_NO;
263   sensor_model->positive_count = 0;
264   sensor_model->negative_count = 0;
265   sensor_model->cls = model_api->create_model (model_api->cls);
266   GNUNET_CONTAINER_DLL_insert (models_head, models_tail, sensor_model);
267   LOG (GNUNET_ERROR_TYPE_DEBUG, "Created sensor model for `%s'.\n",
268        sensor->name);
269   return GNUNET_YES;
270 }
271
272
273 /**
274  * Start the sensor analysis module
275  *
276  * @param c our service configuration
277  * @param sensors multihashmap of loaded sensors
278  * @return #GNUNET_OK if started successfully, #GNUNET_SYSERR otherwise
279  */
280 int
281 SENSOR_analysis_start (const struct GNUNET_CONFIGURATION_Handle *c,
282                        struct GNUNET_CONTAINER_MultiHashMap *sensors)
283 {
284   char *model_name;
285
286   GNUNET_assert (NULL != sensors);
287   cfg = c;
288   if (GNUNET_OK !=
289       GNUNET_CONFIGURATION_get_value_string (cfg, "sensor-analysis", "MODEL",
290                                              &model_name))
291   {
292     LOG (GNUNET_ERROR_TYPE_ERROR,
293          _("Analysis model not defined in configuration.\n"));
294     return GNUNET_SYSERR;
295   }
296   GNUNET_asprintf (&model_lib_name, "libgnunet_plugin_sensor_model_%s",
297                    model_name);
298   model_api = GNUNET_PLUGIN_load (model_lib_name, (void *) cfg);
299   GNUNET_free (model_name);
300   if (NULL == model_api)
301   {
302     LOG (GNUNET_ERROR_TYPE_ERROR, _("Could not load analysis model `%s'.\n"),
303          model_lib_name);
304     return GNUNET_SYSERR;
305   }
306   peerstore = GNUNET_PEERSTORE_connect (cfg);
307   if (NULL == peerstore)
308   {
309     LOG (GNUNET_ERROR_TYPE_ERROR,
310          _("Could not connect to peerstore service.\n"));
311     SENSOR_analysis_stop ();
312     return GNUNET_SYSERR;
313   }
314   if (GNUNET_OK !=
315       GNUNET_CONFIGURATION_get_value_number (cfg, "sensor-analysis",
316                                              "CONFIRMATION_COUNT",
317                                              &confirmation_count))
318     confirmation_count = 1;
319   GNUNET_CRYPTO_get_peer_identity (cfg, &peerid);
320   GNUNET_CONTAINER_multihashmap_iterate (sensors, &init_sensor_model, NULL);
321   return GNUNET_OK;
322 }
323
324 /* end of gnunet-service-sensor_analysis.c */