convert fs publish to MQ
[oweals/gnunet.git] / src / peerinfo-tool / gnunet-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-2014 GNUnet e.V.
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file peerinfo-tool/gnunet-peerinfo.c
23  * @brief Print information about other known peers.
24  * @author Christian Grothoff
25  * @author Matthias Wachs
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_hello_lib.h"
30 #include "gnunet_transport_service.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet-peerinfo_plugins.h"
33
34 /**
35  * How long until we time out during address lookup?
36  */
37 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
38
39 /**
40  * Structure we use to collect printable address information.
41  */
42 struct PrintContext;
43
44 /**
45  * Record we keep for each printable address.
46  */
47 struct AddressRecord
48 {
49   /**
50    * Current address-to-string context (if active, otherwise NULL).
51    */
52   struct GNUNET_TRANSPORT_AddressToStringContext *atsc;
53
54   /**
55    * Address expiration time
56    */
57   struct GNUNET_TIME_Absolute expiration;
58
59   /**
60    * Printable address.
61    */
62   char *result;
63
64   /**
65    * Print context this address record belongs to.
66    */
67   struct PrintContext *pc;
68 };
69
70
71 /**
72  * Structure we use to collect printable address information.
73  */
74 struct PrintContext
75 {
76
77   /**
78    * Kept in DLL.
79    */
80   struct PrintContext *next;
81
82   /**
83    * Kept in DLL.
84    */
85   struct PrintContext *prev;
86
87   /**
88    * Identity of the peer.
89    */
90   struct GNUNET_PeerIdentity peer;
91
92   /**
93    * List of printable addresses.
94    */
95   struct AddressRecord *address_list;
96
97   /**
98    * Number of completed addresses in @e address_list.
99    */
100   unsigned int num_addresses;
101
102   /**
103    * Number of addresses allocated in @e address_list.
104    */
105   unsigned int address_list_size;
106
107   /**
108    * Current offset in @e address_list (counted down).
109    */
110   unsigned int off;
111
112   /**
113    * Hello was friend only, #GNUNET_YES or #GNUNET_NO
114    */
115   int friend_only;
116
117 };
118
119
120 /**
121  * Option '-n'
122  */
123 static int no_resolve;
124
125 /**
126  * Option '-q'
127  */
128 static int be_quiet;
129
130 /**
131  * Option '-f'
132  */
133 static int include_friend_only;
134
135 /**
136  * Option '-s'
137  */
138 static int get_self;
139
140 /**
141  * Option
142  */
143 static int get_uri;
144
145 /**
146  * Option
147  */
148 static int default_operation;
149
150 /**
151  * Option '-i'
152  */
153 static int get_info;
154
155 /**
156  * Option
157  */
158 static char *put_uri;
159
160 /**
161  * Option -d
162  */
163 static char *dump_hello;
164
165 /**
166  * Handle to peerinfo service.
167  */
168 static struct GNUNET_PEERINFO_Handle *peerinfo;
169
170 /**
171  * Configuration handle.
172  */
173 static const struct GNUNET_CONFIGURATION_Handle *cfg;
174
175 /**
176  * Main state machine task (if active).
177  */
178 static struct GNUNET_SCHEDULER_Task * tt;
179
180 /**
181  * Pending #GNUNET_TRANSPORT_get_hello() operation.
182  */
183 static struct GNUNET_TRANSPORT_GetHelloHandle *gh;
184
185 /**
186  * Connection to transport service.
187  */
188 static struct GNUNET_TRANSPORT_Handle *transport;
189
190 /**
191  * Current iterator context (if active, otherwise NULL).
192  */
193 static struct GNUNET_PEERINFO_IteratorContext *pic;
194
195 /**
196  * My peer identity.
197  */
198 static struct GNUNET_PeerIdentity my_peer_identity;
199
200 /**
201  * Head of list of print contexts.
202  */
203 static struct PrintContext *pc_head;
204
205 /**
206  * Tail of list of print contexts.
207  */
208 static struct PrintContext *pc_tail;
209
210 /**
211  * Handle to current #GNUNET_PEERINFO_add_peer() operation.
212  */
213 static struct GNUNET_MQ_Envelope *ac;
214
215 /**
216  * Hello of this peer (if initialized).
217  */
218 static struct GNUNET_HELLO_Message *my_hello;
219
220
221 /**
222  * Main state machine that goes over all options and
223  * runs the next requested function.
224  *
225  * @param cls unused
226  */
227 static void
228 state_machine (void *cls);
229
230
231 /* ********************* 'get_info' ******************* */
232
233 /**
234  * Print the collected address information to the console and free @a pc.
235  *
236  * @param pc printing context
237  */
238 static void
239 dump_pc (struct PrintContext *pc)
240 {
241   unsigned int i;
242
243   printf (_("%sPeer `%s'\n"),
244           (GNUNET_YES == pc->friend_only) ? "F2F: " : "",
245           GNUNET_i2s_full (&pc->peer));
246   for (i = 0; i < pc->num_addresses; i++)
247   {
248     if (NULL != pc->address_list[i].result)
249     {
250       printf (_("\tExpires: %s \t %s\n"),
251               GNUNET_STRINGS_absolute_time_to_string (pc->address_list[i].expiration),
252               pc->address_list[i].result);
253       GNUNET_free (pc->address_list[i].result);
254     }
255   }
256   printf ("\n");
257   GNUNET_free_non_null (pc->address_list);
258   GNUNET_CONTAINER_DLL_remove (pc_head,
259                                pc_tail,
260                                pc);
261   GNUNET_free (pc);
262   if ( (NULL == pc_head) &&
263        (NULL == pic) )
264     tt = GNUNET_SCHEDULER_add_now (&state_machine,
265                                    NULL);
266 }
267
268
269 /* ************************* list all known addresses **************** */
270
271
272 /**
273  * Function to call with a human-readable format of an address
274  *
275  * @param cls closure
276  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
277  * @param res result of the address to string conversion:
278  *        if #GNUNET_OK: address was valid (conversion to
279  *                       string might still have failed)
280  *        if #GNUNET_SYSERR: address is invalid
281  */
282 static void
283 process_resolved_address (void *cls,
284                           const char *address,
285                           int res)
286 {
287   struct AddressRecord *ar = cls;
288   struct PrintContext *pc = ar->pc;
289
290   if (NULL != address)
291   {
292     if (0 != strlen (address))
293     {
294       if (NULL != ar->result)
295         GNUNET_free (ar->result);
296       ar->result = GNUNET_strdup (address);
297     }
298     return;
299   }
300   ar->atsc = NULL;
301   if (GNUNET_SYSERR == res)
302     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
303                 _("Failure: Cannot convert address to string for peer `%s'\n"),
304                 GNUNET_i2s (&ar->pc->peer));
305   pc->num_addresses++;
306   if (pc->num_addresses == pc->address_list_size)
307     dump_pc (pc);
308 }
309
310
311 /**
312  * Iterator callback to go over all addresses and count them.
313  *
314  * @param cls `struct PrintContext *` with `off` to increment
315  * @param address the address
316  * @param expiration expiration time
317  * @return #GNUNET_OK to keep the address and continue
318  */
319 static int
320 count_address (void *cls,
321                const struct GNUNET_HELLO_Address *address,
322                struct GNUNET_TIME_Absolute expiration)
323 {
324   struct PrintContext *pc = cls;
325
326   pc->off++;
327   return GNUNET_OK;
328 }
329
330
331 /**
332  * Iterator callback to go over all addresses.
333  *
334  * @param cls closure
335  * @param address the address
336  * @param expiration expiration time
337  * @return #GNUNET_OK to keep the address and continue
338  */
339 static int
340 print_address (void *cls,
341                const struct GNUNET_HELLO_Address *address,
342                struct GNUNET_TIME_Absolute expiration)
343 {
344   struct PrintContext *pc = cls;
345   struct AddressRecord *ar;
346
347   GNUNET_assert (0 < pc->off);
348   ar = &pc->address_list[--pc->off];
349   ar->pc = pc;
350   ar->expiration = expiration;
351   GNUNET_asprintf (&ar->result,
352                    "%s:%u:%u",
353                    address->transport_name,
354                    address->address_length,
355                    address->local_info);
356   ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg,
357                                                  address,
358                                                  no_resolve,
359                                                  TIMEOUT,
360                                                  &process_resolved_address, ar);
361   return GNUNET_OK;
362 }
363
364
365 /**
366  * Print information about the peer.  Currently prints the `struct
367  * GNUNET_PeerIdentity` and the transport address.
368  *
369  * @param cls the `struct PrintContext *`
370  * @param peer identity of the peer
371  * @param hello addresses of the peer
372  * @param err_msg error message
373  */
374 static void
375 print_peer_info (void *cls,
376                  const struct GNUNET_PeerIdentity *peer,
377                  const struct GNUNET_HELLO_Message *hello,
378                  const char *err_msg)
379 {
380   struct PrintContext *pc;
381   int friend_only;
382
383   if (NULL == peer)
384   {
385     pic = NULL; /* end of iteration */
386     if (NULL != err_msg)
387     {
388       FPRINTF (stderr,
389                _("Error in communication with PEERINFO service: %s\n"),
390                err_msg);
391     }
392     if (NULL == pc_head)
393       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
394     return;
395   }
396   friend_only = GNUNET_NO;
397   if (NULL != hello)
398         friend_only = GNUNET_HELLO_is_friend_only (hello);
399   if ( (GNUNET_YES == be_quiet) ||
400        (NULL == hello) )
401   {
402     printf ("%s%s\n",
403             (GNUNET_YES == friend_only) ? "F2F: " : "",
404             GNUNET_i2s_full (peer));
405     return;
406   }
407   pc = GNUNET_new (struct PrintContext);
408   GNUNET_CONTAINER_DLL_insert (pc_head,
409                                pc_tail,
410                                pc);
411   pc->peer = *peer;
412   pc->friend_only = friend_only;
413   GNUNET_HELLO_iterate_addresses (hello,
414                                   GNUNET_NO,
415                                   &count_address,
416                                   pc);
417   if (0 == pc->off)
418   {
419     dump_pc (pc);
420     return;
421   }
422   pc->address_list_size = pc->off;
423   pc->address_list = GNUNET_malloc (sizeof (struct AddressRecord) * pc->off);
424   GNUNET_HELLO_iterate_addresses (hello,
425                                   GNUNET_NO,
426                                   &print_address,
427                                   pc);
428 }
429
430 /* ************************* DUMP Hello  ************************** */
431
432 /**
433  * Count the number of addresses in the HELLO.
434  *
435  * @param cls pointer to an `int *` used for the counter
436  * @param address an address to count
437  * @param expiration (unused)
438  * @return #GNUNET_OK
439  */
440 static int
441 count_addr (void *cls,
442             const struct GNUNET_HELLO_Address *address,
443             struct GNUNET_TIME_Absolute expiration)
444 {
445   int *c = cls;
446
447   (*c) ++;
448   return GNUNET_OK;
449 }
450
451
452 /**
453  * Write HELLO of my peer to a file.
454  *
455  * @param cls the `struct GetUriContext *`
456  * @param peer identity of the peer (unused)
457  * @param hello addresses of the peer
458  * @param err_msg error message
459  */
460 static void
461 dump_my_hello ()
462 {
463   unsigned int size;
464   unsigned int c_addr;
465
466   size = GNUNET_HELLO_size (my_hello);
467   if (0 == size)
468   {
469     FPRINTF (stderr,
470              _("Failure: Received invalid %s\n"),
471              "HELLO");
472     return;
473   }
474   if (GNUNET_SYSERR ==
475       GNUNET_DISK_fn_write (dump_hello,
476                             my_hello,
477                             size,
478                             GNUNET_DISK_PERM_USER_READ |
479                             GNUNET_DISK_PERM_USER_WRITE |
480                             GNUNET_DISK_PERM_GROUP_READ |
481                             GNUNET_DISK_PERM_OTHER_READ))
482   {
483     FPRINTF (stderr,
484              _("Failed to write HELLO with %u bytes to file `%s'\n"),
485              size,
486              dump_hello);
487     if (0 != UNLINK (dump_hello))
488       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
489                                 GNUNET_ERROR_TYPE_BULK,
490                                 "unlink",
491                                 dump_hello);
492
493   }
494   c_addr = 0;
495   GNUNET_HELLO_iterate_addresses (my_hello,
496                                   GNUNET_NO,
497                                   count_addr,
498                                   &c_addr);
499
500   if (! be_quiet)
501   {
502     FPRINTF (stderr,
503              _("Wrote %s HELLO containing %u addresses with %u bytes to file `%s'\n"),
504              (GNUNET_YES == GNUNET_HELLO_is_friend_only (my_hello)) ? "friend-only": "public",
505              c_addr,
506              size,
507              dump_hello);
508   }
509   GNUNET_free (dump_hello);
510   dump_hello = NULL;
511 }
512
513
514 /* ************************* GET URI ************************** */
515
516
517 /**
518  * Print URI of the peer.
519  *
520  * @param cls the `struct GetUriContext *`
521  * @param peer identity of the peer (unused)
522  * @param hello addresses of the peer
523  * @param err_msg error message
524  */
525 static void
526 print_my_uri (void *cls,
527               const struct GNUNET_PeerIdentity *peer,
528               const struct GNUNET_HELLO_Message *hello,
529               const char *err_msg)
530 {
531   char *uri;
532
533   if (NULL == peer)
534   {
535     pic = NULL;
536     if (NULL != err_msg)
537       FPRINTF (stderr,
538                _("Error in communication with PEERINFO service: %s\n"),
539                err_msg);
540     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
541     return;
542   }
543
544   if (NULL == hello)
545     return;
546   uri = GNUNET_HELLO_compose_uri (hello,
547                                   &GPI_plugins_find);
548   if (NULL != uri)
549   {
550     printf ("%s\n",
551             (const char *) uri);
552     GNUNET_free (uri);
553   }
554 }
555
556
557 /* ************************* import HELLO by URI ********************* */
558
559
560 /**
561  * Continuation called from #GNUNET_PEERINFO_add_peer()
562  *
563  * @param cls closure, NULL
564  */
565 static void
566 add_continuation (void *cls)
567 {
568   ac = NULL;
569   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
570 }
571
572
573 /**
574  * Parse the PUT URI given at the command line and add it to our peerinfo
575  * database.
576  *
577  * @param put_uri URI string to parse
578  * @return #GNUNET_OK on success,
579  *         #GNUNET_SYSERR if the URI was invalid,
580  *         #GNUNET_NO on other errors
581  */
582 static int
583 parse_hello_uri (const char *put_uri)
584 {
585   struct GNUNET_HELLO_Message *hello = NULL;
586
587   int ret = GNUNET_HELLO_parse_uri (put_uri,
588                                     &my_peer_identity.public_key,
589                                     &hello,
590                                     &GPI_plugins_find);
591
592   if (NULL != hello)
593   {
594     /* WARNING: this adds the address from URI WITHOUT verification! */
595     if (GNUNET_OK == ret)
596       ac = GNUNET_PEERINFO_add_peer (peerinfo,
597                                      hello,
598                                      &add_continuation,
599                                      NULL);
600     else
601       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
602     GNUNET_free (hello);
603   }
604   return ret;
605 }
606
607
608 /* ************************ Main state machine ********************* */
609
610
611 /**
612  * Main state machine that goes over all options and
613  * runs the next requested function.
614  *
615  * @param cls unused
616  */
617 static void
618 shutdown_task (void *cls)
619 {
620   struct PrintContext *pc;
621   struct AddressRecord *ar;
622   unsigned int i;
623
624   if (NULL != ac)
625   {
626     GNUNET_MQ_send_cancel (ac);
627     ac = NULL;
628   }
629   if (NULL != tt)
630   {
631     GNUNET_SCHEDULER_cancel (tt);
632     tt = NULL;
633   }
634   if (NULL != pic)
635   {
636     GNUNET_PEERINFO_iterate_cancel (pic);
637     pic = NULL;
638   }
639   if (NULL != gh)
640   {
641     GNUNET_TRANSPORT_get_hello_cancel (gh);
642     gh = NULL;
643   }
644   if (NULL != transport)
645   {
646     GNUNET_TRANSPORT_disconnect (transport);
647     transport = NULL;
648   }
649   while (NULL != (pc = pc_head))
650   {
651     GNUNET_CONTAINER_DLL_remove (pc_head,
652                                  pc_tail,
653                                  pc);
654     for (i=0;i<pc->address_list_size;i++)
655     {
656       ar = &pc->address_list[i];
657       GNUNET_free_non_null (ar->result);
658       if (NULL != ar->atsc)
659       {
660         GNUNET_TRANSPORT_address_to_string_cancel (ar->atsc);
661         ar->atsc = NULL;
662       }
663     }
664     GNUNET_free_non_null (pc->address_list);
665     GNUNET_free (pc);
666   }
667   GPI_plugins_unload ();
668   if (NULL != peerinfo)
669   {
670     GNUNET_PEERINFO_disconnect (peerinfo);
671     peerinfo = NULL;
672   }
673   if (NULL != my_hello)
674   {
675     GNUNET_free (my_hello);
676     my_hello = NULL;
677   }
678 }
679
680
681 /**
682  * Function called with our peer's HELLO message.
683  * Used to obtain our peer's public key.
684  *
685  * @param cls NULL
686  * @param hello the HELLO message
687  */
688 static void
689 hello_callback (void *cls,
690                 const struct GNUNET_MessageHeader *hello)
691 {
692   if (NULL == hello)
693   {
694     fprintf (stderr,
695              _("Failed to get my own HELLO from this peer!\n"));
696     GNUNET_SCHEDULER_shutdown ();
697     return;
698   }
699   my_hello = (struct GNUNET_HELLO_Message *) GNUNET_copy_message (hello);
700   GNUNET_assert (GNUNET_OK ==
701                  GNUNET_HELLO_get_id (my_hello,
702                                       &my_peer_identity));
703   GNUNET_TRANSPORT_get_hello_cancel (gh);
704   gh = NULL;
705   GNUNET_TRANSPORT_disconnect (transport);
706   transport = NULL;
707   if (NULL != dump_hello)
708     dump_my_hello ();
709   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
710 }
711
712
713 /**
714  * Function called with the result of the check if the PEERINFO
715  * service is running.
716  *
717  * @param cls closure with our configuration
718  * @param result #GNUNET_YES if PEERINFO is running
719  */
720 static void
721 testservice_task (void *cls,
722                   int result)
723 {
724   if (GNUNET_YES != result)
725   {
726     FPRINTF (stderr,
727              _("Service `%s' is not running, please start GNUnet\n"),
728              "peerinfo");
729     return;
730   }
731
732   if (NULL == (peerinfo = GNUNET_PEERINFO_connect (cfg)))
733   {
734     FPRINTF (stderr,
735              "%s",
736              _("Could not access PEERINFO service.  Exiting.\n"));
737     return;
738   }
739   if ( (GNUNET_YES == get_self) ||
740        (GNUNET_YES == get_uri) ||
741        (NULL != dump_hello) )
742   {
743     transport = GNUNET_TRANSPORT_connect (cfg,
744                                           NULL,
745                                           NULL,
746                                           NULL, NULL, NULL);
747     gh = GNUNET_TRANSPORT_get_hello (transport,
748                                      &hello_callback,
749                                      NULL);
750   }
751   else
752   {
753     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
754   }
755   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
756                                  NULL);
757 }
758
759
760 /**
761  * Main function that will be run by the scheduler.
762  *
763  * @param cls closure
764  * @param args remaining command-line arguments
765  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
766  * @param c configuration
767  */
768 static void
769 run (void *cls,
770      char *const *args,
771      const char *cfgfile,
772      const struct GNUNET_CONFIGURATION_Handle *c)
773 {
774   cfg = c;
775   if ( (NULL != args[0]) &&
776        (NULL == put_uri) &&
777        (args[0] == strcasestr (args[0], "gnunet://hello/")) )
778   {
779     put_uri = GNUNET_strdup (args[0]);
780     args++;
781   }
782   if (NULL != args[0])
783   {
784     FPRINTF (stderr,
785              _("Invalid command line argument `%s'\n"),
786              args[0]);
787     return;
788   }
789
790   GNUNET_CLIENT_service_test ("peerinfo",
791                               cfg,
792                               GNUNET_TIME_UNIT_SECONDS,
793                               &testservice_task, (void *) cfg);
794 }
795
796
797 /**
798  * Main state machine that goes over all options and
799  * runs the next requested function.
800  *
801  * @param cls unused
802  */
803 static void
804 state_machine (void *cls)
805 {
806   tt = NULL;
807
808   if (NULL != put_uri)
809   {
810     GPI_plugins_load (cfg);
811     if (GNUNET_SYSERR == parse_hello_uri (put_uri))
812     {
813       fprintf (stderr,
814                _("Invalid URI `%s'\n"),
815                put_uri);
816       GNUNET_SCHEDULER_shutdown ();
817     }
818     GNUNET_free (put_uri);
819     put_uri = NULL;
820   }
821   else if (GNUNET_YES == get_info)
822   {
823     get_info = GNUNET_NO;
824     GPI_plugins_load (cfg);
825     pic = GNUNET_PEERINFO_iterate (peerinfo,
826                                    include_friend_only,
827                                    NULL,
828                                    &print_peer_info, NULL);
829   }
830   else if (GNUNET_YES == get_self)
831   {
832     get_self = GNUNET_NO;
833     if (be_quiet)
834       printf ("%s\n",
835               GNUNET_i2s_full (&my_peer_identity));
836     else
837       printf (_("I am peer `%s'.\n"),
838               GNUNET_i2s_full (&my_peer_identity));
839     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
840   }
841   else if (GNUNET_YES == get_uri)
842   {
843     GPI_plugins_load (cfg);
844     pic = GNUNET_PEERINFO_iterate (peerinfo,
845                                    include_friend_only,
846                                    &my_peer_identity,
847                                    &print_my_uri, NULL);
848     get_uri = GNUNET_NO;
849   }
850   else if (GNUNET_YES == default_operation)
851   {
852     /* default operation list all */
853     default_operation = GNUNET_NO;
854     get_info = GNUNET_YES;
855     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
856   }
857   else
858   {
859     GNUNET_SCHEDULER_shutdown ();
860   }
861   default_operation = GNUNET_NO;
862 }
863
864
865 /**
866  * The main function to obtain peer information.
867  *
868  * @param argc number of arguments from the command line
869  * @param argv command line arguments
870  * @return 0 ok, 1 on error
871  */
872 int
873 main (int argc, char *const *argv)
874 {
875   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
876     {'n', "numeric", NULL,
877      gettext_noop ("don't resolve host names"),
878      0, &GNUNET_GETOPT_set_one, &no_resolve},
879     {'q', "quiet", NULL,
880      gettext_noop ("output only the identity strings"),
881      0, &GNUNET_GETOPT_set_one, &be_quiet},
882     {'f', "friends", NULL,
883      gettext_noop ("include friend-only information"),
884      0, &GNUNET_GETOPT_set_one, &include_friend_only},
885     {'s', "self", NULL,
886      gettext_noop ("output our own identity only"),
887      0, &GNUNET_GETOPT_set_one, &get_self},
888     {'i', "info", NULL,
889      gettext_noop ("list all known peers"),
890      0, &GNUNET_GETOPT_set_one, &get_info},
891     {'d', "dump-hello", NULL,
892      gettext_noop ("dump hello to file"),
893      1, &GNUNET_GETOPT_set_string, &dump_hello},
894     {'g', "get-hello", NULL,
895      gettext_noop ("also output HELLO uri(s)"),
896      0, &GNUNET_GETOPT_set_one, &get_uri},
897     {'p', "put-hello", "HELLO",
898      gettext_noop ("add given HELLO uri to the database"),
899      1, &GNUNET_GETOPT_set_string, &put_uri},
900     GNUNET_GETOPT_OPTION_END
901   };
902   int ret;
903
904   default_operation = GNUNET_YES;
905   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc,
906                                                  argv,
907                                                  &argc, &argv))
908     return 2;
909
910   ret = (GNUNET_OK ==
911          GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
912                              gettext_noop ("Print information about peers."),
913                              options, &run, NULL)) ? 0 : 1;
914   GNUNET_free ((void*) argv);
915   return ret;
916 }
917
918 /* end of gnunet-peerinfo.c */