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