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