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