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