allow empty/NULL context message
[oweals/gnunet.git] / src / peerinfo / gnunet-service-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-2014 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, 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/).
27  *
28  * @author Christian Grothoff
29  */
30
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_hello_lib.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_statistics_service.h"
36 #include "peerinfo.h"
37
38 /**
39  * How often do we scan the HOST_DIR for new entries?
40  */
41 #define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
42
43 /**
44  * How often do we discard old entries in data/hosts/?
45  */
46 #define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
47
48
49 /**
50  * In-memory cache of known hosts.
51  */
52 struct HostEntry
53 {
54
55   /**
56    * Identity of the peer.
57    */
58   struct GNUNET_PeerIdentity identity;
59
60   /**
61    * Hello for the peer (can be NULL)
62    */
63   struct GNUNET_HELLO_Message *hello;
64
65   /**
66    * Friend only hello for the peer (can be NULL)
67    */
68   struct GNUNET_HELLO_Message *friend_only_hello;
69
70 };
71
72 /**
73  * Transmit context for GET requests
74  */
75 struct TransmitContext
76 {
77   /**
78    * Server transmit context
79    */
80   struct GNUNET_SERVER_TransmitContext *tc;
81
82   /**
83    * Include friend only HELLOs #GNUNET_YES or #GNUNET_NO
84    */
85   int friend_only;
86 };
87
88
89 /**
90  * Result of reading a file
91  */
92 struct ReadHostFileContext
93 {
94   /**
95    * Hello for the peer (can be NULL)
96    */
97   struct GNUNET_HELLO_Message *hello;
98
99   /**
100    * Friend only hello for the peer (can be NULL)
101    */
102   struct GNUNET_HELLO_Message *friend_only_hello;
103 };
104
105
106 /**
107  * Client notification context
108  */
109 struct NotificationContext
110 {
111   /**
112    * Next in DLL
113    */
114   struct NotificationContext *prev;
115
116   /**
117    * Previous in DLL
118    */
119   struct NotificationContext *next;
120
121   /**
122    * Server client
123    */
124   struct GNUNET_SERVER_Client *client;
125
126   /**
127    * Interested in friend only HELLO?
128    */
129   int include_friend_only;
130 };
131
132
133 /**
134  * The in-memory list of known hosts, mapping of
135  * host IDs to 'struct HostEntry*' values.
136  */
137 static struct GNUNET_CONTAINER_MultiPeerMap *hostmap;
138
139 /**
140  * Clients to immediately notify about all changes.
141  */
142 static struct GNUNET_SERVER_NotificationContext *notify_list;
143
144 /**
145  * Directory where the hellos are stored in (peerinfo/)
146  */
147 static char *networkIdDirectory;
148
149 /**
150  * Handle for reporting statistics.
151  */
152 static struct GNUNET_STATISTICS_Handle *stats;
153
154 /**
155  * DLL of notification contexts: head
156  */
157 static struct NotificationContext *nc_head;
158
159 /**
160  * DLL of notification contexts: tail
161  */
162 static struct NotificationContext *nc_tail;
163
164 /**
165  * Handle for task to run #cron_clean_data_hosts()
166  */
167 static struct GNUNET_SCHEDULER_Task *cron_clean;
168
169 /**
170  * Handle for task to run #cron_scan_directory_hosts()
171  */
172 static struct GNUNET_SCHEDULER_Task *cron_scan;
173
174
175 /**
176  * Notify all clients in the notify list about the
177  * given host entry changing.
178  *
179  * @param he entry of the host for which we generate a notification
180  * @param include_friend_only create public of friend-only message
181  * @return generated notification message
182  */
183 static struct InfoMessage *
184 make_info_message (const struct HostEntry *he,
185                    int include_friend_only)
186 {
187   struct InfoMessage *im;
188   struct GNUNET_HELLO_Message *src;
189   size_t hs;
190
191   if (GNUNET_YES == include_friend_only)
192         src = he->friend_only_hello;
193   else
194         src = he->hello;
195
196   hs = (NULL == src) ? 0 : GNUNET_HELLO_size (src);
197   im = GNUNET_malloc (sizeof (struct InfoMessage) + hs);
198   im->header.size = htons (hs + sizeof (struct InfoMessage));
199   im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
200   im->peer = he->identity;
201   if (NULL != src)
202     memcpy (&im[1], src, hs);
203   return im;
204 }
205
206
207 /**
208  * Address iterator that causes expired entries to be discarded.
209  *
210  * @param cls pointer to the current time
211  * @param address the address
212  * @param expiration expiration time for the address
213  * @return #GNUNET_NO if expiration smaller than the current time
214  */
215 static int
216 discard_expired (void *cls,
217                  const struct GNUNET_HELLO_Address *address,
218                  struct GNUNET_TIME_Absolute expiration)
219 {
220   const struct GNUNET_TIME_Absolute *now = cls;
221
222   if (now->abs_value_us > expiration.abs_value_us)
223   {
224     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
225                 _("Removing expired address of transport `%s'\n"),
226                 address->transport_name);
227     return GNUNET_NO;
228   }
229   return GNUNET_OK;
230 }
231
232
233 /**
234  * Address iterator that counts the remaining addresses.
235  *
236  * @param cls pointer to the counter
237  * @param address the address
238  * @param expiration expiration time for the address
239  * @return #GNUNET_OK (always)
240  */
241 static int
242 count_addresses (void *cls,
243                  const struct GNUNET_HELLO_Address *address,
244                  struct GNUNET_TIME_Absolute expiration)
245 {
246   unsigned int *cnt = cls;
247
248   (*cnt)++;
249   return GNUNET_OK;
250 }
251
252
253 /**
254  * Get the filename under which we would store the GNUNET_HELLO_Message
255  * for the given host and protocol.
256  *
257  * @param id peer for which we need the filename for the HELLO
258  * @return filename of the form DIRECTORY/HOSTID
259  */
260 static char *
261 get_host_filename (const struct GNUNET_PeerIdentity *id)
262 {
263   char *fn;
264
265   if (NULL == networkIdDirectory)
266     return NULL;
267   GNUNET_asprintf (&fn, "%s%s%s", networkIdDirectory, DIR_SEPARATOR_STR,
268                    GNUNET_i2s_full (id));
269   return fn;
270 }
271
272
273 /**
274  * Broadcast information about the given entry to all
275  * clients that care.
276  *
277  * @param entry entry to broadcast about
278  */
279 static void
280 notify_all (struct HostEntry *entry)
281 {
282   struct InfoMessage *msg_pub;
283   struct InfoMessage *msg_friend;
284   struct NotificationContext *cur;
285
286   msg_pub = make_info_message (entry, GNUNET_NO);
287   msg_friend = make_info_message (entry, GNUNET_YES);
288   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
289               "Notifying all clients about peer `%s'\n",
290               GNUNET_i2s(&entry->identity));
291   for (cur = nc_head; NULL != cur; cur = cur->next)
292   {
293     if (GNUNET_NO == cur->include_friend_only)
294       {
295         GNUNET_SERVER_notification_context_unicast (notify_list,
296                                                     cur->client,
297                                                     &msg_pub->header,
298                                                     GNUNET_NO);
299       }
300     if (GNUNET_YES == cur->include_friend_only)
301     {
302       GNUNET_SERVER_notification_context_unicast (notify_list,
303                                                   cur->client,
304                                                   &msg_friend->header,
305                                                   GNUNET_NO);
306     }
307   }
308   GNUNET_free (msg_pub);
309   GNUNET_free (msg_friend);
310 }
311
312
313 /**
314  * Bind a host address (hello) to a hostId.
315  *
316  * @param peer the peer for which this is a hello
317  * @param hello the verified (!) hello message
318  */
319 static void
320 update_hello (const struct GNUNET_PeerIdentity *peer,
321               const struct GNUNET_HELLO_Message *hello);
322
323
324 /**
325  * Try to read the HELLOs in the given filename and discard expired
326  * addresses.  Removes the file if one the HELLO is malformed.  If all
327  * addresses are expired, the HELLO is also removed (but the HELLO
328  * with the public key is still returned if it was found and valid).
329  * The file can contain multiple HELLO messages.
330  *
331  * @param fn name of the file
332  * @param unlink_garbage if #GNUNET_YES, try to remove useless files
333  * @param r ReadHostFileContext to store the resutl
334  */
335 static void
336 read_host_file (const char *fn,
337                 int unlink_garbage,
338                 struct ReadHostFileContext *r)
339 {
340   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
341   unsigned int size_total;
342   struct GNUNET_TIME_Absolute now;
343   unsigned int left;
344   const struct GNUNET_HELLO_Message *hello;
345   struct GNUNET_HELLO_Message *hello_clean;
346   unsigned read_pos;
347   int size_hello;
348
349   r->friend_only_hello = NULL;
350   r->hello = NULL;
351
352   if (GNUNET_YES != GNUNET_DISK_file_test (fn))
353     return;
354   size_total = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
355   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356               "Read %u bytes from `%s'\n",
357               size_total,
358               fn);
359   if (size_total < sizeof (struct GNUNET_MessageHeader))
360   {
361     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362                 _("Failed to parse HELLO in file `%s': %s\n"),
363                 fn, "Fail has invalid size");
364     if ( (GNUNET_YES == unlink_garbage) &&
365          (0 != UNLINK (fn)) &&
366          (ENOENT != errno) )
367       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
368                                 "unlink",
369                                 fn);
370     return;
371   }
372
373   read_pos = 0;
374   while (read_pos < size_total)
375   {
376     hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
377     size_hello = GNUNET_HELLO_size (hello);
378     if ( (0 == size_hello) ||
379          (size_total - read_pos < size_hello) )
380     {
381       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
382                   _("Failed to parse HELLO in file `%s'\n"),
383                   fn);
384       if (0 == read_pos)
385       {
386         if ((GNUNET_YES == unlink_garbage) &&
387             (0 != UNLINK (fn)) &&
388             (ENOENT != errno) )
389           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
390                                     "unlink",
391                                     fn);
392       }
393       else
394       {
395         if ((GNUNET_YES == unlink_garbage) &&
396             (0 != TRUNCATE (fn, read_pos)) &&
397             (ENOENT != errno) )
398           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
399                                     "truncate",
400                                     fn);
401       }
402       return;
403     }
404
405     now = GNUNET_TIME_absolute_get ();
406     hello_clean = GNUNET_HELLO_iterate_addresses (hello, GNUNET_YES,
407                                                   &discard_expired, &now);
408     if (NULL == hello_clean)
409     {
410       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
411                   _("Failed to parse HELLO in file `%s'\n"),
412                   fn);
413       if ((GNUNET_YES == unlink_garbage) &&
414           (0 != UNLINK (fn)) &&
415           (ENOENT != errno) )
416         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
417                                   "unlink",
418                                   fn);
419       return;
420     }
421     left = 0;
422     (void) GNUNET_HELLO_iterate_addresses (hello_clean, GNUNET_NO,
423                                            &count_addresses, &left);
424
425     if (0 == left)
426     {
427       GNUNET_free (hello_clean);
428       break;
429     }
430
431     if (GNUNET_NO == GNUNET_HELLO_is_friend_only (hello_clean))
432     {
433       if (NULL == r->hello)
434         r->hello = hello_clean;
435       else
436       {
437         GNUNET_break (0);
438         GNUNET_free (r->hello);
439         r->hello = hello_clean;
440       }
441     }
442     else
443     {
444       if (NULL == r->friend_only_hello)
445         r->friend_only_hello = hello_clean;
446       else
447       {
448         GNUNET_break (0);
449         GNUNET_free (r->friend_only_hello);
450         r->friend_only_hello = hello_clean;
451       }
452     }
453     read_pos += size_hello;
454   }
455
456   if (0 == left)
457   {
458     /* no addresses left, remove from disk */
459     if ( (GNUNET_YES == unlink_garbage) &&
460          (0 != UNLINK (fn)) )
461       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
462                                 "unlink",
463                                 fn);
464   }
465   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
466               "Found `%s' and `%s' HELLO message in file\n",
467               (NULL != r->hello) ? "public" : "NON-public",
468               (NULL != r->friend_only_hello) ? "friend only" : "NO friend only");
469 }
470
471
472 /**
473  * Add a host to the list and notify clients about this event
474  *
475  * @param identity the identity of the host
476  * @return the HostEntry
477  */
478 static struct HostEntry *
479 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
480 {
481   struct HostEntry *entry;
482   struct ReadHostFileContext r;
483   char *fn;
484
485   entry = GNUNET_CONTAINER_multipeermap_get (hostmap, identity);
486   if (NULL == entry)
487   {
488     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489                 "Adding new peer `%s'\n",
490                 GNUNET_i2s (identity));
491     GNUNET_STATISTICS_update (stats, gettext_noop ("# peers known"), 1,
492                               GNUNET_NO);
493     entry = GNUNET_new (struct HostEntry);
494     entry->identity = *identity;
495     GNUNET_assert (GNUNET_OK ==
496                    GNUNET_CONTAINER_multipeermap_put (hostmap,
497                                                       &entry->identity,
498                                                       entry,
499                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
500     notify_all (entry);
501     fn = get_host_filename (identity);
502     if (NULL != fn)
503     {
504       read_host_file (fn, GNUNET_YES, &r);
505       if (NULL != r.hello)
506         update_hello (identity, r.hello);
507       if (NULL != r.friend_only_hello)
508         update_hello (identity, r.friend_only_hello);
509       GNUNET_free_non_null (r.hello);
510       GNUNET_free_non_null (r.friend_only_hello);
511       GNUNET_free (fn);
512     }
513   }
514   return entry;
515 }
516
517
518 /**
519  * Remove a file that should not be there.  LOG
520  * success or failure.
521  *
522  * @param fullname name of the file to remove
523  */
524 static void
525 remove_garbage (const char *fullname)
526 {
527   if (0 == UNLINK (fullname))
528     GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
529                 _("File `%s' in directory `%s' does not match naming convention. Removed.\n"),
530                 fullname,
531                 networkIdDirectory);
532   else
533     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
534                               "unlink",
535                               fullname);
536 }
537
538
539 /**
540  * Closure for #hosts_directory_scan_callback().
541  */
542 struct DirScanContext
543 {
544   /**
545    * #GNUNET_YES if we should remove files that are broken,
546    * #GNUNET_NO if the directory we are iterating over should
547    * be treated as read-only by us.
548    */
549   int remove_files;
550
551   /**
552    * Counter for the number of (valid) entries found, incremented
553    * by one for each match.
554    */
555   unsigned int matched;
556 };
557
558
559 /**
560  * Function that is called on each HELLO file in a particular directory.
561  * Try to parse the file and add the HELLO to our list.
562  *
563  * @param cls pointer to 'unsigned int' to increment for each file, or NULL
564  *            if the file is from a read-only, read-once resource directory
565  * @param fullname name of the file to parse
566  * @return #GNUNET_OK (continue iteration)
567  */
568 static int
569 hosts_directory_scan_callback (void *cls,
570                                const char *fullname)
571 {
572   struct DirScanContext *dsc = cls;
573   struct GNUNET_PeerIdentity identity;
574   struct ReadHostFileContext r;
575   const char *filename;
576   struct GNUNET_PeerIdentity id_public;
577   struct GNUNET_PeerIdentity id_friend;
578   struct GNUNET_PeerIdentity id;
579
580   if (GNUNET_YES != GNUNET_DISK_file_test (fullname))
581     return GNUNET_OK;           /* ignore non-files */
582
583   filename = strrchr (fullname, DIR_SEPARATOR);
584   if ((NULL == filename) || (1 > strlen (filename)))
585     filename = fullname;
586   else
587     filename ++;
588
589   read_host_file (fullname, dsc->remove_files, &r);
590   if ( (NULL == r.hello) && (NULL == r.friend_only_hello))
591     return GNUNET_OK;
592   if (NULL != r.friend_only_hello)
593   {
594     if (GNUNET_OK != GNUNET_HELLO_get_id (r.friend_only_hello, &id_friend))
595       if (GNUNET_YES == dsc->remove_files)
596       {
597         remove_garbage (fullname);
598         return GNUNET_OK;
599       }
600     id = id_friend;
601   }
602   if (NULL != r.hello)
603   {
604     if (GNUNET_OK != GNUNET_HELLO_get_id (r.hello, &id_public))
605       if (GNUNET_YES == dsc->remove_files)
606       {
607         remove_garbage (fullname);
608         return GNUNET_OK;
609       }
610     id = id_public;
611   }
612
613   if ( (NULL != r.hello) && (NULL != r.friend_only_hello) &&
614        (0 != memcmp (&id_friend, &id_public, sizeof (id_friend))) )
615   {
616     /* HELLOs are not for the same peer */
617     GNUNET_break (0);
618     if (GNUNET_YES == dsc->remove_files)
619       remove_garbage (fullname);
620     return GNUNET_OK;
621   }
622   if (GNUNET_OK ==
623       GNUNET_CRYPTO_eddsa_public_key_from_string (filename,
624                                                   strlen (filename),
625                                                   &identity.public_key))
626   {
627     if (0 != memcmp (&id, &identity, sizeof (id_friend)))
628     {
629       /* HELLOs are not for the same peer */
630       GNUNET_break (0);
631       if (GNUNET_YES == dsc->remove_files)
632         remove_garbage (fullname);
633       return GNUNET_OK;
634     }
635   }
636
637   /* ok, found something valid, remember HELLO */
638   add_host_to_known_hosts (&id);
639   if (NULL != r.hello)
640   {
641     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
642                 "Updating peer `%s' public HELLO \n",
643                 GNUNET_i2s (&id));
644     update_hello (&id, r.hello);
645     GNUNET_free (r.hello);
646   }
647   if (NULL != r.friend_only_hello)
648   {
649     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
650                 "Updating peer `%s' friend only HELLO \n",
651                 GNUNET_i2s (&id));
652     update_hello (&id, r.friend_only_hello);
653     GNUNET_free (r.friend_only_hello);
654   }
655   dsc->matched++;
656   return GNUNET_OK;
657 }
658
659
660 /**
661  * Call this method periodically to scan data/hosts for new hosts.
662  *
663  * @param cls unused
664  */
665 static void
666 cron_scan_directory_data_hosts (void *cls)
667 {
668   static unsigned int retries;
669   struct DirScanContext dsc;
670
671   cron_scan = NULL;
672   if (GNUNET_SYSERR ==
673       GNUNET_DISK_directory_create (networkIdDirectory))
674   {
675     cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
676                                                             GNUNET_SCHEDULER_PRIORITY_IDLE,
677                                                             &cron_scan_directory_data_hosts, NULL);
678     return;
679   }
680   dsc.matched = 0;
681   dsc.remove_files = GNUNET_YES;
682   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
683               _("Scanning directory `%s'\n"),
684               networkIdDirectory);
685   GNUNET_DISK_directory_scan (networkIdDirectory,
686                               &hosts_directory_scan_callback, &dsc);
687   if ((0 == dsc.matched) && (0 == (++retries & 31)))
688     GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
689                 _("Still no peers found in `%s'!\n"),
690                 networkIdDirectory);
691   cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
692                                                           GNUNET_SCHEDULER_PRIORITY_IDLE,
693                                                           &cron_scan_directory_data_hosts,
694                                                           NULL);
695 }
696
697
698 /**
699  * Update the HELLO of a friend by merging the addresses.
700  *
701  * @param hello original hello
702  * @param friend_hello hello with additional addresses
703  * @return merged HELLO
704  */
705 static struct GNUNET_HELLO_Message *
706 update_friend_hello (const struct GNUNET_HELLO_Message *hello,
707                      const struct GNUNET_HELLO_Message *friend_hello)
708 {
709   struct GNUNET_HELLO_Message * res;
710   struct GNUNET_HELLO_Message * tmp;
711   struct GNUNET_PeerIdentity pid;
712
713   if (NULL != friend_hello)
714   {
715     res = GNUNET_HELLO_merge (hello, friend_hello);
716     GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
717     return res;
718   }
719
720   if (GNUNET_OK !=
721       GNUNET_HELLO_get_id (hello, &pid))
722   {
723     GNUNET_break (0);
724     return NULL;
725   }
726   tmp = GNUNET_HELLO_create (&pid.public_key,
727                              NULL,
728                              NULL,
729                              GNUNET_YES);
730   res = GNUNET_HELLO_merge (hello, tmp);
731   GNUNET_free (tmp);
732   GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
733   return res;
734 }
735
736
737 /**
738  * Bind a host address (hello) to a hostId.
739  *
740  * @param peer the peer for which this is a hello
741  * @param hello the verified (!) hello message
742  */
743 static void
744 update_hello (const struct GNUNET_PeerIdentity *peer,
745               const struct GNUNET_HELLO_Message *hello)
746 {
747   char *fn;
748   struct HostEntry *host;
749   struct GNUNET_HELLO_Message *mrg;
750   struct GNUNET_HELLO_Message **dest;
751   struct GNUNET_TIME_Absolute delta;
752   unsigned int cnt;
753   unsigned int size;
754   int friend_hello_type;
755   int store_hello;
756   int store_friend_hello;
757   int pos;
758   char *buffer;
759
760   host = GNUNET_CONTAINER_multipeermap_get (hostmap, peer);
761   GNUNET_assert (NULL != host);
762
763   friend_hello_type = GNUNET_HELLO_is_friend_only (hello);
764   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
765               "Updating %s HELLO for `%s'\n",
766               (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
767               GNUNET_i2s (peer));
768
769   dest = NULL;
770   if (GNUNET_YES == friend_hello_type)
771   {
772     dest = &host->friend_only_hello;
773   }
774   else
775   {
776     dest = &host->hello;
777   }
778
779   if (NULL == (*dest))
780   {
781     (*dest) = GNUNET_malloc (GNUNET_HELLO_size (hello));
782     memcpy ((*dest), hello, GNUNET_HELLO_size (hello));
783   }
784   else
785   {
786     mrg = GNUNET_HELLO_merge ((*dest), hello);
787     delta = GNUNET_HELLO_equals (mrg, (*dest), GNUNET_TIME_absolute_get ());
788     if (delta.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
789     {
790       /* no differences, just ignore the update */
791       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
792                   "No change in %s HELLO for `%s'\n",
793                   (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
794                   GNUNET_i2s (peer));
795       GNUNET_free (mrg);
796       return;
797     }
798     GNUNET_free ((*dest));
799     (*dest) = mrg;
800   }
801
802   if ( (NULL != (host->hello)) &&
803        (GNUNET_NO == friend_hello_type) )
804   {
805     /* Update friend only hello */
806     mrg = update_friend_hello (host->hello, host->friend_only_hello);
807     if (NULL != host->friend_only_hello)
808       GNUNET_free (host->friend_only_hello);
809     host->friend_only_hello = mrg;
810   }
811
812   if (NULL != host->hello)
813     GNUNET_assert ((GNUNET_NO ==
814                     GNUNET_HELLO_is_friend_only (host->hello)));
815   if (NULL != host->friend_only_hello)
816     GNUNET_assert ((GNUNET_YES ==
817                     GNUNET_HELLO_is_friend_only (host->friend_only_hello)));
818
819   fn = get_host_filename (peer);
820   if ( (NULL != fn) &&
821        (GNUNET_OK ==
822         GNUNET_DISK_directory_create_for_file (fn)) )
823   {
824     store_hello = GNUNET_NO;
825     size = 0;
826     cnt = 0;
827     if (NULL != host->hello)
828       (void) GNUNET_HELLO_iterate_addresses (host->hello,
829                                              GNUNET_NO,
830                                              &count_addresses,
831                                              &cnt);
832     if (cnt > 0)
833     {
834       store_hello = GNUNET_YES;
835       size += GNUNET_HELLO_size (host->hello);
836     }
837     cnt = 0;
838     if (NULL != host->friend_only_hello)
839       (void) GNUNET_HELLO_iterate_addresses (host->friend_only_hello,
840                                              GNUNET_NO,
841                                              &count_addresses,
842                                              &cnt);
843     store_friend_hello = GNUNET_NO;
844     if (0 < cnt)
845     {
846       store_friend_hello = GNUNET_YES;
847       size += GNUNET_HELLO_size (host->friend_only_hello);
848     }
849
850     if ( (GNUNET_NO == store_hello) &&
851          (GNUNET_NO == store_friend_hello) )
852     {
853       /* no valid addresses, don't put HELLO on disk; in fact,
854          if one exists on disk, remove it */
855       (void) UNLINK (fn);
856     }
857     else
858     {
859       buffer = GNUNET_malloc (size);
860       pos = 0;
861
862       if (GNUNET_YES == store_hello)
863       {
864         memcpy (buffer,
865                 host->hello,
866                 GNUNET_HELLO_size (host->hello));
867         pos += GNUNET_HELLO_size (host->hello);
868       }
869       if (GNUNET_YES == store_friend_hello)
870       {
871         memcpy (&buffer[pos],
872                 host->friend_only_hello,
873                 GNUNET_HELLO_size (host->friend_only_hello));
874         pos += GNUNET_HELLO_size (host->friend_only_hello);
875       }
876       GNUNET_assert (pos == size);
877
878       if (GNUNET_SYSERR == GNUNET_DISK_fn_write (fn, buffer, size,
879                                                  GNUNET_DISK_PERM_USER_READ |
880                                                  GNUNET_DISK_PERM_USER_WRITE |
881                                                  GNUNET_DISK_PERM_GROUP_READ |
882                                                  GNUNET_DISK_PERM_OTHER_READ))
883         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
884       else
885         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
886                     "Stored %s %s HELLO in %s  with total size %u\n",
887                     (GNUNET_YES == store_friend_hello) ? "friend-only": "",
888                     (GNUNET_YES == store_hello) ? "public": "",
889                     fn, size);
890       GNUNET_free (buffer);
891     }
892   }
893   GNUNET_free_non_null (fn);
894   notify_all (host);
895 }
896
897
898 /**
899  * Do transmit info about peer to given host.
900  *
901  * @param cls NULL to hit all hosts, otherwise specifies a particular target
902  * @param key hostID
903  * @param value information to transmit
904  * @return #GNUNET_YES (continue to iterate)
905  */
906 static int
907 add_to_tc (void *cls,
908            const struct GNUNET_PeerIdentity *key,
909            void *value)
910 {
911   struct TransmitContext *tc = cls;
912   struct HostEntry *pos = value;
913   struct InfoMessage *im;
914   uint16_t hs;
915   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
916
917   hs = 0;
918   im = (struct InfoMessage *) buf;
919
920   if ( (NULL != pos->hello) &&
921        (GNUNET_NO == tc->friend_only) )
922   {
923         /* Copy public HELLO */
924     hs = GNUNET_HELLO_size (pos->hello);
925     GNUNET_assert (hs < GNUNET_SERVER_MAX_MESSAGE_SIZE -
926                    sizeof (struct InfoMessage));
927     memcpy (&im[1], pos->hello, hs);
928     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
929                 "Sending public HELLO with size %u for peer `%4s'\n",
930                 hs, GNUNET_i2s (key));
931   }
932   else if ( (NULL != pos->friend_only_hello) &&
933             (GNUNET_YES == tc->friend_only) )
934   {
935         /* Copy friend only HELLO */
936     hs = GNUNET_HELLO_size (pos->friend_only_hello);
937     GNUNET_assert (hs < GNUNET_SERVER_MAX_MESSAGE_SIZE -
938                    sizeof (struct InfoMessage));
939     memcpy (&im[1], pos->friend_only_hello, hs);
940     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
941                 "Sending friend-only HELLO with size %u for peer `%4s'\n",
942                 hs,
943                 GNUNET_i2s (key));
944   }
945   else
946   {
947     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
948                 "Adding no HELLO for peer `%s'\n",
949                 GNUNET_i2s (key));
950   }
951
952   im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
953   im->header.size = htons (sizeof (struct InfoMessage) + hs);
954   im->reserved = htonl (0);
955   im->peer = pos->identity;
956   GNUNET_SERVER_transmit_context_append_message (tc->tc, &im->header);
957   return GNUNET_YES;
958 }
959
960
961 /**
962  * @brief delete expired HELLO entries in directory
963  *
964  * @param cls pointer to current time (`struct GNUNET_TIME_Absolute *`)
965  * @param fn filename to test to see if the HELLO expired
966  * @return #GNUNET_OK (continue iteration)
967  */
968 static int
969 discard_hosts_helper (void *cls,
970                       const char *fn)
971 {
972   struct GNUNET_TIME_Absolute *now = cls;
973   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
974   const struct GNUNET_HELLO_Message *hello;
975   struct GNUNET_HELLO_Message *new_hello;
976   int read_size;
977   unsigned int cur_hello_size;
978   unsigned int new_hello_size;
979   int read_pos;
980   int write_pos;
981   unsigned int cnt;
982   char *writebuffer;
983
984   read_size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
985   if (read_size < (int) sizeof (struct GNUNET_MessageHeader))
986   {
987     if (0 != UNLINK (fn))
988       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
989                                 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
990     return GNUNET_OK;
991   }
992
993   writebuffer = GNUNET_malloc (read_size);
994   read_pos = 0;
995   write_pos = 0;
996   while (read_pos < read_size)
997   {
998     /* Check each HELLO */
999     hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
1000     cur_hello_size = GNUNET_HELLO_size (hello);
1001     if (0 == cur_hello_size)
1002     {
1003       /* Invalid data, discard */
1004       if (0 != UNLINK (fn))
1005         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1006                                   GNUNET_ERROR_TYPE_BULK,
1007                                   "unlink", fn);
1008       return GNUNET_OK;
1009     }
1010     new_hello = GNUNET_HELLO_iterate_addresses (hello,
1011                                                 GNUNET_YES,
1012                                                 &discard_expired, now);
1013     cnt = 0;
1014     if (NULL != new_hello)
1015       (void) GNUNET_HELLO_iterate_addresses (hello,
1016                                              GNUNET_NO,
1017                                              &count_addresses, &cnt);
1018     if ( (NULL != new_hello) && (0 < cnt) )
1019     {
1020       /* Store new HELLO to write it when done */
1021       new_hello_size = GNUNET_HELLO_size (new_hello);
1022       memcpy (&writebuffer[write_pos], new_hello, new_hello_size);
1023       write_pos += new_hello_size;
1024     }
1025     read_pos += cur_hello_size;
1026     GNUNET_free_non_null (new_hello);
1027   }
1028
1029   if (0 < write_pos)
1030   {
1031       GNUNET_DISK_fn_write (fn, writebuffer,write_pos,
1032                             GNUNET_DISK_PERM_USER_READ |
1033                             GNUNET_DISK_PERM_USER_WRITE |
1034                             GNUNET_DISK_PERM_GROUP_READ |
1035                             GNUNET_DISK_PERM_OTHER_READ);
1036   }
1037   else if (0 != UNLINK (fn))
1038     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1039                               GNUNET_ERROR_TYPE_BULK,
1040                               "unlink", fn);
1041
1042   GNUNET_free (writebuffer);
1043   return GNUNET_OK;
1044 }
1045
1046
1047 /**
1048  * Call this method periodically to scan peerinfo/ for ancient
1049  * HELLOs to expire.
1050  *
1051  * @param cls unused
1052  */
1053 static void
1054 cron_clean_data_hosts (void *cls)
1055 {
1056   struct GNUNET_TIME_Absolute now;
1057
1058   cron_clean = NULL;
1059   now = GNUNET_TIME_absolute_get ();
1060   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1061               _("Cleaning up directory `%s'\n"),
1062               networkIdDirectory);
1063   GNUNET_DISK_directory_scan (networkIdDirectory,
1064                               &discard_hosts_helper,
1065                               &now);
1066   cron_clean = GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
1067                                              &cron_clean_data_hosts,
1068                                              NULL);
1069 }
1070
1071
1072 /**
1073  * Handle HELLO-message.
1074  *
1075  * @param cls closure
1076  * @param client identification of the client
1077  * @param message the actual message
1078  */
1079 static void
1080 handle_hello (void *cls,
1081               struct GNUNET_SERVER_Client *client,
1082               const struct GNUNET_MessageHeader *message)
1083 {
1084   const struct GNUNET_HELLO_Message *hello;
1085   struct GNUNET_PeerIdentity pid;
1086
1087   hello = (const struct GNUNET_HELLO_Message *) message;
1088   if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
1089   {
1090     GNUNET_break (0);
1091     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1092     return;
1093   }
1094   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1095               "`%s' message received for peer `%4s'\n",
1096               "HELLO",
1097               GNUNET_i2s (&pid));
1098   add_host_to_known_hosts (&pid);
1099   update_hello (&pid, hello);
1100   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1101 }
1102
1103
1104 /**
1105  * Handle GET-message.
1106  *
1107  * @param cls closure
1108  * @param client identification of the client
1109  * @param message the actual message
1110  */
1111 static void
1112 handle_get (void *cls,
1113             struct GNUNET_SERVER_Client *client,
1114             const struct GNUNET_MessageHeader *message)
1115 {
1116   const struct ListPeerMessage *lpm;
1117   struct TransmitContext tcx;
1118
1119   lpm = (const struct ListPeerMessage *) message;
1120   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1121               "`%s' message received for peer `%4s'\n",
1122               "GET",
1123               GNUNET_i2s (&lpm->peer));
1124   tcx.friend_only = ntohl (lpm->include_friend_only);
1125   tcx.tc = GNUNET_SERVER_transmit_context_create (client);
1126   GNUNET_CONTAINER_multipeermap_get_multiple (hostmap, &lpm->peer,
1127                                               &add_to_tc, &tcx);
1128   GNUNET_SERVER_transmit_context_append_data (tcx.tc, NULL, 0,
1129                                               GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1130   GNUNET_SERVER_transmit_context_run (tcx.tc, GNUNET_TIME_UNIT_FOREVER_REL);
1131 }
1132
1133
1134 /**
1135  * Handle GET-ALL-message.
1136  *
1137  * @param cls closure
1138  * @param client identification of the client
1139  * @param message the actual message
1140  */
1141 static void
1142 handle_get_all (void *cls, struct GNUNET_SERVER_Client *client,
1143                 const struct GNUNET_MessageHeader *message)
1144 {
1145   const struct ListAllPeersMessage *lapm;
1146   struct TransmitContext tcx;
1147
1148   lapm = (const struct ListAllPeersMessage *) message;
1149   tcx.friend_only = ntohl (lapm->include_friend_only);
1150   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1151               "`%s' message received\n",
1152               "GET_ALL");
1153   GNUNET_SERVER_disable_receive_done_warning (client);
1154   tcx.tc = GNUNET_SERVER_transmit_context_create (client);
1155   GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1156                                          &add_to_tc,
1157                                          &tcx);
1158   GNUNET_SERVER_transmit_context_append_data (tcx.tc, NULL, 0,
1159                                               GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1160   GNUNET_SERVER_transmit_context_run (tcx.tc, GNUNET_TIME_UNIT_FOREVER_REL);
1161 }
1162
1163
1164
1165 /**
1166  * Pass the given client the information we have in the respective
1167  * host entry; the client is already in the notification context.
1168  *
1169  * @param cls the `struct GNUNET_SERVER_Client` to notify
1170  * @param key key for the value (unused)
1171  * @param value the `struct HostEntry` to notify the client about
1172  * @return #GNUNET_YES (always, continue to iterate)
1173  */
1174 static int
1175 do_notify_entry (void *cls,
1176                  const struct GNUNET_PeerIdentity *key,
1177                  void *value)
1178 {
1179   struct NotificationContext *nc = cls;
1180   struct HostEntry *he = value;
1181   struct InfoMessage *msg;
1182
1183   if ( (NULL == he->hello) &&
1184        (GNUNET_NO == nc->include_friend_only) )
1185   {
1186     /* We have no public hello  */
1187     return GNUNET_YES;
1188   }
1189
1190   if ( (NULL == he->friend_only_hello) &&
1191        (GNUNET_YES == nc->include_friend_only) )
1192   {
1193     /* We have no friend hello */
1194     return GNUNET_YES;
1195   }
1196
1197   msg = make_info_message (he, nc->include_friend_only);
1198   GNUNET_SERVER_notification_context_unicast (notify_list,
1199                                               nc->client,
1200                                               &msg->header,
1201                                               GNUNET_NO);
1202   GNUNET_free (msg);
1203   return GNUNET_YES;
1204 }
1205
1206
1207 /**
1208  * Handle NOTIFY-message.
1209  *
1210  * @param cls closure
1211  * @param client identification of the client
1212  * @param message the actual message
1213  */
1214 static void
1215 handle_notify (void *cls,
1216                struct GNUNET_SERVER_Client *client,
1217                const struct GNUNET_MessageHeader *message)
1218 {
1219   struct NotifyMessage *nm = (struct NotifyMessage *) message;
1220   struct NotificationContext *nc;
1221
1222   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1223               "`%s' message received\n",
1224               "NOTIFY");
1225   nc = GNUNET_new (struct NotificationContext);
1226   nc->client = client;
1227   nc->include_friend_only = ntohl (nm->include_friend_only);
1228
1229   GNUNET_CONTAINER_DLL_insert (nc_head, nc_tail, nc);
1230   GNUNET_SERVER_client_mark_monitor (client);
1231         GNUNET_SERVER_notification_context_add (notify_list, client);
1232   GNUNET_CONTAINER_multipeermap_iterate (hostmap, &do_notify_entry, nc);
1233   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1234 }
1235
1236
1237 /**
1238  * Client disconnect callback
1239  *
1240  * @param cls unused
1241  * @param client server client
1242  */
1243 static void
1244 disconnect_cb (void *cls,
1245                struct GNUNET_SERVER_Client *client)
1246 {
1247   struct NotificationContext *cur;
1248
1249   for (cur = nc_head; NULL != cur; cur = cur->next)
1250     if (cur->client == client)
1251       break;
1252   if (NULL == cur)
1253     return;
1254   GNUNET_CONTAINER_DLL_remove (nc_head, nc_tail, cur);
1255   GNUNET_free (cur);
1256 }
1257
1258
1259 /**
1260  * Release memory taken by a host entry.
1261  *
1262  * @param cls NULL
1263  * @param key key of the host entry
1264  * @param value the `struct HostEntry` to free
1265  * @return #GNUNET_YES (continue to iterate)
1266  */
1267 static int
1268 free_host_entry (void *cls,
1269                  const struct GNUNET_PeerIdentity *key,
1270                  void *value)
1271 {
1272   struct HostEntry *he = value;
1273
1274   GNUNET_free_non_null (he->hello);
1275   GNUNET_free_non_null (he->friend_only_hello);
1276   GNUNET_free (he);
1277   return GNUNET_YES;
1278 }
1279
1280
1281 /**
1282  * Clean up our state.  Called during shutdown.
1283  *
1284  * @param cls unused
1285  */
1286 static void
1287 shutdown_task (void *cls)
1288 {
1289   struct NotificationContext *cur;
1290   struct NotificationContext *next;
1291
1292   GNUNET_SERVER_notification_context_destroy (notify_list);
1293   notify_list = NULL;
1294
1295   for (cur = nc_head; NULL != cur; cur = next)
1296   {
1297     next = cur->next;
1298     GNUNET_CONTAINER_DLL_remove (nc_head, nc_tail, cur);
1299     GNUNET_free (cur);
1300   }
1301   GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1302                                          &free_host_entry,
1303                                          NULL);
1304   GNUNET_CONTAINER_multipeermap_destroy (hostmap);
1305   if (NULL != stats)
1306   {
1307     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1308     stats = NULL;
1309   }
1310   if (NULL != cron_clean)
1311   {
1312     GNUNET_SCHEDULER_cancel (cron_clean);
1313     cron_clean = NULL;
1314   }
1315   if (NULL != cron_scan)
1316   {
1317     GNUNET_SCHEDULER_cancel (cron_scan);
1318     cron_scan = NULL;
1319   }
1320 }
1321
1322
1323 /**
1324  * Start up peerinfo service.
1325  *
1326  * @param cls closure
1327  * @param server the initialized server
1328  * @param cfg configuration to use
1329  */
1330 static void
1331 run (void *cls,
1332      struct GNUNET_SERVER_Handle *server,
1333      const struct GNUNET_CONFIGURATION_Handle *cfg)
1334 {
1335   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1336     {&handle_hello, NULL, GNUNET_MESSAGE_TYPE_HELLO, 0},
1337     {&handle_get, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET,
1338      sizeof (struct ListPeerMessage)},
1339     {&handle_get_all, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
1340      sizeof (struct ListAllPeersMessage)},
1341     {&handle_notify, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
1342      sizeof (struct NotifyMessage)},
1343     {NULL, NULL, 0, 0}
1344   };
1345   char *peerdir;
1346   char *ip;
1347   struct DirScanContext dsc;
1348   int noio;
1349   int use_included;
1350
1351   hostmap = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES);
1352   stats = GNUNET_STATISTICS_create ("peerinfo", cfg);
1353   notify_list = GNUNET_SERVER_notification_context_create (server, 0);
1354   noio = GNUNET_CONFIGURATION_get_value_yesno (cfg, "peerinfo", "NO_IO");
1355   use_included = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1356                                                        "peerinfo",
1357                                                        "USE_INCLUDED_HELLOS");
1358   if (GNUNET_SYSERR == use_included)
1359     use_included = GNUNET_NO;
1360   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1361                                  NULL);
1362   if (GNUNET_YES != noio)
1363   {
1364     GNUNET_assert (GNUNET_OK ==
1365                    GNUNET_CONFIGURATION_get_value_filename (cfg, "peerinfo",
1366                                                             "HOSTS",
1367                                                             &networkIdDirectory));
1368     if (GNUNET_OK !=
1369         GNUNET_DISK_directory_create (networkIdDirectory))
1370     {
1371       GNUNET_SCHEDULER_shutdown ();
1372       return;
1373     }
1374
1375     cron_scan = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1376                                                     &cron_scan_directory_data_hosts,
1377                                                     NULL);
1378
1379     cron_clean = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1380                                                      &cron_clean_data_hosts,
1381                                                      NULL);
1382     if (GNUNET_YES == use_included)
1383     {
1384       ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1385       GNUNET_asprintf (&peerdir,
1386                        "%shellos",
1387                        ip);
1388       GNUNET_free(ip);
1389
1390       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1391                   _("Importing HELLOs from `%s'\n"),
1392                   peerdir);
1393       dsc.matched = 0;
1394       dsc.remove_files = GNUNET_NO;
1395
1396       GNUNET_DISK_directory_scan (peerdir,
1397                                   &hosts_directory_scan_callback,
1398                                   &dsc);
1399       GNUNET_free (peerdir);
1400     }
1401     else
1402     {
1403       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1404                  _("Skipping import of included HELLOs\n"));
1405     }
1406   }
1407   GNUNET_SERVER_add_handlers (server, handlers);
1408   GNUNET_SERVER_disconnect_notify (server,
1409                                    &disconnect_cb,
1410                                    NULL);
1411 }
1412
1413
1414 /**
1415  * The main function for the peerinfo service.
1416  *
1417  * @param argc number of arguments from the command line
1418  * @param argv command line arguments
1419  * @return 0 ok, 1 on error
1420  */
1421 int
1422 main (int argc,
1423       char *const *argv)
1424 {
1425   int ret;
1426
1427   ret =
1428       (GNUNET_OK ==
1429        GNUNET_SERVICE_run (argc, argv,
1430                            "peerinfo",
1431                            GNUNET_SERVICE_OPTION_NONE,
1432                            &run, NULL)) ? 0 : 1;
1433   GNUNET_free_non_null (networkIdDirectory);
1434   return ret;
1435 }
1436
1437
1438 /* end of gnunet-service-peerinfo.c */