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