work on HELLO
[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   struct GNUNET_TIME_Absolute delta;
445
446   add_host_to_known_hosts (peer);
447   host = lookup_host_entry (peer);
448   GNUNET_assert (host != NULL);
449   if (host->hello == NULL)
450     {
451       host->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
452       memcpy (host->hello, hello, GNUNET_HELLO_size (hello));
453     }
454   else
455     {
456       mrg = GNUNET_HELLO_merge (host->hello, hello);
457       delta = GNUNET_HELLO_equals (mrg,
458                                    host->hello,
459                                    GNUNET_TIME_absolute_get ());
460       if (delta.value == GNUNET_TIME_UNIT_FOREVER_ABS.value)
461         {
462           GNUNET_free (mrg);
463           return;
464         }
465       GNUNET_free (host->hello);
466       host->hello = mrg;
467     }
468   fn = get_host_filename (peer);
469   GNUNET_DISK_directory_create_for_file (fn);
470   GNUNET_DISK_fn_write (fn, 
471                         host->hello, 
472                         GNUNET_HELLO_size (host->hello),
473                         GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
474                         | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ);
475   GNUNET_free (fn);
476   notify_all (host);
477 }
478
479
480 /**
481  * Do transmit info either for only the host matching the given
482  * argument or for all known hosts and change their trust values by
483  * the given delta.
484  *
485  * @param only NULL to hit all hosts, otherwise specifies a particular target
486  * @param trust_change how much should the trust be changed
487  * @param client who is making the request (and will thus receive our confirmation)
488  */
489 static void
490 send_to_each_host (const struct GNUNET_PeerIdentity *only,
491                    int trust_change, struct GNUNET_SERVER_Client *client)
492 {
493   struct HostEntry *pos;
494   struct InfoMessage *im;
495   uint16_t hs;
496   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
497   struct GNUNET_SERVER_TransmitContext *tc;
498
499   tc = GNUNET_SERVER_transmit_context_create (client);
500   pos = hosts;
501   while (pos != NULL)
502     {
503       if ((only == NULL) ||
504           (0 ==
505            memcmp (only, &pos->identity,
506                    sizeof (struct GNUNET_PeerIdentity))))
507         {
508           change_host_trust (&pos->identity, trust_change);
509           hs = 0;
510           im = (struct InfoMessage *) buf;
511           if (pos->hello != NULL)
512             {
513               hs = GNUNET_HELLO_size (pos->hello);
514               GNUNET_assert (hs <
515                              GNUNET_SERVER_MAX_MESSAGE_SIZE -
516                              sizeof (struct InfoMessage));
517               memcpy (&im[1], pos->hello, hs);
518             }
519           im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
520           im->header.size = htons (sizeof (struct InfoMessage) + hs);
521           im->trust = htonl (pos->trust);
522           im->peer = pos->identity;
523           GNUNET_SERVER_transmit_context_append_message (tc,
524                                                          &im->header);
525         }
526       pos = pos->next;
527     }
528   GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
529                                               GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
530   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
531 }
532
533
534 /**
535  * Write host-trust information to a file - flush the buffer entry!
536  * Assumes synchronized access.
537  */
538 static void
539 flush_trust (struct HostEntry *host)
540 {
541   char *fn;
542   uint32_t trust;
543
544   if (host->trust == host->disk_trust)
545     return;                     /* unchanged */
546   fn = get_trust_filename (&host->identity);
547   if (host->trust == 0)
548     {
549       if ((0 != UNLINK (fn)) && (errno != ENOENT))
550         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
551                                   GNUNET_ERROR_TYPE_BULK, "unlink", fn);
552     }
553   else
554     {
555       trust = htonl (host->trust);
556       if (sizeof(uint32_t) == GNUNET_DISK_fn_write (fn, &trust, 
557                                                     sizeof(uint32_t),
558                                                     GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
559                                                     | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ))
560         host->disk_trust = host->trust;
561     }
562   GNUNET_free (fn);
563 }
564
565 /**
566  * Call this method periodically to scan data/hosts for new hosts.
567  */
568 static void
569 cron_flush_trust (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
570 {
571   struct HostEntry *pos;
572
573   pos = hosts;
574   while (pos != NULL)
575     {
576       flush_trust (pos);
577       pos = pos->next;
578     }
579   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
580     return;
581   GNUNET_SCHEDULER_add_delayed (tc->sched,
582                                 TRUST_FLUSH_FREQ, &cron_flush_trust, NULL);
583 }
584
585
586 /**
587  * @brief delete expired HELLO entries in data/hosts/
588  */
589 static int
590 discard_hosts_helper (void *cls, const char *fn)
591 {
592   struct GNUNET_TIME_Absolute *now = cls;
593   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
594   const struct GNUNET_HELLO_Message *hello;
595   struct GNUNET_HELLO_Message *new_hello;
596   int size;
597
598   size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
599   if ((size < sizeof (struct GNUNET_MessageHeader)) && (0 != UNLINK (fn)))
600     {
601       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
602                                 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
603       return GNUNET_OK;
604     }
605   hello = (const struct GNUNET_HELLO_Message *) buffer;
606   new_hello = GNUNET_HELLO_iterate_addresses (hello,
607                                               GNUNET_YES,
608                                               &discard_expired, now);
609   if (new_hello != NULL)
610     {
611       GNUNET_DISK_fn_write (fn, 
612                             new_hello,
613                             GNUNET_HELLO_size (new_hello),
614                             GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE
615                             | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ);
616       GNUNET_free (new_hello);
617     }
618   else
619     {
620       if (0 != UNLINK (fn))
621         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
622                                   GNUNET_ERROR_TYPE_BULK, "unlink", fn);      
623     }
624   return GNUNET_OK;
625 }
626
627
628 /**
629  * Call this method periodically to scan data/hosts for new hosts.
630  */
631 static void
632 cron_clean_data_hosts (void *cls,
633                        const struct GNUNET_SCHEDULER_TaskContext *tc)
634 {
635   struct GNUNET_TIME_Absolute now;
636
637   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
638     return;
639   now = GNUNET_TIME_absolute_get ();
640   GNUNET_DISK_directory_scan (networkIdDirectory,
641                               &discard_hosts_helper, &now);
642
643   GNUNET_SCHEDULER_add_delayed (tc->sched,
644                                 DATA_HOST_CLEAN_FREQ,
645                                 &cron_clean_data_hosts, NULL);
646 }
647
648
649 /**
650  * Handle ADD-message.
651  *
652  * @param cls closure
653  * @param client identification of the client
654  * @param message the actual message
655  */
656 static void
657 handle_add (void *cls,
658             struct GNUNET_SERVER_Client *client,
659             const struct GNUNET_MessageHeader *message)
660 {
661   const struct PeerAddMessage *pam;
662   const struct GNUNET_MessageHeader *hello;
663   uint16_t size;
664
665   size = ntohs (message->size);
666   if (size <
667       sizeof (struct PeerAddMessage) + sizeof (struct GNUNET_MessageHeader))
668     {
669       GNUNET_break (0);
670       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
671       return;
672     }
673   pam = (const struct PeerAddMessage *) message;
674   hello = (const struct GNUNET_MessageHeader *) &pam[1];
675   if (size != sizeof (struct PeerAddMessage) + ntohs (hello->size))
676     {
677       GNUNET_break (0);
678       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
679       return;
680     }
681   bind_address (&pam->peer, (const struct GNUNET_HELLO_Message *) hello);
682   GNUNET_SERVER_receive_done (client, GNUNET_OK);
683 }
684
685
686 /**
687  * Handle GET-message.
688  *
689  * @param cls closure
690  * @param client identification of the client
691  * @param message the actual message
692  */
693 static void
694 handle_get (void *cls,
695             struct GNUNET_SERVER_Client *client,
696             const struct GNUNET_MessageHeader *message)
697 {
698   const struct ListPeerMessage *lpm;
699
700   lpm = (const struct ListPeerMessage *) message;
701   send_to_each_host (&lpm->peer, ntohl (lpm->trust_change), client);
702 }
703
704
705 /**
706  * Handle GET-ALL-message.
707  *
708  * @param cls closure
709  * @param client identification of the client
710  * @param message the actual message
711  */
712 static void
713 handle_get_all (void *cls,
714                 struct GNUNET_SERVER_Client *client,
715                 const struct GNUNET_MessageHeader *message)
716 {
717   const struct ListAllPeersMessage *lpm;
718
719   lpm = (const struct ListAllPeersMessage *) message;
720   send_to_each_host (NULL, ntohl (lpm->trust_change), client);
721 }
722
723
724 /**
725  * Handle NOTIFY-message.
726  *
727  * @param cls closure
728  * @param client identification of the client
729  * @param message the actual message
730  */
731 static void
732 handle_notify (void *cls,
733             struct GNUNET_SERVER_Client *client,
734             const struct GNUNET_MessageHeader *message)
735 {
736   struct InfoMessage *msg;
737   struct HostEntry *pos;
738
739   GNUNET_SERVER_notification_context_add (notify_list,
740                                           client);
741   pos = hosts;
742   while (NULL != pos)
743     {
744       msg = make_info_message (pos);
745       GNUNET_SERVER_notification_context_unicast (notify_list,
746                                                   client,
747                                                   &msg->header,
748                                                   GNUNET_NO);
749       GNUNET_free (msg);
750       pos = pos->next;
751     }
752 }
753
754
755 /**
756  * List of handlers for the messages understood by this
757  * service.
758  */
759 static struct GNUNET_SERVER_MessageHandler handlers[] = {
760   {&handle_add, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_ADD, 0},
761   {&handle_get, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET,
762    sizeof (struct ListPeerMessage)},
763   {&handle_get_all, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
764    sizeof (struct ListAllPeersMessage)},
765   {&handle_notify, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
766    sizeof (struct GNUNET_MessageHeader)},
767   {NULL, NULL, 0, 0}
768 };
769
770
771 /**
772  * Clean up our state.  Called during shutdown.
773  *
774  * @param cls unused
775  * @param tc scheduler task context, unused
776  */
777 static void
778 shutdown_task (void *cls,
779                const struct GNUNET_SCHEDULER_TaskContext *tc)
780 {
781   GNUNET_SERVER_notification_context_destroy (notify_list);
782   notify_list = NULL;
783   if (stats != NULL)
784     {
785       GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
786       stats = NULL;
787     }
788 }
789
790
791 /**
792  * Process statistics requests.
793  *
794  * @param cls closure
795  * @param sched scheduler to use
796  * @param server the initialized server
797  * @param cfg configuration to use
798  */
799 static void
800 run (void *cls,
801      struct GNUNET_SCHEDULER_Handle *sched,
802      struct GNUNET_SERVER_Handle *server,
803      const struct GNUNET_CONFIGURATION_Handle *cfg)
804 {
805   stats = GNUNET_STATISTICS_create (sched, "peerinfo", cfg);
806   notify_list = GNUNET_SERVER_notification_context_create (server, 0);
807   GNUNET_assert (GNUNET_OK ==
808                  GNUNET_CONFIGURATION_get_value_filename (cfg,
809                                                           "peerinfo",
810                                                           "HOSTS",
811                                                           &networkIdDirectory));
812   GNUNET_assert (GNUNET_OK ==
813                  GNUNET_CONFIGURATION_get_value_filename (cfg,
814                                                           "peerinfo",
815                                                           "TRUST",
816                                                           &trustDirectory));
817   GNUNET_DISK_directory_create (networkIdDirectory);
818   GNUNET_DISK_directory_create (trustDirectory);
819   GNUNET_SCHEDULER_add_with_priority (sched,
820                                       GNUNET_SCHEDULER_PRIORITY_IDLE,
821                                       &cron_scan_directory_data_hosts, NULL);
822   GNUNET_SCHEDULER_add_with_priority (sched,
823                                       GNUNET_SCHEDULER_PRIORITY_HIGH,
824                                       &cron_flush_trust, NULL);
825   GNUNET_SCHEDULER_add_with_priority (sched,
826                                       GNUNET_SCHEDULER_PRIORITY_IDLE,
827                                       &cron_clean_data_hosts, NULL);
828   GNUNET_SCHEDULER_add_delayed (sched,
829                                 GNUNET_TIME_UNIT_FOREVER_REL,
830                                 &shutdown_task, NULL);
831   GNUNET_SERVER_add_handlers (server, handlers);
832 }
833
834
835 /**
836  * The main function for the statistics service.
837  *
838  * @param argc number of arguments from the command line
839  * @param argv command line arguments
840  * @return 0 ok, 1 on error
841  */
842 int
843 main (int argc, char *const *argv)
844 {
845   int ret;
846
847   ret = (GNUNET_OK ==
848          GNUNET_SERVICE_run (argc,
849                              argv,
850                               "peerinfo",
851                              GNUNET_SERVICE_OPTION_NONE,
852                              &run, NULL)) ? 0 : 1;
853   GNUNET_free_non_null (networkIdDirectory);
854   GNUNET_free_non_null (trustDirectory);
855   return ret;
856 }
857
858
859 /* end of gnunet-service-peerinfo.c */