fix log call: wait until pid is initialized
[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
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 /**
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 != memcmp (&id_friend,
588                       &id_public,
589                       sizeof (id_friend))) )
590   {
591     /* HELLOs are not for the same peer */
592     GNUNET_break (0);
593     if (GNUNET_YES == dsc->remove_files)
594       remove_garbage (fullname);
595     return GNUNET_OK;
596   }
597   if (GNUNET_OK ==
598       GNUNET_CRYPTO_eddsa_public_key_from_string (filename,
599                                                   strlen (filename),
600                                                   &identity.public_key))
601   {
602     if (0 != memcmp (&id, &identity, sizeof (id_friend)))
603     {
604       /* HELLOs are not for the same peer */
605       GNUNET_break (0);
606       if (GNUNET_YES == dsc->remove_files)
607         remove_garbage (fullname);
608       return GNUNET_OK;
609     }
610   }
611
612   /* ok, found something valid, remember HELLO */
613   add_host_to_known_hosts (&id);
614   if (NULL != r.hello)
615   {
616     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
617                 "Updating peer `%s' public HELLO \n",
618                 GNUNET_i2s (&id));
619     update_hello (&id, r.hello);
620     GNUNET_free (r.hello);
621   }
622   if (NULL != r.friend_only_hello)
623   {
624     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
625                 "Updating peer `%s' friend only HELLO \n",
626                 GNUNET_i2s (&id));
627     update_hello (&id, r.friend_only_hello);
628     GNUNET_free (r.friend_only_hello);
629   }
630   dsc->matched++;
631   return GNUNET_OK;
632 }
633
634
635 /**
636  * Call this method periodically to scan data/hosts for new hosts.
637  *
638  * @param cls unused
639  */
640 static void
641 cron_scan_directory_data_hosts (void *cls)
642 {
643   static unsigned int retries;
644   struct DirScanContext dsc;
645
646   (void) cls;
647   cron_scan = NULL;
648   if (GNUNET_SYSERR ==
649       GNUNET_DISK_directory_create (networkIdDirectory))
650   {
651     cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
652                                                             GNUNET_SCHEDULER_PRIORITY_IDLE,
653                                                             &cron_scan_directory_data_hosts, NULL);
654     return;
655   }
656   dsc.matched = 0;
657   dsc.remove_files = GNUNET_YES;
658   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
659               _("Scanning directory `%s'\n"),
660               networkIdDirectory);
661   GNUNET_DISK_directory_scan (networkIdDirectory,
662                               &hosts_directory_scan_callback,
663                               &dsc);
664   if ( (0 == dsc.matched) &&
665        (0 == (++retries & 31)) )
666     GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
667                 _("Still no peers found in `%s'!\n"),
668                 networkIdDirectory);
669   cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
670                                                           GNUNET_SCHEDULER_PRIORITY_IDLE,
671                                                           &cron_scan_directory_data_hosts,
672                                                           NULL);
673 }
674
675
676 /**
677  * Update the HELLO of a friend by merging the addresses.
678  *
679  * @param hello original hello
680  * @param friend_hello hello with additional addresses
681  * @return merged HELLO
682  */
683 static struct GNUNET_HELLO_Message *
684 update_friend_hello (const struct GNUNET_HELLO_Message *hello,
685                      const struct GNUNET_HELLO_Message *friend_hello)
686 {
687   struct GNUNET_HELLO_Message * res;
688   struct GNUNET_HELLO_Message * tmp;
689   struct GNUNET_PeerIdentity pid;
690
691   if (NULL != friend_hello)
692   {
693     res = GNUNET_HELLO_merge (hello,
694                               friend_hello);
695     GNUNET_assert (GNUNET_YES ==
696                    GNUNET_HELLO_is_friend_only (res));
697     return res;
698   }
699
700   if (GNUNET_OK !=
701       GNUNET_HELLO_get_id (hello, &pid))
702   {
703     GNUNET_break (0);
704     return NULL;
705   }
706   tmp = GNUNET_HELLO_create (&pid.public_key,
707                              NULL,
708                              NULL,
709                              GNUNET_YES);
710   res = GNUNET_HELLO_merge (hello, tmp);
711   GNUNET_free (tmp);
712   GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
713   return res;
714 }
715
716
717 /**
718  * Bind a host address (hello) to a hostId.
719  *
720  * @param peer the peer for which this is a hello
721  * @param hello the verified (!) hello message
722  */
723 static void
724 update_hello (const struct GNUNET_PeerIdentity *peer,
725               const struct GNUNET_HELLO_Message *hello)
726 {
727   char *fn;
728   struct HostEntry *host;
729   struct GNUNET_HELLO_Message *mrg;
730   struct GNUNET_HELLO_Message **dest;
731   struct GNUNET_TIME_Absolute delta;
732   unsigned int cnt;
733   unsigned int size;
734   int friend_hello_type;
735   int store_hello;
736   int store_friend_hello;
737   unsigned int pos;
738   char *buffer;
739
740   host = GNUNET_CONTAINER_multipeermap_get (hostmap, peer);
741   GNUNET_assert (NULL != host);
742
743   friend_hello_type = GNUNET_HELLO_is_friend_only (hello);
744   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745               "Updating %s HELLO for `%s'\n",
746               (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
747               GNUNET_i2s (peer));
748
749   dest = NULL;
750   if (GNUNET_YES == friend_hello_type)
751   {
752     dest = &host->friend_only_hello;
753   }
754   else
755   {
756     dest = &host->hello;
757   }
758
759   if (NULL == (*dest))
760   {
761     (*dest) = GNUNET_malloc (GNUNET_HELLO_size (hello));
762     GNUNET_memcpy ((*dest), hello, GNUNET_HELLO_size (hello));
763   }
764   else
765   {
766     mrg = GNUNET_HELLO_merge ((*dest),
767                               hello);
768     delta = GNUNET_HELLO_equals (mrg,
769                                  (*dest),
770                                  GNUNET_TIME_absolute_get ());
771     if (delta.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
772     {
773       /* no differences, just ignore the update */
774       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
775                   "No change in %s HELLO for `%s'\n",
776                   (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
777                   GNUNET_i2s (peer));
778       GNUNET_free (mrg);
779       return;
780     }
781     GNUNET_free ((*dest));
782     (*dest) = mrg;
783   }
784
785   if ( (NULL != (host->hello)) &&
786        (GNUNET_NO == friend_hello_type) )
787   {
788     /* Update friend only hello */
789     mrg = update_friend_hello (host->hello,
790                                host->friend_only_hello);
791     if (NULL != host->friend_only_hello)
792       GNUNET_free (host->friend_only_hello);
793     host->friend_only_hello = mrg;
794   }
795
796   if (NULL != host->hello)
797     GNUNET_assert ((GNUNET_NO ==
798                     GNUNET_HELLO_is_friend_only (host->hello)));
799   if (NULL != host->friend_only_hello)
800     GNUNET_assert ((GNUNET_YES ==
801                     GNUNET_HELLO_is_friend_only (host->friend_only_hello)));
802
803   fn = get_host_filename (peer);
804   if ( (NULL != fn) &&
805        (GNUNET_OK ==
806         GNUNET_DISK_directory_create_for_file (fn)) )
807   {
808     store_hello = GNUNET_NO;
809     size = 0;
810     cnt = 0;
811     if (NULL != host->hello)
812       (void) GNUNET_HELLO_iterate_addresses (host->hello,
813                                              GNUNET_NO,
814                                              &count_addresses,
815                                              &cnt);
816     if (cnt > 0)
817     {
818       store_hello = GNUNET_YES;
819       size += GNUNET_HELLO_size (host->hello);
820     }
821     cnt = 0;
822     if (NULL != host->friend_only_hello)
823       (void) GNUNET_HELLO_iterate_addresses (host->friend_only_hello,
824                                              GNUNET_NO,
825                                              &count_addresses,
826                                              &cnt);
827     store_friend_hello = GNUNET_NO;
828     if (0 < cnt)
829     {
830       store_friend_hello = GNUNET_YES;
831       size += GNUNET_HELLO_size (host->friend_only_hello);
832     }
833
834     if ( (GNUNET_NO == store_hello) &&
835          (GNUNET_NO == store_friend_hello) )
836     {
837       /* no valid addresses, don't put HELLO on disk; in fact,
838          if one exists on disk, remove it */
839       (void) UNLINK (fn);
840     }
841     else
842     {
843       buffer = GNUNET_malloc (size);
844       pos = 0;
845
846       if (GNUNET_YES == store_hello)
847       {
848         GNUNET_memcpy (buffer,
849                        host->hello,
850                        GNUNET_HELLO_size (host->hello));
851         pos += GNUNET_HELLO_size (host->hello);
852       }
853       if (GNUNET_YES == store_friend_hello)
854       {
855         GNUNET_memcpy (&buffer[pos],
856                        host->friend_only_hello,
857                        GNUNET_HELLO_size (host->friend_only_hello));
858         pos += GNUNET_HELLO_size (host->friend_only_hello);
859       }
860       GNUNET_assert (pos == size);
861
862       if (GNUNET_SYSERR == GNUNET_DISK_fn_write (fn, buffer, size,
863                                                  GNUNET_DISK_PERM_USER_READ |
864                                                  GNUNET_DISK_PERM_USER_WRITE |
865                                                  GNUNET_DISK_PERM_GROUP_READ |
866                                                  GNUNET_DISK_PERM_OTHER_READ))
867         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
868       else
869         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
870                     "Stored %s %s HELLO in %s  with total size %u\n",
871                     (GNUNET_YES == store_friend_hello) ? "friend-only": "",
872                     (GNUNET_YES == store_hello) ? "public": "",
873                     fn,
874                     size);
875       GNUNET_free (buffer);
876     }
877   }
878   GNUNET_free_non_null (fn);
879   notify_all (host);
880 }
881
882
883 /**
884  * Closure for #add_to_tc()
885  */
886 struct TransmitContext
887 {
888   /**
889    * Client to transmit to
890    */
891   struct GNUNET_SERVICE_Client *client;
892
893   /**
894    * Include friend only HELLOs #GNUNET_YES or #GNUNET_NO
895    */
896   int friend_only;
897 };
898
899
900 /**
901  * Do transmit info about peer to given host.
902  *
903  * @param cls NULL to hit all hosts, otherwise specifies a particular target
904  * @param key hostID
905  * @param value information to transmit
906  * @return #GNUNET_YES (continue to iterate)
907  */
908 static int
909 add_to_tc (void *cls,
910            const struct GNUNET_PeerIdentity *key,
911            void *value)
912 {
913   struct TransmitContext *tc = cls;
914   struct HostEntry *pos = value;
915   struct InfoMessage *im;
916   uint16_t hs;
917   struct GNUNET_MQ_Envelope *env;
918
919   hs = 0;
920
921   if ( (NULL != pos->hello) &&
922        (GNUNET_NO == tc->friend_only) )
923   {
924         /* Copy public HELLO */
925     hs = GNUNET_HELLO_size (pos->hello);
926     GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE -
927                    sizeof (struct InfoMessage));
928     env = GNUNET_MQ_msg_extra (im,
929                                hs,
930                                GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
931     GNUNET_memcpy (&im[1],
932                    pos->hello,
933                    hs);
934     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
935                 "Sending public HELLO with size %u for peer `%s'\n",
936                 hs,
937                 GNUNET_i2s (key));
938   }
939   else if ( (NULL != pos->friend_only_hello) &&
940             (GNUNET_YES == tc->friend_only) )
941   {
942         /* Copy friend only HELLO */
943     hs = GNUNET_HELLO_size (pos->friend_only_hello);
944     GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE -
945                    sizeof (struct InfoMessage));
946     env = GNUNET_MQ_msg_extra (im,
947                                hs,
948                                GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
949     GNUNET_memcpy (&im[1],
950                    pos->friend_only_hello,
951                    hs);
952     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
953                 "Sending friend-only HELLO with size %u for peer `%s'\n",
954                 hs,
955                 GNUNET_i2s (key));
956   }
957   else
958   {
959     env = GNUNET_MQ_msg (im,
960                          GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
961     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
962                 "Adding no HELLO for peer `%s'\n",
963                 GNUNET_i2s (key));
964   }
965   im->peer = pos->identity;
966   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (tc->client),
967                   env);
968   return GNUNET_YES;
969 }
970
971
972 /**
973  * @brief delete expired HELLO entries in directory
974  *
975  * @param cls pointer to current time (`struct GNUNET_TIME_Absolute *`)
976  * @param fn filename to test to see if the HELLO expired
977  * @return #GNUNET_OK (continue iteration)
978  */
979 static int
980 discard_hosts_helper (void *cls,
981                       const char *fn)
982 {
983   struct GNUNET_TIME_Absolute *now = cls;
984   char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
985   const struct GNUNET_HELLO_Message *hello;
986   struct GNUNET_HELLO_Message *new_hello;
987   int read_size;
988   unsigned int cur_hello_size;
989   unsigned int new_hello_size;
990   int read_pos;
991   int write_pos;
992   unsigned int cnt;
993   char *writebuffer;
994   uint64_t fsize;
995   
996   if (GNUNET_OK !=
997       GNUNET_DISK_file_size (fn,
998                              &fsize,
999                              GNUNET_YES,
1000                              GNUNET_YES))
1001   {
1002     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1003                               GNUNET_ERROR_TYPE_BULK,
1004                               "fstat",
1005                               fn);
1006     return GNUNET_OK;
1007   }
1008   read_size = GNUNET_DISK_fn_read (fn,
1009                                    buffer,
1010                                    sizeof (buffer));
1011
1012   if ( (read_size < (int) sizeof (struct GNUNET_MessageHeader)) ||
1013        (fsize > GNUNET_MAX_MESSAGE_SIZE) )
1014   {
1015     if (0 != UNLINK (fn))
1016       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1017                                 GNUNET_ERROR_TYPE_BULK,
1018                                 "unlink",
1019                                 fn);
1020     return GNUNET_OK;
1021   }
1022
1023   writebuffer = GNUNET_malloc (read_size);
1024   read_pos = 0;
1025   write_pos = 0;
1026   while (read_pos < read_size)
1027   {
1028     /* Check each HELLO */
1029     hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
1030     cur_hello_size = GNUNET_HELLO_size (hello);
1031     if (0 == cur_hello_size)
1032     {
1033       /* Invalid data, discard */
1034       if (0 != UNLINK (fn))
1035         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1036                                   GNUNET_ERROR_TYPE_BULK,
1037                                   "unlink",
1038                                   fn);
1039       GNUNET_free (writebuffer);
1040       return GNUNET_OK;
1041     }
1042     new_hello = GNUNET_HELLO_iterate_addresses (hello,
1043                                                 GNUNET_YES,
1044                                                 &discard_expired,
1045                                                 now);
1046     cnt = 0;
1047     if (NULL != new_hello)
1048       (void) GNUNET_HELLO_iterate_addresses (hello,
1049                                              GNUNET_NO,
1050                                              &count_addresses,
1051                                              &cnt);
1052     if ( (NULL != new_hello) && (0 < cnt) )
1053     {
1054       /* Store new HELLO to write it when done */
1055       new_hello_size = GNUNET_HELLO_size (new_hello);
1056       GNUNET_memcpy (&writebuffer[write_pos],
1057                      new_hello,
1058                      new_hello_size);
1059       write_pos += new_hello_size;
1060     }
1061     read_pos += cur_hello_size;
1062     GNUNET_free_non_null (new_hello);
1063   }
1064
1065   if (0 < write_pos)
1066   {
1067     GNUNET_DISK_fn_write (fn,
1068                           writebuffer,
1069                           write_pos,
1070                           GNUNET_DISK_PERM_USER_READ |
1071                           GNUNET_DISK_PERM_USER_WRITE |
1072                           GNUNET_DISK_PERM_GROUP_READ |
1073                           GNUNET_DISK_PERM_OTHER_READ);
1074   }
1075   else if (0 != UNLINK (fn))
1076     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1077                               GNUNET_ERROR_TYPE_BULK,
1078                               "unlink", fn);
1079
1080   GNUNET_free (writebuffer);
1081   return GNUNET_OK;
1082 }
1083
1084
1085 /**
1086  * Call this method periodically to scan peerinfo/ for ancient
1087  * HELLOs to expire.
1088  *
1089  * @param cls unused
1090  */
1091 static void
1092 cron_clean_data_hosts (void *cls)
1093 {
1094   struct GNUNET_TIME_Absolute now;
1095
1096   (void) cls;
1097   cron_clean = NULL;
1098   now = GNUNET_TIME_absolute_get ();
1099   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1100               _("Cleaning up directory `%s'\n"),
1101               networkIdDirectory);
1102   GNUNET_DISK_directory_scan (networkIdDirectory,
1103                               &discard_hosts_helper,
1104                               &now);
1105   cron_clean = GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
1106                                              &cron_clean_data_hosts,
1107                                              NULL);
1108 }
1109
1110
1111 /**
1112  * Check HELLO-message.
1113  *
1114  * @param cls identification of the client
1115  * @param hello the actual message
1116  * @return #GNUNET_OK if @a hello is well-formed
1117  */
1118 static int
1119 check_hello (void *cls,
1120              const struct GNUNET_HELLO_Message *hello)
1121 {
1122   struct GNUNET_PeerIdentity pid;
1123
1124   (void) cls;
1125   if (GNUNET_OK !=
1126       GNUNET_HELLO_get_id (hello,
1127                            &pid))
1128   {
1129     GNUNET_break (0);
1130     return GNUNET_SYSERR;
1131   }
1132   return GNUNET_OK;
1133 }
1134
1135
1136 /**
1137  * Handle HELLO-message.
1138  *
1139  * @param cls identification of the client
1140  * @param hello the actual message
1141  */
1142 static void
1143 handle_hello (void *cls,
1144               const struct GNUNET_HELLO_Message *hello)
1145 {
1146   struct GNUNET_SERVICE_Client *client = cls;
1147   struct GNUNET_PeerIdentity pid;
1148
1149   GNUNET_assert (GNUNET_OK ==
1150                  GNUNET_HELLO_get_id (hello,
1151                                       &pid));
1152   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153               "HELLO message received for peer `%s'\n",
1154               GNUNET_i2s (&pid));
1155   add_host_to_known_hosts (&pid);
1156   update_hello (&pid,
1157                 hello);
1158   GNUNET_SERVICE_client_continue (client);
1159 }
1160
1161
1162 /**
1163  * Handle GET-message.
1164  *
1165  * @param cls identification of the client
1166  * @param lpm the actual message
1167  */
1168 static void
1169 handle_get (void *cls,
1170             const struct ListPeerMessage *lpm)
1171 {
1172   struct GNUNET_SERVICE_Client *client = cls;
1173   struct TransmitContext tcx;
1174   struct GNUNET_MessageHeader *msg;
1175   struct GNUNET_MQ_Envelope *env;
1176
1177   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1178               "GET message received for peer `%s'\n",
1179               GNUNET_i2s (&lpm->peer));
1180   tcx.friend_only = ntohl (lpm->include_friend_only);
1181   tcx.client = client;
1182   GNUNET_CONTAINER_multipeermap_get_multiple (hostmap,
1183                                               &lpm->peer,
1184                                               &add_to_tc,
1185                                               &tcx);
1186   env = GNUNET_MQ_msg (msg,
1187                        GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1188   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1189                   env);
1190   GNUNET_SERVICE_client_continue (client);
1191 }
1192
1193
1194 /**
1195  * Handle GET-ALL-message.
1196  *
1197  * @param cls identification of the client
1198  * @param lapm the actual message
1199  */
1200 static void
1201 handle_get_all (void *cls,
1202                 const struct ListAllPeersMessage *lapm)
1203 {
1204   struct GNUNET_SERVICE_Client *client = cls;
1205   struct TransmitContext tcx;
1206   struct GNUNET_MQ_Envelope *env;
1207   struct GNUNET_MessageHeader *msg;
1208
1209   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1210               "GET_ALL message received\n");
1211   tcx.friend_only = ntohl (lapm->include_friend_only);
1212   tcx.client = client;
1213   GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1214                                          &add_to_tc,
1215                                          &tcx);
1216   env = GNUNET_MQ_msg (msg,
1217                        GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1218   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1219                   env);
1220   GNUNET_SERVICE_client_continue (client);
1221 }
1222
1223
1224 /**
1225  * Handle NOTIFY-message.
1226  *
1227  * @param cls identification of the client
1228  * @param nm the actual message
1229  */
1230 static void
1231 handle_notify (void *cls,
1232                const struct NotifyMessage *nm)
1233 {
1234   struct GNUNET_SERVICE_Client *client = cls;
1235   struct GNUNET_MQ_Handle *mq;
1236   struct TransmitContext tcx;
1237   struct GNUNET_MQ_Envelope *env;
1238   struct GNUNET_MessageHeader *msg;
1239
1240   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1241               "NOTIFY message received\n");
1242   mq = GNUNET_SERVICE_client_get_mq (client);
1243   GNUNET_SERVICE_client_mark_monitor (client);
1244   if (ntohl (nm->include_friend_only))
1245     GNUNET_notification_context_add (notify_friend_only_list,
1246                                      mq);
1247   else
1248     GNUNET_notification_context_add (notify_list,
1249                                      mq);
1250   tcx.friend_only = ntohl (nm->include_friend_only);
1251   tcx.client = client;
1252   GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1253                                          &add_to_tc,
1254                                          &tcx);
1255   env = GNUNET_MQ_msg (msg,
1256                        GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1257   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1258                   env);
1259   GNUNET_SERVICE_client_continue (client);
1260 }
1261
1262
1263 /**
1264  * Client connect callback
1265  *
1266  * @param cls unused
1267  * @param client server client
1268  * @param mq for @a client
1269  * @return @a client
1270  */
1271 static void *
1272 client_connect_cb (void *cls,
1273                    struct GNUNET_SERVICE_Client *client,
1274                    struct GNUNET_MQ_Handle *mq)
1275 {
1276   (void) cls;
1277   (void) mq;
1278   return client;
1279 }
1280
1281
1282 /**
1283  * Client disconnect callback
1284  *
1285  * @param cls unused
1286  * @param client server client
1287  * @param app_ctx should be @a client
1288  */
1289 static void
1290 client_disconnect_cb (void *cls,
1291                       struct GNUNET_SERVICE_Client *client,
1292                       void *app_ctx)
1293 {
1294   (void) cls;
1295   GNUNET_assert (app_ctx == client);
1296 }
1297
1298
1299 /**
1300  * Release memory taken by a host entry.
1301  *
1302  * @param cls NULL
1303  * @param key key of the host entry
1304  * @param value the `struct HostEntry` to free
1305  * @return #GNUNET_YES (continue to iterate)
1306  */
1307 static int
1308 free_host_entry (void *cls,
1309                  const struct GNUNET_PeerIdentity *key,
1310                  void *value)
1311 {
1312   struct HostEntry *he = value;
1313
1314   (void) cls;
1315   (void) key;
1316   GNUNET_free_non_null (he->hello);
1317   GNUNET_free_non_null (he->friend_only_hello);
1318   GNUNET_free (he);
1319   return GNUNET_YES;
1320 }
1321
1322
1323 /**
1324  * Clean up our state.  Called during shutdown.
1325  *
1326  * @param cls unused
1327  */
1328 static void
1329 shutdown_task (void *cls)
1330 {
1331   (void) cls;
1332   GNUNET_notification_context_destroy (notify_list);
1333   notify_list = NULL;
1334   GNUNET_notification_context_destroy (notify_friend_only_list);
1335   notify_friend_only_list = NULL;
1336
1337   GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1338                                          &free_host_entry,
1339                                          NULL);
1340   GNUNET_CONTAINER_multipeermap_destroy (hostmap);
1341   if (NULL != stats)
1342   {
1343     GNUNET_STATISTICS_destroy (stats,
1344                                GNUNET_NO);
1345     stats = NULL;
1346   }
1347   if (NULL != cron_clean)
1348   {
1349     GNUNET_SCHEDULER_cancel (cron_clean);
1350     cron_clean = NULL;
1351   }
1352   if (NULL != cron_scan)
1353   {
1354     GNUNET_SCHEDULER_cancel (cron_scan);
1355     cron_scan = NULL;
1356   }
1357   if (NULL != networkIdDirectory)
1358   {
1359     GNUNET_free (networkIdDirectory);
1360     networkIdDirectory = NULL;
1361   }
1362 }
1363
1364
1365 /**
1366  * Start up peerinfo service.
1367  *
1368  * @param cls closure
1369  * @param cfg configuration to use
1370  * @param service the initialized service
1371  */
1372 static void
1373 run (void *cls,
1374      const struct GNUNET_CONFIGURATION_Handle *cfg,
1375      struct GNUNET_SERVICE_Handle *service)
1376 {
1377   char *peerdir;
1378   char *ip;
1379   struct DirScanContext dsc;
1380   int noio;
1381   int use_included;
1382
1383   (void) cls;
1384   (void) service;
1385   hostmap
1386     = GNUNET_CONTAINER_multipeermap_create (1024,
1387                                             GNUNET_YES);
1388   stats
1389     = GNUNET_STATISTICS_create ("peerinfo",
1390                                 cfg);
1391   notify_list
1392     = GNUNET_notification_context_create (0);
1393   notify_friend_only_list
1394     = GNUNET_notification_context_create (0);
1395   noio = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1396                                                "peerinfo",
1397                                                "NO_IO");
1398   use_included
1399     = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1400                                             "peerinfo",
1401                                             "USE_INCLUDED_HELLOS");
1402   if (GNUNET_SYSERR == use_included)
1403     use_included = GNUNET_NO;
1404   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1405                                  NULL);
1406   if (GNUNET_YES != noio)
1407   {
1408     GNUNET_assert (GNUNET_OK ==
1409                    GNUNET_CONFIGURATION_get_value_filename (cfg,
1410                                                             "peerinfo",
1411                                                             "HOSTS",
1412                                                             &networkIdDirectory));
1413     if (GNUNET_OK !=
1414         GNUNET_DISK_directory_create (networkIdDirectory))
1415     {
1416       GNUNET_SCHEDULER_shutdown ();
1417       return;
1418     }
1419
1420     cron_scan
1421       = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1422                                             &cron_scan_directory_data_hosts,
1423                                             NULL);
1424
1425     cron_clean
1426       = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1427                                             &cron_clean_data_hosts,
1428                                             NULL);
1429     if (GNUNET_YES == use_included)
1430     {
1431       ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1432       GNUNET_asprintf (&peerdir,
1433                        "%shellos",
1434                        ip);
1435       GNUNET_free (ip);
1436
1437       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1438                   _("Importing HELLOs from `%s'\n"),
1439                   peerdir);
1440       dsc.matched = 0;
1441       dsc.remove_files = GNUNET_NO;
1442
1443       GNUNET_DISK_directory_scan (peerdir,
1444                                   &hosts_directory_scan_callback,
1445                                   &dsc);
1446       GNUNET_free (peerdir);
1447     }
1448     else
1449     {
1450       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1451                   _("Skipping import of included HELLOs\n"));
1452     }
1453   }
1454 }
1455
1456
1457 /**
1458  * Define "main" method using service macro.
1459  */
1460 GNUNET_SERVICE_MAIN
1461 ("peerinfo",
1462  GNUNET_SERVICE_OPTION_NONE,
1463  &run,
1464  &client_connect_cb,
1465  &client_disconnect_cb,
1466  NULL,
1467  GNUNET_MQ_hd_var_size (hello,
1468                         GNUNET_MESSAGE_TYPE_HELLO,
1469                         struct GNUNET_HELLO_Message,
1470                         NULL),
1471  GNUNET_MQ_hd_fixed_size (get,
1472                           GNUNET_MESSAGE_TYPE_PEERINFO_GET,
1473                           struct ListPeerMessage,
1474                           NULL),
1475  GNUNET_MQ_hd_fixed_size (get_all,
1476                           GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
1477                           struct ListAllPeersMessage,
1478                           NULL),
1479  GNUNET_MQ_hd_fixed_size (notify,
1480                           GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
1481                           struct NotifyMessage,
1482                           NULL),
1483  GNUNET_MQ_handler_end ());
1484
1485
1486 /* end of gnunet-service-peerinfo.c */