doxygen additions
[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 2, 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/ and data/credit/).
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_disk_lib.h"
37 #include "gnunet_hello_lib.h"
38 #include "gnunet_protocols.h"
39 #include "gnunet_service_lib.h"
40 #include "gnunet_statistics_service.h"
41 #include "peerinfo.h"
42
43 /**
44  * How often do we scan the HOST_DIR for new entries?
45  */
46 #define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
47
48 /**
49  * How often do we flush trust values to disk?
50  */
51 #define TRUST_FLUSH_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
52
53 /**
54  * How often do we discard old entries in data/hosts/?
55  */
56 #define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
57
58 /**
59  * In-memory cache of known hosts.
60  */
61 struct HostEntry
62 {
63
64   /**
65    * This is a linked list.
66    */
67   struct HostEntry *next;
68
69   /**
70    * Identity of the peer.
71    */
72   struct GNUNET_PeerIdentity identity;
73
74   /**
75    * Hello for the peer (can be NULL)
76    */
77   struct GNUNET_HELLO_Message *hello;
78
79   /**
80    * Trust rating for this peer
81    */
82   uint32_t trust;
83
84   /**
85    * Trust rating for this peer on disk.
86    */
87   uint32_t disk_trust;
88
89 };
90
91
92 /**
93  * The in-memory list of known hosts.
94  */
95 static struct HostEntry *hosts;
96
97 /**
98  * Clients to immediately notify about all changes.
99  */
100 static struct GNUNET_SERVER_NotificationContext *notify_list;
101
102 /**
103  * Directory where the hellos are stored in (data/hosts)
104  */
105 static char *networkIdDirectory;
106
107 /**
108  * Where do we store trust information?
109  */
110 static char *trustDirectory;
111
112 /**
113  * Handle for reporting statistics.
114  */
115 static struct GNUNET_STATISTICS_Handle *stats;
116
117
118 /**
119  * Notify all clients in the notify list about the
120  * given host entry changing.
121  */
122 static struct InfoMessage *
123 make_info_message (const struct HostEntry *he)
124 {
125   struct InfoMessage *im;
126   size_t hs;
127
128   hs = (he->hello == NULL) ? 0 : GNUNET_HELLO_size (he->hello);
129   im = GNUNET_malloc (sizeof (struct InfoMessage) + hs);
130   im->header.size = htons (hs + sizeof (struct InfoMessage));
131   im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
132   im->trust = htonl (he->trust);
133   im->peer = he->identity;
134   memcpy (&im[1], he->hello, hs);
135   return im;
136 }
137
138
139 /**
140  * Address iterator that causes expired entries to be discarded.
141  *
142  * @param cls pointer to the current time
143  * @param tname name of the transport
144  * @param expiration expiration time for the address
145  * @param addr the address
146  * @param addrlen length of addr in bytes
147  * @return GNUNET_NO if expiration smaller than the current time
148  */
149 static int
150 discard_expired (void *cls,
151                  const char *tname,
152                  struct GNUNET_TIME_Absolute expiration,
153                  const void *addr, size_t addrlen)
154 {
155   const struct GNUNET_TIME_Absolute *now = cls;
156   if (now->value > expiration.value)
157     {
158       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
159                   _("Removing expired address of transport `%s'\n"),
160                   tname);
161       return GNUNET_NO;
162     }
163   return GNUNET_OK;
164 }
165
166
167 /**
168  * Get the filename under which we would store the GNUNET_HELLO_Message
169  * for the given host and protocol.
170  * @return filename of the form DIRECTORY/HOSTID
171  */
172 static char *
173 get_host_filename (const struct GNUNET_PeerIdentity *id)
174 {
175   struct GNUNET_CRYPTO_HashAsciiEncoded fil;
176   char *fn;
177
178   GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
179   GNUNET_asprintf (&fn,
180                    "%s%s%s", networkIdDirectory, DIR_SEPARATOR_STR, &fil);
181   return fn;
182 }
183
184
185 /**
186  * Get the filename under which we would store the GNUNET_HELLO_Message
187  * for the given host and protocol.
188  * @return filename of the form DIRECTORY/HOSTID
189  */
190 static char *
191 get_trust_filename (const struct GNUNET_PeerIdentity *id)
192 {
193   struct GNUNET_CRYPTO_HashAsciiEncoded fil;
194   char *fn;
195
196   GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
197   GNUNET_asprintf (&fn, "%s%s%s", trustDirectory, DIR_SEPARATOR_STR, &fil);
198   return fn;
199 }
200
201
202 /**
203  * Find the host entry for the given peer.  Call
204  * only when synchronized!
205  * @return NULL if not found
206  */
207 static struct HostEntry *
208 lookup_host_entry (const struct GNUNET_PeerIdentity *id)
209 {
210   struct HostEntry *pos;
211
212   pos = hosts;
213   while ((pos != NULL) &&
214          (0 !=
215           memcmp (id, &pos->identity, sizeof (struct GNUNET_PeerIdentity))))
216     pos = pos->next;
217   return pos;
218 }
219
220
221 /**
222  * Broadcast information about the given entry to all 
223  * clients that care.
224  *
225  * @param entry entry to broadcast about
226  */
227 static void
228 notify_all (struct HostEntry *entry)
229 {
230   struct InfoMessage *msg;
231
232   msg = make_info_message (entry);
233   GNUNET_SERVER_notification_context_broadcast (notify_list,
234                                                 &msg->header,
235                                                 GNUNET_NO);
236   GNUNET_free (msg);
237 }
238
239
240 /**
241  * Add a host to the list.
242  *
243  * @param identity the identity of the host
244  */
245 static void
246 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
247 {
248   struct HostEntry *entry;
249   char *fn;
250   uint32_t trust;
251   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
252   const struct GNUNET_HELLO_Message *hello;
253   struct GNUNET_HELLO_Message *hello_clean;
254   int size;
255   struct GNUNET_TIME_Absolute now;
256
257   entry = lookup_host_entry (identity);
258   if (entry != NULL)
259     return;
260   GNUNET_STATISTICS_update (stats,
261                             gettext_noop ("# peers known"),
262                             1,
263                             GNUNET_NO);
264   entry = GNUNET_malloc (sizeof (struct HostEntry));
265   entry->identity = *identity;
266   fn = get_trust_filename (identity);
267   if ((GNUNET_DISK_file_test (fn) == GNUNET_YES) &&
268       (sizeof (trust) == GNUNET_DISK_fn_read (fn, &trust, sizeof (trust))))
269     entry->disk_trust = entry->trust = ntohl (trust);
270   GNUNET_free (fn);
271
272   fn = get_host_filename (identity);
273   if (GNUNET_DISK_file_test (fn) == GNUNET_YES)
274     {
275       size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
276       hello = (const struct GNUNET_HELLO_Message *) buffer;
277       if ( (size < sizeof (struct GNUNET_MessageHeader)) ||
278            (size != ntohs((((const struct GNUNET_MessageHeader*) hello)->size))) ||
279            (size != GNUNET_HELLO_size (hello)) )
280         {
281           GNUNET_break (0);
282           if (0 != UNLINK (fn))
283             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
284                                       "unlink",
285                                       fn);
286         }
287       else
288         {
289           now = GNUNET_TIME_absolute_get ();
290           hello_clean = GNUNET_HELLO_iterate_addresses (hello,
291                                                         GNUNET_YES,
292                                                         &discard_expired, &now);
293           entry->hello = hello_clean;
294         }
295     }
296   GNUNET_free (fn);
297   entry->next = hosts;
298   hosts = entry;
299   notify_all (entry);
300 }
301
302
303 /**
304  * Increase the host credit by a value.
305  *
306  * @param hostId is the identity of the host
307  * @param value is the int value by which the
308  *  host credit is to be increased or decreased
309  * @returns the actual change in trust (positive or negative)
310  */
311 static int
312 change_host_trust (const struct GNUNET_PeerIdentity *hostId, int value)
313 {
314   struct HostEntry *host;
315   unsigned int old_trust;
316
317   if (value == 0)
318     return 0;
319   host = lookup_host_entry (hostId);
320   if (host == NULL)
321     {
322       add_host_to_known_hosts (hostId);
323       host = lookup_host_entry (hostId);
324     }
325   GNUNET_assert (host != NULL);
326   old_trust = host->trust;
327   if (value > 0)
328     {
329       if (host->trust + value < host->trust)
330         {
331           value = ((uint32_t) - 1) - host->trust;
332           host->trust = (uint32_t) - 1; /* maximized */
333         }
334       else
335         host->trust += value;
336     }
337   else
338     {
339       if (host->trust < -value)
340         {
341           value = -host->trust;
342           host->trust = 0;
343         }
344       else
345         host->trust += value;
346     }
347   if (host->trust != old_trust)
348     notify_all (host);    
349   return value;
350 }
351
352
353 /**
354  * Remove a file that should not be there.  LOG
355  * success or failure.
356  */
357 static void
358 remove_garbage (const char *fullname)
359 {
360   if (0 == UNLINK (fullname))
361     GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
362                 _
363                 ("File `%s' in directory `%s' does not match naming convention. "
364                  "Removed.\n"), fullname, networkIdDirectory);
365   else
366     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
367                               GNUNET_ERROR_TYPE_BULK, "unlink", fullname);
368 }
369
370
371 static int
372 hosts_directory_scan_callback (void *cls, const char *fullname)
373 {
374   unsigned int *matched = cls;
375   struct GNUNET_PeerIdentity identity;
376   const char *filename;
377
378   if (GNUNET_DISK_file_test (fullname) != GNUNET_YES)
379     return GNUNET_OK;           /* ignore non-files */
380   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
381     {
382       remove_garbage (fullname);
383       return GNUNET_OK;
384     }
385   filename =
386     &fullname[strlen (fullname) -
387               sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) + 1];
388   if (filename[-1] != DIR_SEPARATOR)
389     {
390       remove_garbage (fullname);
391       return GNUNET_OK;
392     }
393   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (filename,
394                                                    &identity.hashPubKey))
395     {
396       remove_garbage (fullname);
397       return GNUNET_OK;
398     }
399   (*matched)++;
400   add_host_to_known_hosts (&identity);
401   return GNUNET_OK;
402 }
403
404
405 /**
406  * Call this method periodically to scan data/hosts for new hosts.
407  */
408 static void
409 cron_scan_directory_data_hosts (void *cls,
410                                 const struct GNUNET_SCHEDULER_TaskContext *tc)
411 {
412   static unsigned int retries;
413   unsigned int count;
414
415   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
416     return;
417   count = 0;
418   GNUNET_DISK_directory_create (networkIdDirectory);
419   GNUNET_DISK_directory_scan (networkIdDirectory,
420                               &hosts_directory_scan_callback, &count);
421   if ((0 == count) && (0 == (++retries & 31)))
422     GNUNET_log (GNUNET_ERROR_TYPE_WARNING |
423                 GNUNET_ERROR_TYPE_BULK,
424                 _("Still no peers found in `%s'!\n"), networkIdDirectory);
425   GNUNET_SCHEDULER_add_delayed (tc->sched,
426                                 DATA_HOST_FREQ,
427                                 &cron_scan_directory_data_hosts, NULL);
428 }
429
430
431 /**
432  * Bind a host address (hello) to a hostId.
433  *
434  * @param peer the peer for which this is a hello
435  * @param hello the verified (!) hello message
436  */
437 static void
438 bind_address (const struct GNUNET_PeerIdentity *peer,
439               const struct GNUNET_HELLO_Message *hello)
440 {
441   char *fn;
442   struct HostEntry *host;
443   struct GNUNET_HELLO_Message *mrg;
444
445   add_host_to_known_hosts (peer);
446   host = lookup_host_entry (peer);
447   GNUNET_assert (host != NULL);
448   if (host->hello == NULL)
449     {
450       host->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
451       memcpy (host->hello, hello, GNUNET_HELLO_size (hello));
452     }
453   else
454     {
455       mrg = GNUNET_HELLO_merge (host->hello, hello);
456       /* FIXME: check if old and merged hello are equal,
457          and if so, bail out early... */
458       GNUNET_free (host->hello);
459       host->hello = mrg;
460     }
461   fn = get_host_filename (peer);
462   GNUNET_DISK_directory_create_for_file (fn);
463   GNUNET_DISK_fn_write (fn, 
464                         host->hello, 
465                         GNUNET_HELLO_size (host->hello),
466                         GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
467                         | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ);
468   GNUNET_free (fn);
469   notify_all (host);
470 }
471
472
473 /**
474  * Do transmit info either for only the host matching the given
475  * argument or for all known hosts and change their trust values by
476  * the given delta.
477  *
478  * @param only NULL to hit all hosts, otherwise specifies a particular target
479  * @param trust_change how much should the trust be changed
480  * @param client who is making the request (and will thus receive our confirmation)
481  */
482 static void
483 send_to_each_host (const struct GNUNET_PeerIdentity *only,
484                    int trust_change, struct GNUNET_SERVER_Client *client)
485 {
486   struct HostEntry *pos;
487   struct InfoMessage *im;
488   uint16_t hs;
489   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
490   struct GNUNET_SERVER_TransmitContext *tc;
491
492   tc = GNUNET_SERVER_transmit_context_create (client);
493   pos = hosts;
494   while (pos != NULL)
495     {
496       if ((only == NULL) ||
497           (0 ==
498            memcmp (only, &pos->identity,
499                    sizeof (struct GNUNET_PeerIdentity))))
500         {
501           change_host_trust (&pos->identity, trust_change);
502           hs = 0;
503           im = (struct InfoMessage *) buf;
504           if (pos->hello != NULL)
505             {
506               hs = GNUNET_HELLO_size (pos->hello);
507               GNUNET_assert (hs <
508                              GNUNET_SERVER_MAX_MESSAGE_SIZE -
509                              sizeof (struct InfoMessage));
510               memcpy (&im[1], pos->hello, hs);
511             }
512           im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
513           im->header.size = htons (sizeof (struct InfoMessage) + hs);
514           im->trust = htonl (pos->trust);
515           im->peer = pos->identity;
516           GNUNET_SERVER_transmit_context_append_message (tc,
517                                                          &im->header);
518         }
519       pos = pos->next;
520     }
521   GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
522                                               GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
523   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
524 }
525
526
527 /**
528  * Write host-trust information to a file - flush the buffer entry!
529  * Assumes synchronized access.
530  */
531 static void
532 flush_trust (struct HostEntry *host)
533 {
534   char *fn;
535   uint32_t trust;
536
537   if (host->trust == host->disk_trust)
538     return;                     /* unchanged */
539   fn = get_trust_filename (&host->identity);
540   if (host->trust == 0)
541     {
542       if ((0 != UNLINK (fn)) && (errno != ENOENT))
543         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
544                                   GNUNET_ERROR_TYPE_BULK, "unlink", fn);
545     }
546   else
547     {
548       trust = htonl (host->trust);
549       if (sizeof(uint32_t) == GNUNET_DISK_fn_write (fn, &trust, 
550                                                     sizeof(uint32_t),
551                                                     GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
552                                                     | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ))
553         host->disk_trust = host->trust;
554     }
555   GNUNET_free (fn);
556 }
557
558 /**
559  * Call this method periodically to scan data/hosts for new hosts.
560  */
561 static void
562 cron_flush_trust (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
563 {
564   struct HostEntry *pos;
565
566   pos = hosts;
567   while (pos != NULL)
568     {
569       flush_trust (pos);
570       pos = pos->next;
571     }
572   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
573     return;
574   GNUNET_SCHEDULER_add_delayed (tc->sched,
575                                 TRUST_FLUSH_FREQ, &cron_flush_trust, NULL);
576 }
577
578
579 /**
580  * @brief delete expired HELLO entries in data/hosts/
581  */
582 static int
583 discard_hosts_helper (void *cls, const char *fn)
584 {
585   struct GNUNET_TIME_Absolute *now = cls;
586   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
587   const struct GNUNET_HELLO_Message *hello;
588   struct GNUNET_HELLO_Message *new_hello;
589   int size;
590
591   size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
592   if ((size < sizeof (struct GNUNET_MessageHeader)) && (0 != UNLINK (fn)))
593     {
594       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
595                                 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
596       return GNUNET_OK;
597     }
598   hello = (const struct GNUNET_HELLO_Message *) buffer;
599   new_hello = GNUNET_HELLO_iterate_addresses (hello,
600                                               GNUNET_YES,
601                                               &discard_expired, now);
602   if (new_hello != NULL)
603     {
604       GNUNET_DISK_fn_write (fn, 
605                             new_hello,
606                             GNUNET_HELLO_size (new_hello),
607                             GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
608                             | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ);
609       GNUNET_free (new_hello);
610     }
611   else
612     {
613       if (0 != UNLINK (fn))
614         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
615                                   GNUNET_ERROR_TYPE_BULK, "unlink", fn);      
616     }
617   return GNUNET_OK;
618 }
619
620
621 /**
622  * Call this method periodically to scan data/hosts for new hosts.
623  */
624 static void
625 cron_clean_data_hosts (void *cls,
626                        const struct GNUNET_SCHEDULER_TaskContext *tc)
627 {
628   struct GNUNET_TIME_Absolute now;
629
630   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
631     return;
632   now = GNUNET_TIME_absolute_get ();
633   GNUNET_DISK_directory_scan (networkIdDirectory,
634                               &discard_hosts_helper, &now);
635
636   GNUNET_SCHEDULER_add_delayed (tc->sched,
637                                 DATA_HOST_CLEAN_FREQ,
638                                 &cron_clean_data_hosts, NULL);
639 }
640
641
642 /**
643  * Handle ADD-message.
644  *
645  * @param cls closure
646  * @param client identification of the client
647  * @param message the actual message
648  */
649 static void
650 handle_add (void *cls,
651             struct GNUNET_SERVER_Client *client,
652             const struct GNUNET_MessageHeader *message)
653 {
654   const struct PeerAddMessage *pam;
655   const struct GNUNET_MessageHeader *hello;
656   uint16_t size;
657
658   size = ntohs (message->size);
659   if (size <
660       sizeof (struct PeerAddMessage) + sizeof (struct GNUNET_MessageHeader))
661     {
662       GNUNET_break (0);
663       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
664       return;
665     }
666   pam = (const struct PeerAddMessage *) message;
667   hello = (const struct GNUNET_MessageHeader *) &pam[1];
668   if (size != sizeof (struct PeerAddMessage) + ntohs (hello->size))
669     {
670       GNUNET_break (0);
671       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
672       return;
673     }
674   bind_address (&pam->peer, (const struct GNUNET_HELLO_Message *) hello);
675   GNUNET_SERVER_receive_done (client, GNUNET_OK);
676 }
677
678
679 /**
680  * Handle GET-message.
681  *
682  * @param cls closure
683  * @param client identification of the client
684  * @param message the actual message
685  */
686 static void
687 handle_get (void *cls,
688             struct GNUNET_SERVER_Client *client,
689             const struct GNUNET_MessageHeader *message)
690 {
691   const struct ListPeerMessage *lpm;
692
693   lpm = (const struct ListPeerMessage *) message;
694   send_to_each_host (&lpm->peer, ntohl (lpm->trust_change), client);
695 }
696
697
698 /**
699  * Handle GET-ALL-message.
700  *
701  * @param cls closure
702  * @param client identification of the client
703  * @param message the actual message
704  */
705 static void
706 handle_get_all (void *cls,
707                 struct GNUNET_SERVER_Client *client,
708                 const struct GNUNET_MessageHeader *message)
709 {
710   const struct ListAllPeersMessage *lpm;
711
712   lpm = (const struct ListAllPeersMessage *) message;
713   send_to_each_host (NULL, ntohl (lpm->trust_change), client);
714 }
715
716
717 /**
718  * Handle NOTIFY-message.
719  *
720  * @param cls closure
721  * @param client identification of the client
722  * @param message the actual message
723  */
724 static void
725 handle_notify (void *cls,
726             struct GNUNET_SERVER_Client *client,
727             const struct GNUNET_MessageHeader *message)
728 {
729   struct InfoMessage *msg;
730   struct HostEntry *pos;
731
732   GNUNET_SERVER_notification_context_add (notify_list,
733                                           client);
734   pos = hosts;
735   while (NULL != pos)
736     {
737       msg = make_info_message (pos);
738       GNUNET_SERVER_notification_context_unicast (notify_list,
739                                                   client,
740                                                   &msg->header,
741                                                   GNUNET_NO);
742       GNUNET_free (msg);
743       pos = pos->next;
744     }
745 }
746
747
748 /**
749  * List of handlers for the messages understood by this
750  * service.
751  */
752 static struct GNUNET_SERVER_MessageHandler handlers[] = {
753   {&handle_add, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_ADD, 0},
754   {&handle_get, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET,
755    sizeof (struct ListPeerMessage)},
756   {&handle_get_all, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
757    sizeof (struct ListAllPeersMessage)},
758   {&handle_notify, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
759    sizeof (struct GNUNET_MessageHeader)},
760   {NULL, NULL, 0, 0}
761 };
762
763
764 /**
765  * Clean up our state.  Called during shutdown.
766  *
767  * @param cls unused
768  * @param tc scheduler task context, unused
769  */
770 static void
771 shutdown_task (void *cls,
772                const struct GNUNET_SCHEDULER_TaskContext *tc)
773 {
774   GNUNET_SERVER_notification_context_destroy (notify_list);
775   notify_list = NULL;
776   if (stats != NULL)
777     {
778       GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
779       stats = NULL;
780     }
781 }
782
783
784 /**
785  * Process statistics requests.
786  *
787  * @param cls closure
788  * @param sched scheduler to use
789  * @param server the initialized server
790  * @param cfg configuration to use
791  */
792 static void
793 run (void *cls,
794      struct GNUNET_SCHEDULER_Handle *sched,
795      struct GNUNET_SERVER_Handle *server,
796      const struct GNUNET_CONFIGURATION_Handle *cfg)
797 {
798   stats = GNUNET_STATISTICS_create (sched, "peerinfo", cfg);
799   notify_list = GNUNET_SERVER_notification_context_create (server, 0);
800   GNUNET_assert (GNUNET_OK ==
801                  GNUNET_CONFIGURATION_get_value_filename (cfg,
802                                                           "peerinfo",
803                                                           "HOSTS",
804                                                           &networkIdDirectory));
805   GNUNET_assert (GNUNET_OK ==
806                  GNUNET_CONFIGURATION_get_value_filename (cfg,
807                                                           "peerinfo",
808                                                           "TRUST",
809                                                           &trustDirectory));
810   GNUNET_DISK_directory_create (networkIdDirectory);
811   GNUNET_DISK_directory_create (trustDirectory);
812   GNUNET_SCHEDULER_add_with_priority (sched,
813                                       GNUNET_SCHEDULER_PRIORITY_IDLE,
814                                       &cron_scan_directory_data_hosts, NULL);
815   GNUNET_SCHEDULER_add_with_priority (sched,
816                                       GNUNET_SCHEDULER_PRIORITY_HIGH,
817                                       &cron_flush_trust, NULL);
818   GNUNET_SCHEDULER_add_with_priority (sched,
819                                       GNUNET_SCHEDULER_PRIORITY_IDLE,
820                                       &cron_clean_data_hosts, NULL);
821   GNUNET_SCHEDULER_add_delayed (sched,
822                                 GNUNET_TIME_UNIT_FOREVER_REL,
823                                 &shutdown_task, NULL);
824   GNUNET_SERVER_add_handlers (server, handlers);
825 }
826
827
828 /**
829  * The main function for the statistics service.
830  *
831  * @param argc number of arguments from the command line
832  * @param argv command line arguments
833  * @return 0 ok, 1 on error
834  */
835 int
836 main (int argc, char *const *argv)
837 {
838   int ret;
839
840   ret = (GNUNET_OK ==
841          GNUNET_SERVICE_run (argc,
842                              argv,
843                               "peerinfo",
844                              GNUNET_SERVICE_OPTION_NONE,
845                              &run, NULL)) ? 0 : 1;
846   GNUNET_free_non_null (networkIdDirectory);
847   GNUNET_free_non_null (trustDirectory);
848   return ret;
849 }
850
851
852 /* end of gnunet-service-peerinfo.c */