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