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