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