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