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