towards sensor scheduling
[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
31 /**
32  * Structure containing sensor definition
33  */
34 struct SensorInfo
35 {
36
37   /*
38    * Sensor name
39    */
40   char *name;
41
42   /*
43    * Path to definition file
44    */
45   char *def_file;
46
47   /*
48    * First part of version number
49    */
50   uint16_t version_major;
51
52   /*
53    * Second part of version number
54    */
55   uint16_t version_minor;
56
57   /*
58    * Sensor description
59    */
60   char *description;
61
62   /*
63    * Sensor currently enabled
64    */
65   int enabled;
66
67   /*
68    * Category under which the sensor falls (e.g. tcp, datastore)
69    */
70   char *category;
71
72   /*
73    * When does the sensor become active
74    */
75   struct GNUNET_TIME_Absolute *start_time;
76
77   /*
78    * When does the sensor expire
79    */
80   struct GNUNET_TIME_Absolute *end_time;
81
82   /*
83    * Time interval to collect sensor information (e.g. every 1 min)
84    */
85   struct GNUNET_TIME_Relative *interval;
86
87   /*
88    * Lifetime of an information sample after which it is deleted from storage
89    */
90   struct GNUNET_TIME_Relative *lifetime;
91
92   /*
93    * A set of required peer capabilities for the sensor to collect meaningful information (e.g. ipv6)
94    */
95   char *capabilities;
96
97   /*
98    * Either "gnunet-statistics" or external "process"
99    */
100   char *source;
101
102   /*
103    * Name of the GNUnet service that is the source for the gnunet-statistics entry
104    */
105   char *gnunet_stat_service;
106
107   /*
108    * Name of the gnunet-statistics entry
109    */
110   char *gnunet_stat_name;
111
112   /*
113    * Name of the external process to be executed
114    */
115   char *ext_process;
116
117   /*
118    * Arguments to be passed to the external process
119    */
120   char *ext_args;
121
122   /*
123    * The output datatype to be expected
124    */
125   char *expected_datatype;
126
127   /*
128    * Peer-identity of peer running collection point
129    */
130   struct GNUNET_PeerIdentity *collection_point;
131
132   /*
133    * Time interval to send sensor information to collection point (e.g. every 30 mins)
134    */
135   struct GNUNET_TIME_Relative *collection_interval;
136
137   /*
138    * Flag specifying if value is to be communicated to the p2p network
139    */
140   int p2p_report;
141
142   /*
143    * Time interval to communicate value to the p2p network
144    */
145   struct GNUNET_TIME_Relative *p2p_interval;
146
147 };
148
149 /**
150  * Our configuration.
151  */
152 static const struct GNUNET_CONFIGURATION_Handle *cfg;
153
154 /**
155  * Hashmap of loaded sensor definitions
156  */
157 struct GNUNET_CONTAINER_MultiHashMap *sensors;
158
159 /**
160  * Task run during shutdown.
161  *
162  * @param cls unused
163  * @param tc unused
164  */
165 static void
166 shutdown_task (void *cls,
167                const struct GNUNET_SCHEDULER_TaskContext *tc)
168 {
169   GNUNET_SCHEDULER_shutdown();
170 }
171
172
173 /**
174  * A client disconnected.  Remove all of its data structure entries.
175  *
176  * @param cls closure, NULL
177  * @param client identification of the client
178  */
179 static void
180 handle_client_disconnect (void *cls,
181                           struct GNUNET_SERVER_Client
182                           * client)
183 {
184 }
185
186 /**
187  * Parses a version number string into major and minor
188  *
189  * @param version full version string
190  * @param major pointer to parsed major value
191  * @param minor pointer to parsed minor value
192  * @return #GNUNET_OK if parsing went ok, #GNUNET_SYSERROR in case of error
193  */
194 static int
195 version_parse(char *version, uint16_t *major, uint16_t *minor)
196 {
197   int majorval = 0;
198   int minorval = 0;
199
200   for(; isdigit(*version); version++)
201   {
202     majorval *= 10;
203     majorval += *version - '0';
204   }
205   if(*version != '.')
206     return GNUNET_SYSERR;
207   version++;
208   for(; isdigit(*version); version++)
209   {
210     minorval *= 10;
211     minorval += *version - '0';
212   }
213   if(*version != 0)
214     return GNUNET_SYSERR;
215   *major = majorval;
216   *minor = minorval;
217
218   return GNUNET_OK;
219 }
220
221 /**
222  * Load sensor definition from configuration
223  *
224  * @param cfg configuration handle
225  * @param sectionname configuration section containing definition
226  */
227 static struct SensorInfo *
228 load_sensor_from_cfg(struct GNUNET_CONFIGURATION_Handle *cfg, const char *sectionname)
229 {
230   struct SensorInfo *sensor;
231   char *version_str;
232   char *starttime_str;
233   char *endtime_str;
234   unsigned long long interval_sec;
235   struct GNUNET_TIME_Relative interval;
236
237   sensor = GNUNET_new(struct SensorInfo);
238   //name
239   sensor->name = GNUNET_strdup(sectionname);
240   //version
241   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "VERSION", &version_str))
242   {
243     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor version\n"));
244     GNUNET_free(sensor);
245     return NULL;
246   }
247   if(GNUNET_OK != version_parse(version_str, &(sensor->version_major), &(sensor->version_minor)))
248   {
249     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Invalid sensor version number, format should be major.minor\n"));
250     GNUNET_free(sensor);
251     GNUNET_free(version_str);
252     return NULL;
253   }
254   GNUNET_free(version_str);
255   //description
256   GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "DESCRIPTION", &sensor->description);
257   //category
258   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "CATEGORY", &sensor->category) ||
259         NULL == sensor->category)
260   {
261     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor category\n"));
262     GNUNET_free(sensor);
263     return NULL;
264   }
265   //enabled
266   if(GNUNET_NO == GNUNET_CONFIGURATION_get_value_yesno(cfg, sectionname, "ENABLED"))
267     sensor->enabled = GNUNET_NO;
268   else
269     sensor->enabled = GNUNET_YES;
270   //start time
271   sensor->start_time = NULL;
272   if(GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "START_TIME", &starttime_str))
273   {
274     GNUNET_STRINGS_fancy_time_to_absolute(starttime_str, sensor->start_time);
275     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Start time loaded: `%s'. Parsed: %d\n", starttime_str, (NULL != sensor->start_time));
276     GNUNET_free(starttime_str);
277   }
278   //end time
279   sensor->end_time = NULL;
280   if(GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, sectionname, "END_TIME", &endtime_str))
281   {
282     GNUNET_STRINGS_fancy_time_to_absolute(endtime_str, sensor->end_time);
283     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "End time loaded: `%s'. Parsed: %d\n", endtime_str, (NULL != sensor->end_time));
284     GNUNET_free(endtime_str);
285   }
286   //interval
287   if(GNUNET_OK != GNUNET_CONFIGURATION_get_value_number(cfg, sectionname, "INTERVAL", &interval_sec))
288   {
289     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error reading sensor run interval\n"));
290     GNUNET_free(sensor);
291     return NULL;
292   }
293   interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, interval_sec);
294   sensor->interval = &interval;
295   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Interval loaded: %" PRIu64 "\n", sensor->interval->rel_value_us);
296
297   return sensor;
298 }
299
300 /**
301  * Load sensor definition from file
302  *
303  * @param filename full path to file containing sensor definition
304  */
305 static struct SensorInfo *
306 load_sensor_from_file(const char *filename)
307 {
308   struct GNUNET_CONFIGURATION_Handle *sensorcfg;
309   const char *filebasename;
310   struct SensorInfo *sensor;
311
312   //test file
313   if(GNUNET_YES != GNUNET_DISK_file_test(filename))
314   {
315     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to access sensor file: %s\n"), filename);
316     return NULL;
317   }
318   //load file as configuration
319   sensorcfg = GNUNET_CONFIGURATION_create();
320   if(GNUNET_SYSERR == GNUNET_CONFIGURATION_parse(sensorcfg, filename))
321   {
322     GNUNET_CONFIGURATION_destroy(sensorcfg);
323     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to load sensor definition: %s\n"), filename);
324     return NULL;
325   }
326   //configuration section should be the same as filename
327   filebasename = GNUNET_STRINGS_get_short_name(filename);
328   sensor = load_sensor_from_cfg(sensorcfg, filebasename);
329   sensor->def_file = GNUNET_strdup(filename);
330
331   GNUNET_CONFIGURATION_destroy(sensorcfg);
332
333   return sensor;
334 }
335
336 /**
337  * Compares version numbers of two sensors
338  *
339  * @param s1 first sensor
340  * @param s2 second sensor
341  * @return 1: s1 > s2, 0: s1 == s2, -1: s1 < s2
342  */
343 static int
344 sensor_version_compare(struct SensorInfo *s1, struct SensorInfo *s2)
345 {
346   if(s1->version_major == s2->version_major)
347     return (s1->version_minor < s2->version_minor) ? -1 : (s1->version_minor > s2->version_minor);
348   else
349     return (s1->version_major < s2->version_major) ? -1 : (s1->version_major > s2->version_major);
350 }
351
352 /**
353  * Adds a new sensor to given hashmap.
354  * If the same name exist, compares versions and update if old.
355  *
356  * @param sensor Sensor structure to add
357  * @param map Hashmap to add to
358  * @return #GNUNET_YES if added, #GNUNET_NO if not added which is not necessarily an error
359  */
360 static int
361 add_sensor_to_hashmap(struct SensorInfo *sensor, struct GNUNET_CONTAINER_MultiHashMap *map)
362 {
363   struct GNUNET_HashCode key;
364   struct SensorInfo *existing;
365
366   GNUNET_CRYPTO_hash(sensor->name, strlen(sensor->name), &key);
367   existing = GNUNET_CONTAINER_multihashmap_get(map, &key);
368   if(NULL != existing) //sensor with same name already exists
369   {
370     if(sensor_version_compare(existing, sensor) >= 0) //same or newer version already exist
371     {
372       GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Sensor `%s' already exists with same or newer version\n"), sensor->name);
373       return GNUNET_NO;
374     }
375     else
376     {
377       GNUNET_CONTAINER_multihashmap_remove(map, &key, existing); //remove the old version
378       GNUNET_free(existing);
379       GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Upgrading sensor `%s' to a newer version\n"), sensor->name);
380     }
381   }
382   if(GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put(map, &key, sensor, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
383   {
384     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error adding new sensor `%s' to global hashmap, this should not happen\n"), sensor->name);
385     return GNUNET_NO;
386   }
387
388   return GNUNET_YES;
389 }
390
391 /**
392  * Iterating over files in sensors directory
393  *
394  * @param cls closure
395  * @param filename complete filename (absolute path)
396  * @return #GNUNET_OK to continue to iterate,
397  *  #GNUNET_NO to stop iteration with no error,
398  *  #GNUNET_SYSERR to abort iteration with error!
399  */
400 static int
401 reload_sensors_dir_cb(void *cls, const char *filename)
402 {
403   struct SensorInfo *sensor;
404
405   sensor = load_sensor_from_file(filename);
406   if(NULL == sensor)
407   {
408     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Error loading sensor from file: %s\n"), filename);
409     return GNUNET_OK;
410   }
411   if(GNUNET_YES == add_sensor_to_hashmap(sensor, sensors))
412     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, _("Sensor `%s' added to global hashmap\n"), sensor->name);
413   else
414     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, ("Could not add sensor `%s' to global hashmap\n"), sensor->name);
415
416   return GNUNET_OK;
417 }
418
419 /*
420  * Get path to the directory containing the sensor definition files
421  *
422  * @return sensor files directory
423  */
424 static char *
425 get_sensor_dir()
426 {
427   char* datadir;
428   char* sensordir;
429
430   datadir = GNUNET_OS_installation_get_path(GNUNET_OS_IPK_DATADIR);
431   GNUNET_asprintf(&sensordir, "%ssensors%s",
432       datadir, DIR_SEPARATOR_STR);
433
434   return sensordir;
435 }
436
437 /**
438  * Reads sensor definitions from data files
439  *
440  */
441 static void
442 reload_sensors()
443 {
444   char* sensordir;
445   int filesfound;
446
447   sensordir = get_sensor_dir();
448   GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Reloading sensor definitions from directory `%s'\n"), sensordir);
449   GNUNET_assert(GNUNET_YES == GNUNET_DISK_directory_test(sensordir, GNUNET_YES));
450
451   //read all files in sensors directory
452   filesfound = GNUNET_DISK_directory_scan(sensordir, &reload_sensors_dir_cb, NULL);
453   GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Loaded %d/%d sensors from directory `%s'\n"),
454       GNUNET_CONTAINER_multihashmap_size(sensors), filesfound, sensordir);
455 }
456
457 /**
458  * Creates a structure with basic sensor info to be sent to a client
459  *
460  * @param sensor sensor information
461  * @return message ready to be sent to client
462  */
463 static struct SensorInfoMessage *
464 create_sensor_info_msg(struct SensorInfo *sensor)
465 {
466   struct SensorInfoMessage *msg;
467   uint16_t len;
468   size_t name_len;
469   size_t desc_len;
470   char *str_ptr;
471
472   name_len = strlen(sensor->name);
473   if(NULL == sensor->description)
474     desc_len = 0;
475   else
476     desc_len = strlen(sensor->description);
477   len = 0;
478   len += sizeof(struct SensorInfoMessage);
479   len += name_len;
480   len += desc_len;
481   msg = GNUNET_malloc(len);
482   msg->header.size = htons(len);
483   msg->header.type = htons(GNUNET_MESSAGE_TYPE_SENSOR_INFO);
484   msg->name_len = htons(name_len);
485   msg->description_len = htons(desc_len);
486   msg->version_major = htons(sensor->version_major);
487   msg->version_minor = htons(sensor->version_minor);
488   str_ptr = (char*) &msg[1];
489   memcpy(str_ptr, sensor->name, name_len);
490   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor name (%d): %.*s\n",
491         name_len, name_len, str_ptr);
492   str_ptr += name_len;
493   memcpy(str_ptr, sensor->description, desc_len);
494   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending sensor description (%d): %.*s\n",
495           desc_len, desc_len, str_ptr);
496
497   return msg;
498 }
499
500 /**
501  * Handle GET SENSOR message.
502  *
503  * @param cls closure
504  * @param client identification of the client
505  * @param message the actual message
506  */
507 static void
508 handle_get_sensor (void *cls, struct GNUNET_SERVER_Client *client,
509             const struct GNUNET_MessageHeader *message)
510 {
511   struct GNUNET_SERVER_TransmitContext *tc;
512   char *sensorname;
513   size_t sensorname_len;
514   struct GNUNET_HashCode key;
515   struct SensorInfo *sensorinfo;
516   struct SensorInfoMessage *msg;
517
518   sensorname = (char *)&message[1];
519   sensorname_len = ntohs(message->size) - sizeof(struct GNUNET_MessageHeader);
520   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received for sensor (%d) `%.*s'\n",
521               "GET SENSOR", sensorname_len, sensorname_len, sensorname);
522   tc = GNUNET_SERVER_transmit_context_create (client);
523   GNUNET_CRYPTO_hash(sensorname, sensorname_len, &key);
524   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created key hash for requested sensor\n");
525   sensorinfo = (struct SensorInfo *)GNUNET_CONTAINER_multihashmap_get(sensors, &key);
526   if(NULL != sensorinfo)
527   {
528     msg = create_sensor_info_msg(sensorinfo);
529     GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
530     GNUNET_free(msg);
531   }
532   else
533     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Requested sensor `%.*s' was not found\n",
534         sensorname_len, sensorname);
535   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
536   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
537 }
538
539 /**
540  * Iterator for sensors and adds them to transmit context
541  *
542  * @param cls a 'struct GNUNET_SERVER_TransmitContext *'
543  * @param key hash of sensor name, key to hashmap
544  * @param value a 'struct SensorInfo *'
545  */
546 int add_sensor_to_tc(void *cls,
547     const struct GNUNET_HashCode *key, void *value)
548 {
549   struct GNUNET_SERVER_TransmitContext *tc = cls;
550   struct SensorInfo *sensorinfo = value;
551   struct SensorInfoMessage *msg;
552
553   msg = create_sensor_info_msg(sensorinfo);
554   GNUNET_SERVER_transmit_context_append_message(tc, (struct GNUNET_MessageHeader *)msg);
555
556   GNUNET_free(msg);
557
558   return GNUNET_YES;
559 }
560
561 /**
562  * Handle GET ALL SENSORS message.
563  *
564  * @param cls closure
565  * @param client identification of the client
566  * @param message the actual message
567  */
568 static void
569 handle_get_all_sensors (void *cls, struct GNUNET_SERVER_Client *client,
570             const struct GNUNET_MessageHeader *message)
571 {
572   struct GNUNET_SERVER_TransmitContext *tc;
573
574   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' message received.\n",
575                 "GET ALL SENSOR");
576   tc = GNUNET_SERVER_transmit_context_create (client);
577   GNUNET_CONTAINER_multihashmap_iterate(sensors, &add_sensor_to_tc, tc);
578   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_SENSOR_END);
579   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
580 }
581
582 /**
583  * Do a series of checks to determine if sensor should execute
584  *
585  * @return #GNUNET_YES / #GNUNET_NO
586  */
587 static int
588 should_run_sensor(struct SensorInfo *sensorinfo)
589 {
590   struct GNUNET_TIME_Absolute now;
591
592   if(GNUNET_NO == sensorinfo->enabled)
593   {
594     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sensor `%s' is disabled, will not run\n");
595     return GNUNET_NO;
596   }
597   now = GNUNET_TIME_absolute_get();
598   if(NULL != sensorinfo->start_time
599       && now.abs_value_us < sensorinfo->start_time->abs_value_us)
600   {
601     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Start time for sensor `%s' not reached yet, will not run\n");
602     return GNUNET_NO;
603   }
604   if(NULL != sensorinfo->end_time
605       && now.abs_value_us >= sensorinfo->end_time->abs_value_us)
606   {
607     GNUNET_log(GNUNET_ERROR_TYPE_INFO, "End time for sensor `%s' passed, will not run\n");
608     return GNUNET_NO;
609   }
610   return GNUNET_YES;
611 }
612
613 /**
614  * Actual execution of a sensor
615  *
616  * @param cls 'struct SensorInfo'
617  * @param tc unsed
618  */
619 void
620 run_sensor (void *cls,
621     const struct GNUNET_SCHEDULER_TaskContext * tc)
622 {
623   struct SensorInfo *sensorinfo = cls;
624
625   if(GNUNET_NO == should_run_sensor(sensorinfo))
626     return;
627   //GNUNET_SCHEDULER_add_delayed(*sensorinfo->interval, &run_sensor, sensorinfo);
628   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Starting the execution of sensor `%s'\n", sensorinfo->name);
629 }
630
631 /**
632  * Starts the execution of a sensor
633  *
634  * @param cls unused
635  * @param key hash of sensor name, key to hashmap
636  * @param value a 'struct SensorInfo *'
637  * @return #GNUNET_YES if we should continue to
638  *         iterate,
639  *         #GNUNET_NO if not.
640  */
641 int schedule_sensor(void *cls,
642     const struct GNUNET_HashCode *key, void *value)
643 {
644   struct SensorInfo *sensorinfo = value;
645
646   if(GNUNET_NO == should_run_sensor(sensorinfo))
647     return GNUNET_YES;
648   GNUNET_SCHEDULER_add_delayed(*sensorinfo->interval, &run_sensor, sensorinfo);
649   return GNUNET_YES;
650 }
651
652 /**
653  * Starts the execution of all enabled sensors
654  *
655  */
656 static void
657 schedule_all_sensors()
658 {
659   GNUNET_CONTAINER_multihashmap_iterate(sensors, &schedule_sensor, NULL);
660 }
661
662 /**
663  * Process statistics requests.
664  *
665  * @param cls closure
666  * @param server the initialized server
667  * @param c configuration to use
668  */
669 static void
670 run (void *cls,
671      struct GNUNET_SERVER_Handle *server,
672      const struct GNUNET_CONFIGURATION_Handle *c)
673 {
674   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
675     {&handle_get_sensor, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GET,
676      0},
677     {&handle_get_all_sensors, NULL, GNUNET_MESSAGE_TYPE_SENSOR_GETALL,
678      sizeof (struct GNUNET_MessageHeader)},
679     {NULL, NULL, 0, 0}
680   };
681
682   cfg = c;
683   sensors = GNUNET_CONTAINER_multihashmap_create(10, GNUNET_NO);
684   reload_sensors();
685   schedule_all_sensors();
686   GNUNET_SERVER_add_handlers (server, handlers);
687   GNUNET_SERVER_disconnect_notify (server, 
688                                    &handle_client_disconnect,
689                                    NULL);
690   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
691                                 &shutdown_task,
692                                 NULL);
693 }
694
695
696 /**
697  * The main function for the sensor service.
698  *
699  * @param argc number of arguments from the command line
700  * @param argv command line arguments
701  * @return 0 ok, 1 on error
702  */
703 int
704 main (int argc, char *const *argv)
705 {
706   return (GNUNET_OK ==
707           GNUNET_SERVICE_run (argc,
708                               argv,
709                               "sensor",
710                               GNUNET_SERVICE_OPTION_NONE,
711                               &run, NULL)) ? 0 : 1;
712 }
713
714 /* end of gnunet-service-sensor.c */