start service
[oweals/gnunet.git] / src / peerinfo / gnunet-service-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2004, 2005, 2007, 2009, 2010 Christian Grothoff (and other contributing authors)
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 peerinfo/gnunet-service-peerinfo.c
23  * @brief maintains list of known peers
24  *
25  * Code to maintain the list of currently known hosts (in memory
26  * structure of data/hosts/).
27  *
28  * @author Christian Grothoff
29  *
30  * TODO:
31  * - HostEntries are never 'free'd (add expiration, upper bound?)
32  */
33
34 #include "platform.h"
35 #include "gnunet_crypto_lib.h"
36 #include "gnunet_container_lib.h"
37 #include "gnunet_disk_lib.h"
38 #include "gnunet_hello_lib.h"
39 #include "gnunet_protocols.h"
40 #include "gnunet_service_lib.h"
41 #include "gnunet_statistics_service.h"
42 #include "peerinfo.h"
43
44 /**
45  * How often do we scan the HOST_DIR for new entries?
46  */
47 #define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
48
49 /**
50  * How often do we discard old entries in data/hosts/?
51  */
52 #define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
53
54 #define WRITE_TO_DISK GNUNET_NO
55
56 /**
57  * In-memory cache of known hosts.
58  */
59 struct HostEntry
60 {
61
62   /**
63    * Identity of the peer.
64    */
65   struct GNUNET_PeerIdentity identity;
66
67   /**
68    * Hello for the peer (can be NULL)
69    */
70   struct GNUNET_HELLO_Message *hello;
71
72 };
73
74
75 /**
76  * The in-memory list of known hosts, mapping of
77  * host IDs to 'struct HostEntry*' values.
78  */
79 static struct GNUNET_CONTAINER_MultiHashMap *hostmap;
80
81 /**
82  * Clients to immediately notify about all changes.
83  */
84 static struct GNUNET_SERVER_NotificationContext *notify_list;
85
86 /**
87  * Directory where the hellos are stored in (data/hosts)
88  */
89 static char *networkIdDirectory;
90
91 /**
92  * Handle for reporting statistics.
93  */
94 static struct GNUNET_STATISTICS_Handle *stats;
95
96
97 /**
98  * Notify all clients in the notify list about the
99  * given host entry changing.
100  */
101 static struct InfoMessage *
102 make_info_message (const struct HostEntry *he)
103 {
104   struct InfoMessage *im;
105   size_t hs;
106
107   hs = (he->hello == NULL) ? 0 : GNUNET_HELLO_size (he->hello);
108   im = GNUNET_malloc (sizeof (struct InfoMessage) + hs);
109   im->header.size = htons (hs + sizeof (struct InfoMessage));
110   im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
111   im->peer = he->identity;
112   if (he->hello != NULL)
113     memcpy (&im[1], he->hello, hs);
114   return im;
115 }
116
117
118 #if WRITE_TO_DISK
119 /**
120  * Address iterator that causes expired entries to be discarded.
121  *
122  * @param cls pointer to the current time
123  * @param tname name of the transport
124  * @param expiration expiration time for the address
125  * @param addr the address
126  * @param addrlen length of addr in bytes
127  * @return GNUNET_NO if expiration smaller than the current time
128  */
129 static int
130 discard_expired (void *cls,
131                  const char *tname,
132                  struct GNUNET_TIME_Absolute expiration,
133                  const void *addr, uint16_t addrlen)
134 {
135   const struct GNUNET_TIME_Absolute *now = cls;
136   if (now->abs_value > expiration.abs_value)
137     {
138       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
139                   _("Removing expired address of transport `%s'\n"),
140                   tname);
141       return GNUNET_NO;
142     }
143   return GNUNET_OK;
144 }
145
146
147 /**
148  * Get the filename under which we would store the GNUNET_HELLO_Message
149  * for the given host and protocol.
150  * @return filename of the form DIRECTORY/HOSTID
151  */
152 static char *
153 get_host_filename (const struct GNUNET_PeerIdentity *id)
154 {
155   struct GNUNET_CRYPTO_HashAsciiEncoded fil;
156   char *fn;
157
158   GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
159   GNUNET_asprintf (&fn,
160                    "%s%s%s", networkIdDirectory, DIR_SEPARATOR_STR, &fil);
161   return fn;
162 }
163 #endif
164
165
166 /**
167  * Broadcast information about the given entry to all 
168  * clients that care.
169  *
170  * @param entry entry to broadcast about
171  */
172 static void
173 notify_all (struct HostEntry *entry)
174 {
175   struct InfoMessage *msg;
176
177   msg = make_info_message (entry);
178   GNUNET_SERVER_notification_context_broadcast (notify_list,
179                                                 &msg->header,
180                                                 GNUNET_NO);
181   GNUNET_free (msg);
182 }
183
184
185 /**
186  * Add a host to the list.
187  *
188  * @param identity the identity of the host
189  */
190 static void
191 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
192 {
193   struct HostEntry *entry;
194 #if WRITE_TO_DISK
195   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
196   const struct GNUNET_HELLO_Message *hello;
197   struct GNUNET_HELLO_Message *hello_clean;
198   int size;
199   struct GNUNET_TIME_Absolute now;
200   char *fn;
201 #endif
202
203   entry = GNUNET_CONTAINER_multihashmap_get (hostmap,
204                                              &identity->hashPubKey);
205   if (entry != NULL)
206     return;
207   GNUNET_STATISTICS_update (stats,
208                             gettext_noop ("# peers known"),
209                             1,
210                             GNUNET_NO);
211   entry = GNUNET_malloc (sizeof (struct HostEntry));
212   entry->identity = *identity;
213 #if WRITE_TO_DISK
214   fn = get_host_filename (identity);
215   if (GNUNET_DISK_file_test (fn) == GNUNET_YES)
216     {
217       size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
218       hello = (const struct GNUNET_HELLO_Message *) buffer;
219       if ( (size < sizeof (struct GNUNET_MessageHeader)) ||
220            (size != ntohs((((const struct GNUNET_MessageHeader*) hello)->size))) ||
221            (size != GNUNET_HELLO_size (hello)) )
222         {
223           GNUNET_break (0);
224           if (0 != UNLINK (fn))
225             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
226                                       "unlink",
227                                       fn);
228         }
229       else
230         {
231           now = GNUNET_TIME_absolute_get ();
232           hello_clean = GNUNET_HELLO_iterate_addresses (hello,
233                                                         GNUNET_YES,
234                                                         &discard_expired, &now);
235           entry->hello = hello_clean;
236         }
237     }
238   GNUNET_free (fn);
239 #endif
240   GNUNET_CONTAINER_multihashmap_put (hostmap,
241                                      &identity->hashPubKey,
242                                      entry,
243                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
244   notify_all (entry);
245 }
246
247 #if WRITE_TO_DISK
248 /**
249  * Remove a file that should not be there.  LOG
250  * success or failure.
251  */
252 static void
253 remove_garbage (const char *fullname)
254 {
255   if (0 == UNLINK (fullname))
256     GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
257                 _
258                 ("File `%s' in directory `%s' does not match naming convention. "
259                  "Removed.\n"), fullname, networkIdDirectory);
260   else
261     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
262                               GNUNET_ERROR_TYPE_BULK, "unlink", fullname);
263 }
264
265
266 static int
267 hosts_directory_scan_callback (void *cls,
268                                const char *fullname)
269 {
270   unsigned int *matched = cls;
271   struct GNUNET_PeerIdentity identity;
272   const char *filename;
273
274   if (GNUNET_DISK_file_test (fullname) != GNUNET_YES)
275     return GNUNET_OK;           /* ignore non-files */
276   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
277     {
278       remove_garbage (fullname);
279       return GNUNET_OK;
280     }
281   filename =
282     &fullname[strlen (fullname) -
283               sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) + 1];
284   if (filename[-1] != DIR_SEPARATOR)
285     {
286       remove_garbage (fullname);
287       return GNUNET_OK;
288     }
289   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (filename,
290                                                    &identity.hashPubKey))
291     {
292       remove_garbage (fullname);
293       return GNUNET_OK;
294     }
295   (*matched)++;
296   add_host_to_known_hosts (&identity);
297   return GNUNET_OK;
298 }
299
300
301 /**
302  * Call this method periodically to scan data/hosts for new hosts.
303  */
304 static void
305 cron_scan_directory_data_hosts (void *cls,
306                                 const struct GNUNET_SCHEDULER_TaskContext *tc)
307 {
308   static unsigned int retries;
309   unsigned int count;
310
311   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
312     return;
313   count = 0;
314   if (GNUNET_SYSERR == GNUNET_DISK_directory_create (networkIdDirectory))
315     {
316       GNUNET_SCHEDULER_add_delayed (DATA_HOST_FREQ,
317                                     &cron_scan_directory_data_hosts, NULL);
318       return;
319     }
320   GNUNET_DISK_directory_scan (networkIdDirectory,
321                               &hosts_directory_scan_callback, &count);
322   if ((0 == count) && (0 == (++retries & 31)))
323     GNUNET_log (GNUNET_ERROR_TYPE_WARNING |
324                 GNUNET_ERROR_TYPE_BULK,
325                 _("Still no peers found in `%s'!\n"), networkIdDirectory);
326   GNUNET_SCHEDULER_add_delayed (DATA_HOST_FREQ,
327                                 &cron_scan_directory_data_hosts, NULL);
328 }
329 #endif
330
331 /**
332  * Bind a host address (hello) to a hostId.
333  *
334  * @param peer the peer for which this is a hello
335  * @param hello the verified (!) hello message
336  */
337 static void
338 bind_address (const struct GNUNET_PeerIdentity *peer,
339               const struct GNUNET_HELLO_Message *hello)
340 {
341 #if WRITE_TO_DISK
342   char *fn;
343 #endif
344   struct HostEntry *host;
345   struct GNUNET_HELLO_Message *mrg;
346   struct GNUNET_TIME_Absolute delta;
347
348   add_host_to_known_hosts (peer);
349   host = GNUNET_CONTAINER_multihashmap_get (hostmap,
350                                             &peer->hashPubKey);
351   GNUNET_assert (host != NULL);
352   if (host->hello == NULL)
353     {
354       host->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
355       memcpy (host->hello, hello, GNUNET_HELLO_size (hello));
356     }
357   else
358     {
359       mrg = GNUNET_HELLO_merge (host->hello, hello);
360       delta = GNUNET_HELLO_equals (mrg,
361                                    host->hello,
362                                    GNUNET_TIME_absolute_get ());
363       if (delta.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
364         {
365           GNUNET_free (mrg);
366           return;
367         }
368       GNUNET_free (host->hello);
369       host->hello = mrg;
370     }
371 #if WRITE_TO_DISK
372   fn = get_host_filename (peer);
373   if (GNUNET_OK == GNUNET_DISK_directory_create_for_file (fn))
374     {
375       if (GNUNET_SYSERR ==
376           GNUNET_DISK_fn_write (fn, 
377                                 host->hello, 
378                                 GNUNET_HELLO_size (host->hello),
379                                 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
380                                 | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ))
381         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
382                                   "write",
383                                   fn);
384         
385     }
386   GNUNET_free (fn);
387 #endif
388   notify_all (host);
389 }
390
391
392
393 /**
394  * Do transmit info about peer to given host.
395  *
396  * @param cls NULL to hit all hosts, otherwise specifies a particular target
397  * @param key hostID
398  * @param value information to transmit
399  * @return GNUNET_YES (continue to iterate)
400  */
401 static int
402 add_to_tc (void *cls,
403            const GNUNET_HashCode *key,
404            void *value)
405 {
406   struct GNUNET_SERVER_TransmitContext *tc = cls;
407   struct HostEntry *pos = value;
408   struct InfoMessage *im;
409   uint16_t hs;
410   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
411
412   hs = 0;
413   im = (struct InfoMessage *) buf;
414   if (pos->hello != NULL)
415     {
416       hs = GNUNET_HELLO_size (pos->hello);
417       GNUNET_assert (hs <
418                      GNUNET_SERVER_MAX_MESSAGE_SIZE -
419                      sizeof (struct InfoMessage));
420       memcpy (&im[1], pos->hello, hs);
421     }
422   im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
423   im->header.size = htons (sizeof (struct InfoMessage) + hs);
424   im->reserved = htonl (0);
425   im->peer = pos->identity;
426   GNUNET_SERVER_transmit_context_append_message (tc,
427                                                  &im->header);
428   return GNUNET_YES;
429 }
430
431 #if WRITE_TO_DISK
432 /**
433  * @brief delete expired HELLO entries in data/hosts/
434  */
435 static int
436 discard_hosts_helper (void *cls, const char *fn)
437 {
438   struct GNUNET_TIME_Absolute *now = cls;
439   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
440   const struct GNUNET_HELLO_Message *hello;
441   struct GNUNET_HELLO_Message *new_hello;
442   int size;
443
444   size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
445   if (size < sizeof (struct GNUNET_MessageHeader))
446     {
447       if (0 != UNLINK (fn))
448         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
449                                   GNUNET_ERROR_TYPE_BULK, "unlink", fn);
450       return GNUNET_OK;
451     }
452   hello = (const struct GNUNET_HELLO_Message *) buffer;
453   new_hello = GNUNET_HELLO_iterate_addresses (hello,
454                                               GNUNET_YES,
455                                               &discard_expired, now);
456   if (new_hello != NULL)
457     {
458       GNUNET_DISK_fn_write (fn, 
459                             new_hello,
460                             GNUNET_HELLO_size (new_hello),
461                             GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
462                             | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ);
463       GNUNET_free (new_hello);
464     }
465   else
466     {
467       if (0 != UNLINK (fn))
468         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
469                                   GNUNET_ERROR_TYPE_BULK, "unlink", fn);      
470     }
471   return GNUNET_OK;
472 }
473
474
475 /**
476  * Call this method periodically to scan data/hosts for new hosts.
477  */
478 static void
479 cron_clean_data_hosts (void *cls,
480                        const struct GNUNET_SCHEDULER_TaskContext *tc)
481 {
482   struct GNUNET_TIME_Absolute now;
483
484   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
485     return;
486   now = GNUNET_TIME_absolute_get ();
487   GNUNET_DISK_directory_scan (networkIdDirectory,
488                               &discard_hosts_helper, &now);
489   GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
490                                 &cron_clean_data_hosts, NULL);
491 }
492 #endif
493
494
495 /**
496  * Handle HELLO-message.
497  *
498  * @param cls closure
499  * @param client identification of the client
500  * @param message the actual message
501  */
502 static void
503 handle_hello (void *cls,
504               struct GNUNET_SERVER_Client *client,
505               const struct GNUNET_MessageHeader *message)
506 {
507   const struct GNUNET_HELLO_Message *hello;
508   struct GNUNET_PeerIdentity pid;
509
510   hello = (const struct GNUNET_HELLO_Message *) message;
511   if (GNUNET_OK !=  GNUNET_HELLO_get_id (hello, &pid))
512     {
513       GNUNET_break (0);
514       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
515       return;
516     }
517 #if DEBUG_PEERINFO
518   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
519               "`%s' message received for peer `%4s'\n",
520               "HELLO",
521               GNUNET_i2s (&pid));
522 #endif
523   bind_address (&pid, hello);
524   GNUNET_SERVER_receive_done (client, GNUNET_OK);
525 }
526
527
528 /**
529  * Handle GET-message.
530  *
531  * @param cls closure
532  * @param client identification of the client
533  * @param message the actual message
534  */
535 static void
536 handle_get (void *cls,
537             struct GNUNET_SERVER_Client *client,
538             const struct GNUNET_MessageHeader *message)
539 {
540   const struct ListPeerMessage *lpm;
541   struct GNUNET_SERVER_TransmitContext *tc;
542
543   lpm = (const struct ListPeerMessage *) message;
544   GNUNET_break (0 == ntohl (lpm->reserved));
545 #if DEBUG_PEERINFO
546   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
547               "`%s' message received for peer `%4s'\n",
548               "GET",
549               GNUNET_i2s (&lpm->peer));
550 #endif
551   tc = GNUNET_SERVER_transmit_context_create (client);
552   GNUNET_CONTAINER_multihashmap_get_multiple (hostmap,
553                                               &lpm->peer.hashPubKey,
554                                               &add_to_tc,
555                                               tc);
556   GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
557                                               GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
558   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
559 }
560
561
562 /**
563  * Handle GET-ALL-message.
564  *
565  * @param cls closure
566  * @param client identification of the client
567  * @param message the actual message
568  */
569 static void
570 handle_get_all (void *cls,
571                 struct GNUNET_SERVER_Client *client,
572                 const struct GNUNET_MessageHeader *message)
573 {
574   struct GNUNET_SERVER_TransmitContext *tc;
575
576 #if DEBUG_PEERINFO
577   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
578               "`%s' message received\n",
579               "GET_ALL");
580 #endif
581   tc = GNUNET_SERVER_transmit_context_create (client);
582   GNUNET_CONTAINER_multihashmap_iterate (hostmap,
583                                          &add_to_tc,
584                                          tc);
585   GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
586                                               GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
587   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
588 }
589
590
591 static int
592 do_notify_entry (void *cls,
593                  const GNUNET_HashCode *key,
594                  void *value)
595 {
596   struct GNUNET_SERVER_Client *client = cls;
597   struct HostEntry *he = value;
598   struct InfoMessage *msg;
599
600   msg = make_info_message (he);
601   GNUNET_SERVER_notification_context_unicast (notify_list,
602                                               client,
603                                               &msg->header,
604                                               GNUNET_NO);
605   GNUNET_free (msg);
606   return GNUNET_YES;
607 }
608
609
610 /**
611  * Handle NOTIFY-message.
612  *
613  * @param cls closure
614  * @param client identification of the client
615  * @param message the actual message
616  */
617 static void
618 handle_notify (void *cls,
619                struct GNUNET_SERVER_Client *client,
620                const struct GNUNET_MessageHeader *message)
621 {
622 #if DEBUG_PEERINFO
623   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624               "`%s' message received\n",
625               "NOTIFY");
626 #endif
627   GNUNET_SERVER_notification_context_add (notify_list,
628                                           client);
629   GNUNET_CONTAINER_multihashmap_iterate (hostmap,
630                                          &do_notify_entry,
631                                          client);
632   GNUNET_SERVER_receive_done (client, GNUNET_OK);
633 }
634
635
636 static int
637 free_host_entry (void *cls,
638                  const GNUNET_HashCode *key,
639                  void *value)
640 {
641   struct HostEntry *he = value;
642
643   GNUNET_free_non_null (he->hello);
644   GNUNET_free (he);
645   return GNUNET_YES;
646 }
647
648 /**
649  * Clean up our state.  Called during shutdown.
650  *
651  * @param cls unused
652  * @param tc scheduler task context, unused
653  */
654 static void
655 shutdown_task (void *cls,
656                const struct GNUNET_SCHEDULER_TaskContext *tc)
657 {
658   GNUNET_SERVER_notification_context_destroy (notify_list);
659   notify_list = NULL;
660   GNUNET_CONTAINER_multihashmap_iterate (hostmap,
661                                          &free_host_entry,
662                                          NULL);
663   GNUNET_CONTAINER_multihashmap_destroy (hostmap);
664   if (stats != NULL)
665     {
666       GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
667       stats = NULL;
668     }
669 }
670
671
672 /**
673  * Process statistics requests.
674  *
675  * @param cls closure
676  * @param server the initialized server
677  * @param cfg configuration to use
678  */
679 static void
680 run (void *cls,
681      struct GNUNET_SERVER_Handle *server,
682      const struct GNUNET_CONFIGURATION_Handle *cfg)
683 {
684   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
685     {&handle_hello, NULL, GNUNET_MESSAGE_TYPE_HELLO, 0},
686     {&handle_get, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET,
687      sizeof (struct ListPeerMessage)},
688     {&handle_get_all, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
689      sizeof (struct GNUNET_MessageHeader)},
690     {&handle_notify, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
691      sizeof (struct GNUNET_MessageHeader)},
692     {NULL, NULL, 0, 0}
693   };
694
695   hostmap = GNUNET_CONTAINER_multihashmap_create (1024);
696   stats = GNUNET_STATISTICS_create ("peerinfo", cfg);
697   notify_list = GNUNET_SERVER_notification_context_create (server, 0);
698 #if WRITE_TO_DISK
699   GNUNET_assert (GNUNET_OK ==
700                  GNUNET_CONFIGURATION_get_value_filename (cfg,
701                                                           "peerinfo",
702                                                           "HOSTS",
703                                                           &networkIdDirectory));
704   GNUNET_DISK_directory_create (networkIdDirectory);
705   GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
706                                       &cron_scan_directory_data_hosts, NULL);
707   GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
708                                       &cron_clean_data_hosts, NULL);
709 #endif
710   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
711                                 &shutdown_task, NULL);
712   GNUNET_SERVER_add_handlers (server, handlers);
713 }
714
715
716 /**
717  * The main function for the statistics service.
718  *
719  * @param argc number of arguments from the command line
720  * @param argv command line arguments
721  * @return 0 ok, 1 on error
722  */
723 int
724 main (int argc, char *const *argv)
725 {
726   int ret;
727
728   ret = (GNUNET_OK ==
729          GNUNET_SERVICE_run (argc,
730                              argv,
731                               "peerinfo",
732                              GNUNET_SERVICE_OPTION_NONE,
733                              &run, NULL)) ? 0 : 1;
734   GNUNET_free_non_null (networkIdDirectory);
735   return ret;
736 }
737
738
739 /* end of gnunet-service-peerinfo.c */