- update default values, eliminate obsolete ones
[oweals/gnunet.git] / src / peerinfo-tool / gnunet-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-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 peerinfo iterations?
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 GNUNET_SCHEDULER_TaskIdentifier tt;
179
180 /**
181  * Current iterator context (if active, otherwise NULL).
182  */
183 static struct GNUNET_PEERINFO_IteratorContext *pic;
184
185 /**
186  * My peer identity.
187  */
188 static struct GNUNET_PeerIdentity my_peer_identity;
189
190 /**
191  * Head of list of print contexts.
192  */
193 static struct PrintContext *pc_head;
194
195 /**
196  * Tail of list of print contexts.
197  */
198 static struct PrintContext *pc_tail;
199
200 /**
201  * Handle to current #GNUNET_PEERINFO_add_peer() operation.
202  */
203 static struct GNUNET_PEERINFO_AddContext *ac;
204
205
206 /**
207  * Main state machine that goes over all options and
208  * runs the next requested function.
209  *
210  * @param cls unused
211  * @param tc unused
212  */
213 static void
214 state_machine (void *cls,
215                const struct GNUNET_SCHEDULER_TaskContext *tc);
216
217
218 /* ********************* 'get_info' ******************* */
219
220 /**
221  * Print the collected address information to the console and free @a pc.
222  *
223  * @param pc printing context
224  */
225 static void
226 dump_pc (struct PrintContext *pc)
227 {
228   unsigned int i;
229
230   printf (_("%sPeer `%s'\n"),
231           (GNUNET_YES == pc->friend_only) ? "F2F: " : "",
232           GNUNET_i2s_full (&pc->peer));
233   for (i = 0; i < pc->num_addresses; i++)
234   {
235     if (NULL != pc->address_list[i].result)
236     {
237       printf (_("\tExpires: %s \t %s\n"),
238               GNUNET_STRINGS_absolute_time_to_string (pc->address_list[i].expiration),
239               pc->address_list[i].result);
240       GNUNET_free (pc->address_list[i].result);
241     }
242   }
243   printf ("\n");
244   GNUNET_free_non_null (pc->address_list);
245   GNUNET_CONTAINER_DLL_remove (pc_head,
246                                pc_tail,
247                                pc);
248   GNUNET_free (pc);
249   if ( (NULL == pc_head) &&
250        (NULL == pic) )
251     tt = GNUNET_SCHEDULER_add_now (&state_machine,
252                                    NULL);
253 }
254
255
256 /* ************************* list all known addresses **************** */
257
258
259 /**
260  * Function to call with a human-readable format of an address
261  *
262  * @param cls closure
263  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
264  * @param res result of the address to string conversion:
265  *        if #GNUNET_OK: address was valid (conversion to
266  *                       string might still have failed)
267  *        if #GNUNET_SYSERR: address is invalid
268  */
269 static void
270 process_resolved_address (void *cls,
271                           const char *address,
272                           int res)
273 {
274   struct AddressRecord *ar = cls;
275   struct PrintContext *pc = ar->pc;
276
277   if (NULL != address)
278   {
279     if (NULL == ar->result)
280       ar->result = GNUNET_strdup (address);
281     return;
282   }
283   ar->atsc = NULL;
284   if (GNUNET_SYSERR == res)
285     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
286              _("Failure: Cannot convert address to string for peer `%s'\n"),
287              GNUNET_i2s (&ar->pc->peer));
288   pc->num_addresses++;
289   if (pc->num_addresses == pc->address_list_size)
290     dump_pc (pc);
291 }
292
293
294 /**
295  * Iterator callback to go over all addresses and count them.
296  *
297  * @param cls `struct PrintContext *` with `off` to increment
298  * @param address the address
299  * @param expiration expiration time
300  * @return #GNUNET_OK to keep the address and continue
301  */
302 static int
303 count_address (void *cls,
304                const struct GNUNET_HELLO_Address *address,
305                struct GNUNET_TIME_Absolute expiration)
306 {
307   struct PrintContext *pc = cls;
308
309   pc->off++;
310   return GNUNET_OK;
311 }
312
313
314 /**
315  * Iterator callback to go over all addresses.
316  *
317  * @param cls closure
318  * @param address the address
319  * @param expiration expiration time
320  * @return #GNUNET_OK to keep the address and continue
321  */
322 static int
323 print_address (void *cls,
324                const struct GNUNET_HELLO_Address *address,
325                struct GNUNET_TIME_Absolute expiration)
326 {
327   struct PrintContext *pc = cls;
328   struct AddressRecord *ar;
329
330   GNUNET_assert (0 < pc->off);
331   ar = &pc->address_list[--pc->off];
332   ar->pc = pc;
333   ar->expiration = expiration;
334   ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg,
335                                                  address,
336                                                  no_resolve,
337                                                  TIMEOUT,
338                                                  &process_resolved_address, ar);
339   return GNUNET_OK;
340 }
341
342
343 /**
344  * Print information about the peer.  Currently prints the `struct
345  * GNUNET_PeerIdentity` and the transport address.
346  *
347  * @param cls the `struct PrintContext *`
348  * @param peer identity of the peer
349  * @param hello addresses of the peer
350  * @param err_msg error message
351  */
352 static void
353 print_peer_info (void *cls,
354                  const struct GNUNET_PeerIdentity *peer,
355                  const struct GNUNET_HELLO_Message *hello,
356                  const char *err_msg)
357 {
358   struct PrintContext *pc;
359   int friend_only;
360
361   if (NULL == peer)
362   {
363     pic = NULL; /* end of iteration */
364     if (NULL != err_msg)
365     {
366       FPRINTF (stderr,
367                _("Error in communication with PEERINFO service: %s\n"),
368                err_msg);
369     }
370     if (NULL == pc_head)
371       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
372     return;
373   }
374   friend_only = GNUNET_NO;
375   if (NULL != hello)
376         friend_only = GNUNET_HELLO_is_friend_only (hello);
377   if ( (GNUNET_YES == be_quiet) ||
378        (NULL == hello) )
379   {
380     printf ("%s%s\n",
381             (GNUNET_YES == friend_only) ? "F2F: " : "",
382             GNUNET_i2s_full (peer));
383     return;
384   }
385   pc = GNUNET_new (struct PrintContext);
386   GNUNET_CONTAINER_DLL_insert (pc_head,
387                                pc_tail,
388                                pc);
389   pc->peer = *peer;
390   pc->friend_only = friend_only;
391   GNUNET_HELLO_iterate_addresses (hello,
392                                   GNUNET_NO,
393                                   &count_address,
394                                   pc);
395   if (0 == pc->off)
396   {
397     dump_pc (pc);
398     return;
399   }
400   pc->address_list_size = pc->off;
401   pc->address_list = GNUNET_malloc (sizeof (struct AddressRecord) * pc->off);
402   GNUNET_HELLO_iterate_addresses (hello,
403                                   GNUNET_NO,
404                                   &print_address,
405                                   pc);
406 }
407
408 /* ************************* DUMP Hello  ************************** */
409
410 /**
411  * Count the number of addresses in the HELLO.
412  *
413  * @param cls pointer to an `int *` used for the counter
414  * @param address an address to count
415  * @param expiration (unused)
416  * @return #GNUNET_OK
417  */
418 static int
419 count_addr (void *cls,
420             const struct GNUNET_HELLO_Address *address,
421             struct GNUNET_TIME_Absolute expiration)
422 {
423   int *c = cls;
424
425   (*c) ++;
426   return GNUNET_OK;
427 }
428
429
430 /**
431  * Write HELLO of my peer to a file.
432  *
433  * @param cls the `struct GetUriContext *`
434  * @param peer identity of the peer (unused)
435  * @param hello addresses of the peer
436  * @param err_msg error message
437  */
438 static void
439 dump_my_hello (void *cls,
440                const struct GNUNET_PeerIdentity *peer,
441                const struct GNUNET_HELLO_Message *hello,
442                const char *err_msg)
443 {
444   unsigned int size;
445   unsigned int c_addr;
446
447   if (NULL == peer)
448   {
449     pic = NULL;
450     if (NULL != err_msg)
451       FPRINTF (stderr,
452                _("Error in communication with PEERINFO service: %s\n"),
453                err_msg);
454     tt = GNUNET_SCHEDULER_add_now (&state_machine,
455                                    NULL);
456     return;
457   }
458
459   if (NULL == hello)
460   {
461     FPRINTF (stderr,
462              _("Failure: Did not receive %s\n"),
463              "HELLO");
464     return;
465   }
466
467   size = GNUNET_HELLO_size (hello);
468   if (0 == size)
469   {
470     FPRINTF (stderr,
471              _("Failure: Received invalid %s\n"),
472              "HELLO");
473       return;
474   }
475   if (GNUNET_SYSERR ==
476       GNUNET_DISK_fn_write (dump_hello, hello, size,
477                             GNUNET_DISK_PERM_USER_READ |
478                             GNUNET_DISK_PERM_USER_WRITE |
479                             GNUNET_DISK_PERM_GROUP_READ |
480                             GNUNET_DISK_PERM_OTHER_READ))
481   {
482     FPRINTF (stderr,
483              _("Failed to write HELLO with %u bytes to file `%s'\n"),
484              size,
485              dump_hello);
486     if (0 != UNLINK (dump_hello))
487       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
488                                 GNUNET_ERROR_TYPE_BULK,
489                                 "unlink",
490                                 dump_hello);
491
492   }
493   c_addr = 0;
494   GNUNET_HELLO_iterate_addresses (hello,
495                                   GNUNET_NO,
496                                   count_addr,
497                                   &c_addr);
498
499   if (! be_quiet)
500   {
501     FPRINTF (stderr,
502              _("Wrote %s HELLO containing %u addresses with %u bytes to file `%s'\n"),
503              (GNUNET_YES == GNUNET_HELLO_is_friend_only(hello)) ? "friend-only": "public",
504              c_addr,
505              size,
506              dump_hello);
507   }
508   GNUNET_free (dump_hello);
509   dump_hello = NULL;
510 }
511
512
513 /* ************************* GET URI ************************** */
514
515
516 /**
517  * Print URI of the peer.
518  *
519  * @param cls the `struct GetUriContext *`
520  * @param peer identity of the peer (unused)
521  * @param hello addresses of the peer
522  * @param err_msg error message
523  */
524 static void
525 print_my_uri (void *cls,
526               const struct GNUNET_PeerIdentity *peer,
527               const struct GNUNET_HELLO_Message *hello,
528               const char *err_msg)
529 {
530   if (NULL == peer)
531   {
532     pic = NULL;
533     if (NULL != err_msg)
534       FPRINTF (stderr,
535                _("Error in communication with PEERINFO service: %s\n"),
536                err_msg);
537     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
538     return;
539   }
540
541   if (NULL == hello)
542     return;
543   char *uri = GNUNET_HELLO_compose_uri (hello,
544                                         &GPI_plugins_find);
545   if (NULL != uri)
546   {
547     printf ("%s\n",
548             (const char *) uri);
549     GNUNET_free (uri);
550   }
551 }
552
553
554 /* ************************* import HELLO by URI ********************* */
555
556
557 /**
558  * Continuation called from #GNUNET_PEERINFO_add_peer()
559  *
560  * @param cls closure, NULL
561  * @param emsg error message, NULL on success
562  */
563 static void
564 add_continuation (void *cls,
565                   const char *emsg)
566 {
567   ac = NULL;
568   if (NULL != emsg)
569     fprintf (stderr,
570              _("Failure adding HELLO: %s\n"),
571              emsg);
572   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
573 }
574
575
576 /**
577  * Parse the PUT URI given at the command line and add it to our peerinfo
578  * database.
579  *
580  * @param put_uri URI string to parse
581  * @return #GNUNET_OK on success,
582  *         #GNUNET_SYSERR if the URI was invalid,
583  *         #GNUNET_NO on other errors
584  */
585 static int
586 parse_hello_uri (const char *put_uri)
587 {
588   struct GNUNET_HELLO_Message *hello = NULL;
589
590   int ret = GNUNET_HELLO_parse_uri (put_uri,
591                                     &my_peer_identity.public_key,
592                                     &hello,
593                                     &GPI_plugins_find);
594
595   if (NULL != hello)
596   {
597     /* WARNING: this adds the address from URI WITHOUT verification! */
598     if (GNUNET_OK == ret)
599       ac = GNUNET_PEERINFO_add_peer (peerinfo, hello,
600                                      &add_continuation,
601                                      NULL);
602     else
603       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
604     GNUNET_free (hello);
605   }
606   return ret;
607 }
608
609
610 /* ************************ Main state machine ********************* */
611
612
613 /**
614  * Main state machine that goes over all options and
615  * runs the next requested function.
616  *
617  * @param cls unused
618  * @param tc scheduler context
619  */
620 static void
621 shutdown_task (void *cls,
622                const struct GNUNET_SCHEDULER_TaskContext *tc)
623 {
624   struct PrintContext *pc;
625   struct AddressRecord *ar;
626   unsigned int i;
627
628   if (NULL != ac)
629   {
630     GNUNET_PEERINFO_add_peer_cancel (ac);
631     ac = NULL;
632   }
633   if (GNUNET_SCHEDULER_NO_TASK != tt)
634   {
635     GNUNET_SCHEDULER_cancel (tt);
636     tt = GNUNET_SCHEDULER_NO_TASK;
637   }
638   if (NULL != pic)
639   {
640     GNUNET_PEERINFO_iterate_cancel (pic);
641     pic = NULL;
642   }
643   while (NULL != (pc = pc_head))
644   {
645     GNUNET_CONTAINER_DLL_remove (pc_head,
646                                  pc_tail,
647                                  pc);
648     for (i=0;i<pc->address_list_size;i++)
649     {
650       ar = &pc->address_list[i];
651       GNUNET_free_non_null (ar->result);
652       if (NULL != ar->atsc)
653       {
654         GNUNET_TRANSPORT_address_to_string_cancel (ar->atsc);
655         ar->atsc = NULL;
656       }
657     }
658     GNUNET_free_non_null (pc->address_list);
659     GNUNET_free (pc);
660   }
661   GPI_plugins_unload ();
662   if (NULL != peerinfo)
663   {
664     GNUNET_PEERINFO_disconnect (peerinfo);
665     peerinfo = NULL;
666   }
667 }
668
669
670 /**
671  * Function called with the result of the check if the PEERINFO
672  * service is running.
673  *
674  * @param cls closure with our configuration
675  * @param result #GNUNET_YES if PEERINFO is running
676  */
677 static void
678 testservice_task (void *cls,
679                   int result)
680 {
681   struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
682   char *fn;
683
684   if (GNUNET_YES != result)
685   {
686     FPRINTF (stderr,
687              _("Service `%s' is not running, please start GNUnet\n"),
688              "peerinfo");
689     return;
690   }
691
692   if (NULL == (peerinfo = GNUNET_PEERINFO_connect (cfg)))
693   {
694     FPRINTF (stderr,
695              "%s",
696              _("Could not access PEERINFO service.  Exiting.\n"));
697     return;
698   }
699   if ( (GNUNET_YES == get_self) ||
700        (GNUNET_YES == get_uri) ||
701        (NULL != dump_hello) )
702   {
703     /* load private key */
704     if (GNUNET_OK !=
705         GNUNET_CONFIGURATION_get_value_filename (cfg,
706                                                  "PEER",
707                                                  "PRIVATE_KEY",
708                                                  &fn))
709     {
710       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
711                                  "PEER",
712                                  "PRIVATE_KEY");
713       return;
714     }
715     if (NULL == (priv = GNUNET_CRYPTO_eddsa_key_create_from_file (fn)))
716     {
717       FPRINTF (stderr,
718                _("Loading hostkey from `%s' failed.\n"),
719                fn);
720       GNUNET_free (fn);
721       return;
722     }
723     GNUNET_free (fn);
724     GNUNET_CRYPTO_eddsa_key_get_public (priv,
725                                         &my_peer_identity.public_key);
726     GNUNET_free (priv);
727   }
728
729   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
730   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
731                                 &shutdown_task,
732                                 NULL);
733 }
734
735
736 /**
737  * Main function that will be run by the scheduler.
738  *
739  * @param cls closure
740  * @param args remaining command-line arguments
741  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
742  * @param c configuration
743  */
744 static void
745 run (void *cls,
746      char *const *args,
747      const char *cfgfile,
748      const struct GNUNET_CONFIGURATION_Handle *c)
749 {
750   cfg = c;
751   if ( (NULL != args[0]) &&
752        (NULL == put_uri) &&
753        (args[0] == strcasestr (args[0], "gnunet://hello/")) )
754   {
755     put_uri = GNUNET_strdup (args[0]);
756     args++;
757   }
758   if (NULL != args[0])
759   {
760     FPRINTF (stderr,
761              _("Invalid command line argument `%s'\n"),
762              args[0]);
763     return;
764   }
765
766   GNUNET_CLIENT_service_test ("peerinfo",
767                               cfg,
768                               GNUNET_TIME_UNIT_SECONDS,
769                               &testservice_task, (void *) cfg);
770 }
771
772
773 /**
774  * Main state machine that goes over all options and
775  * runs the next requested function.
776  *
777  * @param cls unused
778  * @param tc scheduler context
779  */
780 static void
781 state_machine (void *cls,
782                const struct GNUNET_SCHEDULER_TaskContext *tc)
783 {
784   tt = GNUNET_SCHEDULER_NO_TASK;
785
786   if (NULL != put_uri)
787   {
788     GPI_plugins_load (cfg);
789     if (GNUNET_SYSERR == parse_hello_uri (put_uri))
790     {
791       fprintf (stderr,
792                _("Invalid URI `%s'\n"),
793                put_uri);
794       GNUNET_SCHEDULER_shutdown ();
795     }
796     GNUNET_free (put_uri);
797     put_uri = NULL;
798   }
799   else if (GNUNET_YES == get_info)
800   {
801     get_info = GNUNET_NO;
802     GPI_plugins_load (cfg);
803     pic = GNUNET_PEERINFO_iterate (peerinfo,
804                                    include_friend_only,
805                                    NULL,
806                                    TIMEOUT,
807                                    &print_peer_info, NULL);
808   }
809   else if (GNUNET_YES == get_self)
810   {
811     get_self = GNUNET_NO;
812     if (be_quiet)
813       printf ("%s\n",
814               GNUNET_i2s_full (&my_peer_identity));
815     else
816       printf (_("I am peer `%s'.\n"),
817               GNUNET_i2s_full (&my_peer_identity));
818     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
819   }
820   else if (GNUNET_YES == get_uri)
821   {
822     GPI_plugins_load (cfg);
823     pic = GNUNET_PEERINFO_iterate (peerinfo,
824                                    include_friend_only,
825                                    &my_peer_identity,
826                                    TIMEOUT,
827                                    &print_my_uri, NULL);
828     get_uri = GNUNET_NO;
829   }
830   else if (NULL != dump_hello)
831   {
832     pic = GNUNET_PEERINFO_iterate (peerinfo,
833                                    include_friend_only,
834                                    &my_peer_identity,
835                                    TIMEOUT,
836                                    &dump_my_hello, NULL);
837   }
838   else if (GNUNET_YES == default_operation)
839   {
840     /* default operation list all */
841     default_operation = GNUNET_NO;
842     get_info = GNUNET_YES;
843     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
844   }
845   else
846     {
847       GNUNET_SCHEDULER_shutdown ();
848   }
849   default_operation = GNUNET_NO;
850 }
851
852
853 /**
854  * The main function to obtain peer information.
855  *
856  * @param argc number of arguments from the command line
857  * @param argv command line arguments
858  * @return 0 ok, 1 on error
859  */
860 int
861 main (int argc, char *const *argv)
862 {
863   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
864     {'n', "numeric", NULL,
865      gettext_noop ("don't resolve host names"),
866      0, &GNUNET_GETOPT_set_one, &no_resolve},
867     {'q', "quiet", NULL,
868      gettext_noop ("output only the identity strings"),
869      0, &GNUNET_GETOPT_set_one, &be_quiet},
870     {'f', "friends", NULL,
871      gettext_noop ("include friend-only information"),
872      0, &GNUNET_GETOPT_set_one, &include_friend_only},
873     {'s', "self", NULL,
874      gettext_noop ("output our own identity only"),
875      0, &GNUNET_GETOPT_set_one, &get_self},
876     {'i', "info", NULL,
877      gettext_noop ("list all known peers"),
878      0, &GNUNET_GETOPT_set_one, &get_info},
879     {'d', "dump-hello", NULL,
880      gettext_noop ("dump hello to file"),
881      1, &GNUNET_GETOPT_set_string, &dump_hello},
882     {'g', "get-hello", NULL,
883      gettext_noop ("also output HELLO uri(s)"),
884      0, &GNUNET_GETOPT_set_one, &get_uri},
885     {'p', "put-hello", "HELLO",
886      gettext_noop ("add given HELLO uri to the database"),
887      1, &GNUNET_GETOPT_set_string, &put_uri},
888     GNUNET_GETOPT_OPTION_END
889   };
890   int ret;
891
892   default_operation = GNUNET_YES;
893   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc,
894                                                  argv,
895                                                  &argc, &argv))
896     return 2;
897
898   ret = (GNUNET_OK ==
899          GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
900                              gettext_noop ("Print information about peers."),
901                              options, &run, NULL)) ? 0 : 1;
902   GNUNET_free ((void*) argv);
903   return ret;
904 }
905
906 /* end of gnunet-peerinfo.c */