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