sensor: completed update functionality
[oweals/gnunet.git] / src / sensor / gnunet-service-sensor.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.c
23  * @brief sensor service implementation
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 /**
34  * Our configuration.
35  */
36 static const struct GNUNET_CONFIGURATION_Handle *cfg;
37
38 /**
39  * Hashmap of loaded sensor definitions
40  */
41 static struct GNUNET_CONTAINER_MultiHashMap *sensors;
42
43 /**
44  * Handle to statistics service
45  */
46 struct GNUNET_STATISTICS_Handle *statistics;
47
48 /**
49  * Handle to peerstore service
50  */
51 struct GNUNET_PEERSTORE_Handle *peerstore;
52
53 /**
54  * Service name
55  */
56 char *subsystem = "sensor";
57
58 /**
59  * My peer id
60  */
61 struct GNUNET_PeerIdentity peerid;
62
63
64 /**
65  * Resets the service by stopping components, reloading sensors and starting
66  * components. This is needed when we receive new sensor updates.
67  */
68 static void
69 reset ();
70
71
72 /**
73  * Change the state of the sensor.
74  * Write the change to file to make it persistent.
75  *
76  * @param sensor sensor info struct
77  * @param state new enabled state: #GNUNET_YES / #GNUNET_NO
78  */
79 static void
80 set_sensor_enabled (struct GNUNET_SENSOR_SensorInfo *sensor, int state)
81 {
82   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
83              "Sensor `%s': Setting enabled to %d.\n",
84              sensor->name, state);
85   sensor->enabled = GNUNET_NO;
86   GNUNET_assert (NULL != sensor->cfg);
87   GNUNET_CONFIGURATION_set_value_string (sensor->cfg,
88                                          sensor->name,
89                                          "ENABLED",
90                                          (GNUNET_YES == state) ? "YES" : "NO");
91   GNUNET_CONFIGURATION_write (sensor->cfg, sensor->def_file);
92 }
93
94
95 /**
96  * Stops components and destroys sensors
97  */
98 static void
99 stop ()
100 {
101   SENSOR_update_stop ();
102   SENSOR_reporting_stop ();
103   SENSOR_analysis_stop ();
104   GNUNET_SENSOR_destroy_sensors (sensors);
105 }
106
107
108 /**
109  * Task run during shutdown.
110  *
111  * @param cls unused
112  * @param tc unused
113  */
114 static void
115 shutdown_task (void *cls,
116                const struct GNUNET_SCHEDULER_TaskContext *tc)
117 {
118   stop ();
119   if (NULL != statistics)
120   {
121     GNUNET_STATISTICS_destroy (statistics, GNUNET_YES);
122     statistics = NULL;
123   }
124   if (NULL != peerstore)
125   {
126     GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_YES);
127     peerstore = NULL;
128   }
129   GNUNET_SCHEDULER_shutdown ();
130 }
131
132
133 /**
134  * A client disconnected.  Remove all of its data structure entries.
135  *
136  * @param cls closure, NULL
137  * @param client identification of the client
138  */
139 static void
140 handle_client_disconnect (void *cls,
141                           struct GNUNET_SERVER_Client *client)
142 {
143 }
144
145 /**
146  * Creates a structure with basic sensor info to be sent to a client.
147  *
148  * @param sensor sensor information
149  * @return message ready to be sent to client
150  */
151 static struct SensorInfoMessage *
152 create_sensor_info_msg (struct GNUNET_SENSOR_SensorInfo *sensor)
153 {
154   struct SensorInfoMessage *msg;
155   uint16_t len;
156   size_t name_len;
157   size_t desc_len;
158   char *str_ptr;
159
160   name_len = strlen(sensor->name);
161   if(NULL == sensor->description)
162     desc_len = 0;
163   else
164     desc_len = strlen(sensor->description) + 1;
165   len = 0;
166   len += sizeof(struct SensorInfoMessage);
167   len += name_len;
168   len += desc_len;
169   msg = GNUNET_malloc(len);
170   msg->header.size = htons(len);
171   msg->header.type = htons(GNUNET_MESSAGE_TYPE_SENSOR_INFO);
172   msg->name_len = htons(name_len);
173   msg->description_len = htons(desc_len);
174   msg->version_major = htons(sensor->version_major);
175   msg->version_minor = htons(sensor->version_minor);
176   str_ptr = (char*) &msg[1];
177   memcpy(str_ptr, sensor->name, name_len);
178   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor name (%d): %.*s\n",
179         name_len, name_len, str_ptr);
180   str_ptr += name_len;
181   memcpy(str_ptr, sensor->description, desc_len);
182   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor description (%d): %.*s\n",
183           desc_len, desc_len, str_ptr);
184
185   return msg;
186 }
187
188 /**
189  * Handle GET SENSOR message.
190  *
191  * @param cls closure
192  * @param client identification of the client
193  * @param message the actual message
194  */
195 static void
196 handle_get_sensor (void *cls,
197                    struct GNUNET_SERVER_Client *client,
198                    const struct GNUNET_MessageHeader *message)
199 {
200   struct GNUNET_SERVER_TransmitContext *tc;
201   char *sensorname;
202   size_t sensorname_len;
203   struct GNUNET_HashCode key;
204   struct GNUNET_SENSOR_SensorInfo *sensorinfo;
205   struct SensorInfoMessage *msg;
206
207   sensorname = (char *)&message[1];
208   sensorname_len = ntohs(message->size) - sizeof(struct GNUNET_MessageHeader);
209   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received for sensor (%d) `%.*s'\n",
210               "GET SENSOR", sensorname_len, sensorname_len, sensorname);
211   tc = GNUNET_SERVER_transmit_context_create (client);
212   GNUNET_CRYPTO_hash(sensorname, sensorname_len, &key);
213   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created key hash for requested sensor\n");
214   sensorinfo = (struct GNUNET_SENSOR_SensorInfo *)GNUNET_CONTAINER_multihashmap_get(sensors, &key);
215   if(NULL != sensorinfo)
216   {
217     msg = create_sensor_info_msg(sensorinfo);
218     GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
219     GNUNET_free(msg);
220   }
221   else
222     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Requested sensor `%.*s' was not found\n",
223         sensorname_len, sensorname);
224   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
225   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
226 }
227
228 /**
229  * Iterator for sensors and adds them to transmit context
230  *
231  * @param cls a `struct GNUNET_SERVER_TransmitContext *`
232  * @param key hash of sensor name, key to hashmap
233  * @param value a `struct GNUNET_SENSOR_SensorInfo *`
234  */
235 static int
236 add_sensor_to_tc (void *cls,
237                   const struct GNUNET_HashCode *key,
238                   void *value)
239 {
240   struct GNUNET_SERVER_TransmitContext *tc = cls;
241   struct GNUNET_SENSOR_SensorInfo *sensorinfo = value;
242   struct SensorInfoMessage *msg;
243
244   msg = create_sensor_info_msg(sensorinfo);
245   GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
246
247   GNUNET_free(msg);
248
249   return GNUNET_YES;
250 }
251
252 /**
253  * Handle GET ALL SENSORS message.
254  *
255  * @param cls closure
256  * @param client identification of the client
257  * @param message the actual message
258  */
259 static void
260 handle_get_all_sensors (void *cls, struct GNUNET_SERVER_Client *client,
261             const struct GNUNET_MessageHeader *message)
262 {
263   struct GNUNET_SERVER_TransmitContext *tc;
264
265   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received.\n",
266                 "GET ALL SENSOR");
267   tc = GNUNET_SERVER_transmit_context_create (client);
268   GNUNET_CONTAINER_multihashmap_iterate(sensors, &add_sensor_to_tc, tc);
269   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
270   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
271 }
272
273 /**
274  * Do a series of checks to determine if sensor should execute
275  *
276  * @return #GNUNET_YES / #GNUNET_NO
277  */
278 static int
279 should_run_sensor(struct GNUNET_SENSOR_SensorInfo *sensorinfo)
280 {
281   struct GNUNET_TIME_Absolute now;
282
283   if(GNUNET_NO == sensorinfo->enabled)
284   {
285     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' is disabled, will not run\n", sensorinfo->name);
286     return GNUNET_NO;
287   }
288   now = GNUNET_TIME_absolute_get();
289   if(NULL != sensorinfo->start_time
290       && now.abs_value_us < sensorinfo->start_time->abs_value_us)
291   {
292     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Start time for sensor `%s' not reached yet, will not run\n", sensorinfo->name);
293     return GNUNET_NO;
294   }
295   if(NULL != sensorinfo->end_time
296       && now.abs_value_us >= sensorinfo->end_time->abs_value_us)
297   {
298     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' expired, disabling.\n", sensorinfo->name);
299     set_sensor_enabled(sensorinfo, GNUNET_NO);
300     return GNUNET_NO;
301   }
302   return GNUNET_YES;
303 }
304
305 /**
306  * Callback function to process statistic values
307  *
308  * @param cls `struct GNUNET_SENSOR_SensorInfo *`
309  * @param subsystem name of subsystem that created the statistic
310  * @param name the name of the datum
311  * @param value the current value
312  * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
313  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
314  */
315 static int
316 sensor_statistics_iterator (void *cls,
317                             const char *ss,
318                             const char *name,
319                             uint64_t value,
320                             int is_persistent)
321 {
322   struct GNUNET_SENSOR_SensorInfo *sensorinfo = cls;
323   double dvalue = (double)value;
324   struct GNUNET_TIME_Absolute expiry;
325
326   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %" PRIu64 "\n", sensorinfo->name, value);
327   expiry = GNUNET_TIME_relative_to_absolute(sensorinfo->lifetime);
328   GNUNET_PEERSTORE_store(peerstore,
329       subsystem,
330       &peerid,
331       sensorinfo->name,
332       &dvalue,
333       sizeof(dvalue),
334       expiry,
335       GNUNET_PEERSTORE_STOREOPTION_MULTIPLE,
336       NULL,
337       NULL);
338   return GNUNET_SYSERR; /* We only want one value */
339 }
340
341 /**
342  * Continuation called after sensor gets all gnunet statistics values
343  *
344  * @param cls `struct GNUNET_SENSOR_SensorInfo *`
345  * @param success #GNUNET_OK if statistics were
346  *        successfully obtained, #GNUNET_SYSERR if not.
347  */
348 static void
349 end_sensor_run_stat (void *cls, int success)
350 {
351   struct GNUNET_SENSOR_SensorInfo *sensorinfo = cls;
352
353   sensorinfo->gnunet_stat_get_handle = NULL;
354   sensorinfo->running = GNUNET_NO;
355 }
356
357 /**
358  * Tries to parse a received sensor value to its
359  * expected datatype
360  *
361  * @param value the string value received, should be null terminated
362  * @param sensor sensor information struct
363  * @param ret pointer to parsed value
364  * @return size of new parsed value, 0 for error
365  */
366 static size_t
367 parse_sensor_value (const char *value,
368                     struct GNUNET_SENSOR_SensorInfo *sensor,
369                     void **ret)
370 {
371   double *dval;
372   char *endptr;
373
374   *ret = NULL;
375   if ('\0' == *value)
376     return 0;
377   if(0 == strcmp("numeric", sensor->expected_datatype))
378   {
379     dval = GNUNET_new(double);
380     *dval = strtod(value, &endptr);
381     if(value == endptr)
382       return 0;
383    *ret = dval;
384    return sizeof(double);
385   }
386   if(0 == strcmp("string", sensor->expected_datatype))
387   {
388     *ret = GNUNET_strdup(value);
389     return strlen(value) + 1;
390   }
391   GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
392       _("Unknown value type expected by sensor, this should not happen.\n"));
393   return 0;
394 }
395
396 /**
397  * Callback for output of executed sensor process
398  *
399  * @param cls `struct GNUNET_SENSOR_SensorInfo *`
400  * @param line line of output from a command, NULL for the end
401  */
402 static void
403 sensor_process_callback (void *cls, const char *line)
404 {
405   struct GNUNET_SENSOR_SensorInfo *sensorinfo = cls;
406   void *value;
407   size_t valsize;
408   struct GNUNET_TIME_Absolute expiry;
409
410   if(NULL == line)
411   {
412     GNUNET_OS_command_stop(sensorinfo->ext_cmd);
413     sensorinfo->ext_cmd = NULL;
414     sensorinfo->running = GNUNET_NO;
415     sensorinfo->ext_cmd_value_received = GNUNET_NO;
416     return;
417   }
418   if(GNUNET_YES == sensorinfo->ext_cmd_value_received)
419     return; /* We only want one *valid* value */
420   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a value for sensor `%s': %s\n", sensorinfo->name, line);
421   valsize = parse_sensor_value(line, sensorinfo, &value);
422   if (valsize == 0) /* invalid value, FIXME: should we disable the sensor now? */
423   {
424     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
425         _("Received an invalid value for sensor `%s': %s\n"),
426         sensorinfo->name, line);
427   }
428   else
429   {
430     sensorinfo->ext_cmd_value_received = GNUNET_YES;
431     expiry = GNUNET_TIME_relative_to_absolute(sensorinfo->lifetime);
432     GNUNET_PEERSTORE_store(peerstore,
433         subsystem,
434         &peerid,
435         sensorinfo->name,
436         value,
437         valsize,
438         expiry,
439         GNUNET_PEERSTORE_STOREOPTION_MULTIPLE,
440         NULL,
441         NULL);
442     GNUNET_free (value);
443   }
444 }
445
446 /**
447  * Checks if the given file is a path
448  *
449  * @return #GNUNET_YES / #GNUNET_NO
450  */
451 static int
452 is_path(char *filename)
453 {
454   size_t filename_len;
455   int i;
456
457   filename_len = strlen(filename);
458   for(i = 0; i < filename_len; i++)
459   {
460     if(DIR_SEPARATOR == filename[i])
461       return GNUNET_YES;
462   }
463   return GNUNET_NO;
464 }
465
466 /**
467  * Actual execution of a sensor
468  *
469  * @param cls 'struct SensorInfo'
470  * @param tc unsed
471  */
472 static void
473 sensor_run (void *cls,
474             const struct GNUNET_SCHEDULER_TaskContext * tc)
475 {
476   struct GNUNET_SENSOR_SensorInfo *sensorinfo = cls;
477   int check_result;
478   char *sensors_dir;
479   char *process_path;
480
481   sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &sensor_run, sensorinfo);
482   if(GNUNET_YES == sensorinfo->running) //FIXME: should we try to kill?
483   {
484     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sensor `%s' running for too long, will try again next interval\n", sensorinfo->name);
485     return;
486   }
487   if(GNUNET_NO == should_run_sensor(sensorinfo))
488     return;
489   sensorinfo->running = GNUNET_YES;
490   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Starting the execution of sensor `%s'\n", sensorinfo->name);
491   if(0 == strcmp ("gnunet-statistics", sensorinfo->source))
492   {
493     sensorinfo->gnunet_stat_get_handle = GNUNET_STATISTICS_get(statistics,
494         sensorinfo->gnunet_stat_service,
495         sensorinfo->gnunet_stat_name,
496         sensorinfo->interval, //try to get values only for the interval of the sensor
497         &end_sensor_run_stat,
498         &sensor_statistics_iterator,
499         sensorinfo);
500   }
501   else if(0 == strcmp ("process", sensorinfo->source))
502   {
503     if(GNUNET_YES == is_path(sensorinfo->ext_process))
504     {
505       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
506           _("Sensor `%s': External process should not be a path, disabling sensor.\n"),
507           sensorinfo->name);
508       set_sensor_enabled(sensorinfo, GNUNET_NO);
509       return;
510     }
511     //check if the process exists in $PATH
512     process_path = GNUNET_strdup(sensorinfo->ext_process);
513     check_result =
514         GNUNET_OS_check_helper_binary(process_path, GNUNET_NO, NULL);
515     if(GNUNET_SYSERR == check_result)
516     {
517       //search in sensor directory
518       sensors_dir = GNUNET_SENSOR_get_sensor_dir ();
519       GNUNET_free(process_path);
520       GNUNET_asprintf(&process_path,
521                       "%s%s-files%s%s",
522                       sensors_dir,
523                       sensorinfo->name,
524                       DIR_SEPARATOR_STR,
525                       sensorinfo->ext_process);
526       GNUNET_free(sensors_dir);
527       check_result =
528         GNUNET_OS_check_helper_binary(process_path, GNUNET_NO, NULL);
529     }
530     if(GNUNET_SYSERR == check_result)
531     {
532       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
533           _("Sensor `%s' process `%s' problem: binary doesn't exist or not executable\n"),
534           sensorinfo->name,
535           sensorinfo->ext_process);
536       set_sensor_enabled(sensorinfo, GNUNET_NO);
537       sensorinfo->running = GNUNET_NO;
538       GNUNET_free(process_path);
539       return;
540     }
541     sensorinfo->ext_cmd_value_received = GNUNET_NO;
542     sensorinfo->ext_cmd = GNUNET_OS_command_run(&sensor_process_callback,
543         sensorinfo,
544         GNUNET_TIME_UNIT_FOREVER_REL,
545         process_path,
546         sensorinfo->ext_process,
547         sensorinfo->ext_args,
548         NULL);
549     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Process started for sensor `%s'\n", sensorinfo->name);
550     GNUNET_free(process_path);
551   }
552   else
553   {
554     sensorinfo->running = GNUNET_NO;
555     GNUNET_break(0); //shouldn't happen
556   }
557 }
558
559 /**
560  * Starts the execution of a sensor
561  *
562  * @param cls unused
563  * @param key hash of sensor name, key to hashmap (unused)
564  * @param value a `struct GNUNET_SENSOR_SensorInfo *`
565  * @return #GNUNET_YES if we should continue to
566  *         iterate,
567  *         #GNUNET_NO if not.
568  */
569 static int
570 schedule_sensor (void *cls, const struct GNUNET_HashCode *key, void *value)
571 {
572   struct GNUNET_SENSOR_SensorInfo *sensorinfo = value;
573
574   if(GNUNET_NO == should_run_sensor(sensorinfo))
575     return GNUNET_YES;
576   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Scheduling sensor `%s' to run after %" PRIu64 " microseconds\n",
577       sensorinfo->name, sensorinfo->interval.rel_value_us);
578   if(GNUNET_SCHEDULER_NO_TASK != sensorinfo->execution_task)
579   {
580     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
581         _("Sensor `%s' execution task already set, this should not happen\n"), sensorinfo->name);
582     return GNUNET_NO;
583   }
584   sensorinfo->execution_task = GNUNET_SCHEDULER_add_delayed(sensorinfo->interval, &sensor_run, sensorinfo);
585   return GNUNET_YES;
586 }
587
588 /**
589  * Starts the execution of all enabled sensors
590  *
591  */
592 static void
593 schedule_all_sensors()
594 {
595   GNUNET_CONTAINER_multihashmap_iterate(sensors, &schedule_sensor, NULL);
596 }
597
598
599 /**
600  * Loads sensors and starts different service components
601  */
602 static void
603 start ()
604 {
605   sensors = GNUNET_SENSOR_load_all_sensors ();
606   schedule_all_sensors();
607   SENSOR_analysis_start(cfg, sensors);
608   SENSOR_reporting_start(cfg, sensors);
609   SENSOR_update_start (cfg, sensors, &reset);
610 }
611
612
613 /**
614  * Process statistics requests.
615  *
616  * @param cls closure
617  * @param server the initialized server
618  * @param c configuration to use
619  */
620 static void
621 run (void *cls,
622      struct GNUNET_SERVER_Handle *server,
623      const struct GNUNET_CONFIGURATION_Handle *c)
624 {
625   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
626     {&handle_get_sensor, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GET,
627      0},
628     {&handle_get_all_sensors, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GETALL,
629      sizeof (struct GNUNET_MessageHeader)},
630     {NULL, NULL, 0, 0}
631   };
632
633   cfg = c;
634   statistics = GNUNET_STATISTICS_create("sensor", cfg);
635   GNUNET_CRYPTO_get_peer_identity(cfg, &peerid);
636   peerstore = GNUNET_PEERSTORE_connect(cfg);
637   GNUNET_SERVER_add_handlers (server, handlers);
638   GNUNET_SERVER_disconnect_notify (server, 
639            &handle_client_disconnect,
640            NULL);
641   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
642         &shutdown_task,
643         NULL);
644   start ();
645 }
646
647
648 /**
649  * Resets the service by stopping components, reloading sensors and starting
650  * components. This is needed when we receive new sensor updates.
651  */
652 static void
653 reset ()
654 {
655   stop ();
656   start ();
657 }
658
659
660 /**
661  * The main function for the sensor service.
662  *
663  * @param argc number of arguments from the command line
664  * @param argv command line arguments
665  * @return 0 ok, 1 on error
666  */
667 int
668 main (int argc, char *const *argv)
669 {
670   return (GNUNET_OK ==
671           GNUNET_SERVICE_run (argc,
672                               argv,
673                               "sensor",
674             GNUNET_SERVICE_OPTION_NONE,
675             &run, NULL)) ? 0 : 1;
676 }
677
678 /* end of gnunet-service-sensor.c */