missing check
[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  * My public key.
194  */
195 static struct GNUNET_CRYPTO_EccPublicKey my_public_key;
196
197 /**
198  * Head of list of print contexts.
199  */
200 static struct PrintContext *pc_head;
201
202 /**
203  * Tail of list of print contexts.
204  */
205 static struct PrintContext *pc_tail;
206
207 /**
208  * Handle to current 'GNUNET_PEERINFO_add_peer' operation.
209  */
210 static struct GNUNET_PEERINFO_AddContext *ac;
211
212
213 /**
214  * Main state machine that goes over all options and
215  * runs the next requested function.
216  *
217  * @param cls unused
218  * @param tc unused
219  */
220 static void
221 state_machine (void *cls,
222                const struct GNUNET_SCHEDULER_TaskContext *tc);
223
224
225 /* ********************* 'get_info' ******************* */
226
227 /**
228  * Print the collected address information to the console and free 'pc'.
229  *
230  * @param pc printing context
231  */
232 static void
233 dump_pc (struct PrintContext *pc)
234 {
235   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
236   unsigned int i;
237
238   GNUNET_CRYPTO_hash_to_enc (&pc->peer.hashPubKey, &enc);
239   printf (_("%sPeer `%s'\n"),
240         (GNUNET_YES == pc->friend_only) ? "F2F: " : "",
241           (const char *) &enc);
242   for (i = 0; i < pc->num_addresses; i++)
243   {
244     if (NULL != pc->address_list[i].result)
245     {
246       printf (_("\tExpires: %s \t %s\n"), GNUNET_STRINGS_absolute_time_to_string(pc->address_list[i].expiration), pc->address_list[i].result);
247       GNUNET_free (pc->address_list[i].result);
248     }
249   }
250   printf ("\n");
251   GNUNET_free_non_null (pc->address_list);
252   GNUNET_CONTAINER_DLL_remove (pc_head,
253                                pc_tail,
254                                pc);
255   GNUNET_free (pc);
256   if ( (NULL == pc_head) &&
257        (NULL == pic) )
258     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);  
259 }
260
261
262 /* ************************* list all known addresses **************** */
263
264
265 /**
266  * Function to call with a human-readable format of an address
267  *
268  * @param cls closure
269  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
270  */
271 static void
272 process_resolved_address (void *cls, const char *address)
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   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 GNUNET_CRYPTO_HashAsciiEncoded enc;
349   struct PrintContext *pc;
350   int friend_only;
351
352   if (NULL == peer)
353   {
354     pic = NULL; /* end of iteration */
355     if (NULL != err_msg)
356     {
357       FPRINTF (stderr, 
358                _("Error in communication with PEERINFO service: %s\n"),
359                err_msg);
360     }
361     if (NULL == pc_head)
362       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
363     return;
364   }
365   friend_only = GNUNET_NO;
366   if (NULL != hello)
367         friend_only = GNUNET_HELLO_is_friend_only (hello);
368   if ((GNUNET_YES == be_quiet) || (NULL == hello))
369   {
370     GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc);
371     printf ("%s%s\n",
372                 (GNUNET_YES == friend_only) ? "F2F: " : "",
373                 (const char *) &enc);
374     return;
375   }
376   pc = GNUNET_malloc (sizeof (struct PrintContext));
377   GNUNET_CONTAINER_DLL_insert (pc_head,
378                                pc_tail, 
379                                pc);
380   pc->peer = *peer;
381   pc->friend_only = friend_only;
382   GNUNET_HELLO_iterate_addresses (hello, 
383                                   GNUNET_NO, 
384                                   &count_address, 
385                                   pc);
386   if (0 == pc->off)
387   {
388     dump_pc (pc);
389     return;
390   }
391   pc->address_list_size = pc->off;
392   pc->address_list = GNUNET_malloc (sizeof (struct AddressRecord) * pc->off);
393   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, 
394                                   &print_address, pc);
395 }
396
397 /* ************************* DUMP Hello  ************************** */
398
399 static int count_addr(void *cls,
400                                                                                  const struct GNUNET_HELLO_Address *address,
401                                                                                  struct GNUNET_TIME_Absolute expiration)
402 {
403         int *c = cls;
404   (*c) ++;
405   return GNUNET_OK;
406 }
407
408 /**
409  * Write Hello of my peer to a file.
410  *
411  * @param cls the 'struct GetUriContext'
412  * @param peer identity of the peer (unused)
413  * @param hello addresses of the peer
414  * @param err_msg error message
415  */
416 static void
417 dump_my_hello (void *cls, const struct GNUNET_PeerIdentity *peer,
418               const struct GNUNET_HELLO_Message *hello,
419               const char *err_msg)
420 {
421         unsigned int size;
422         unsigned int c_addr;
423   if (peer == NULL)
424   {
425     pic = NULL;
426     if (err_msg != NULL)
427       FPRINTF (stderr,
428                _("Error in communication with PEERINFO service: %s\n"),
429                err_msg);
430     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
431     return;
432   }
433
434   if (NULL == hello)
435   {
436                 FPRINTF (stderr,
437                          _("Failure: Did not receive %s\n"), "HELLO");
438     return;
439   }
440
441   size = GNUNET_HELLO_size (hello);
442   if (0 == size)
443   {
444                 FPRINTF (stderr,
445                          _("Failure: Received invalid %s\n"), "HELLO");
446       return;
447   }
448   if (GNUNET_SYSERR == GNUNET_DISK_fn_write (dump_hello, hello, size,
449                             GNUNET_DISK_PERM_USER_READ |
450                             GNUNET_DISK_PERM_USER_WRITE |
451                             GNUNET_DISK_PERM_GROUP_READ |
452                             GNUNET_DISK_PERM_OTHER_READ))
453   {
454                 FPRINTF (stderr, _("Failed to write HELLO with %u bytes to file `%s'\n"),
455                          size, dump_hello);
456                 if (0 != UNLINK (dump_hello))
457                 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
458                               GNUNET_ERROR_TYPE_BULK, "unlink", dump_hello);
459
460   }
461   c_addr = 0;
462   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, count_addr, &c_addr);
463
464   if (!be_quiet)
465   {
466                 FPRINTF (stderr,
467                          _("Wrote %s HELLO containing %u addresses with %u bytes to file `%s'\n"),
468                          (GNUNET_YES == GNUNET_HELLO_is_friend_only(hello)) ? "friend-only": "public",
469                                         c_addr, size, dump_hello);
470   }
471
472   GNUNET_free (dump_hello);
473   dump_hello = NULL;
474
475 }
476
477
478 /* ************************* GET URI ************************** */
479
480
481 /**
482  * Print URI of the peer.
483  *
484  * @param cls the 'struct GetUriContext'
485  * @param peer identity of the peer (unused)
486  * @param hello addresses of the peer
487  * @param err_msg error message
488  */
489 static void
490 print_my_uri (void *cls, const struct GNUNET_PeerIdentity *peer,
491               const struct GNUNET_HELLO_Message *hello, 
492               const char *err_msg)
493 {
494   if (peer == NULL)
495   {
496     pic = NULL;
497     if (err_msg != NULL)
498       FPRINTF (stderr,
499                _("Error in communication with PEERINFO service: %s\n"), 
500                err_msg);
501     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
502     return;
503   }
504
505   if (NULL == hello)
506     return;
507   char *uri = GNUNET_HELLO_compose_uri(hello, &GPI_plugins_find);
508   if (NULL != uri) {
509     printf ("%s\n", (const char *) uri);
510     GNUNET_free (uri);
511   }
512 }
513
514
515 /* ************************* import HELLO by URI ********************* */
516
517
518 /**
519  * Continuation called from 'GNUNET_PEERINFO_add_peer'
520  *
521  * @param cls closure, NULL
522  * @param emsg error message, NULL on success
523  */
524 static void
525 add_continuation (void *cls,
526                   const char *emsg)
527 {
528   ac = NULL;
529   if (NULL != emsg)
530     fprintf (stderr,
531              _("Failure adding HELLO: %s\n"),
532              emsg);
533   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
534 }
535
536
537 /**
538  * Parse the PUT URI given at the command line and add it to our peerinfo 
539  * database.
540  *
541  * @param put_uri URI string to parse
542  * @return GNUNET_OK on success, GNUNET_SYSERR if the URI was invalid, GNUNET_NO on other errors
543  */
544 static int
545 parse_hello_uri (const char *put_uri)
546 {
547   struct GNUNET_HELLO_Message *hello = NULL;
548
549   int ret = GNUNET_HELLO_parse_uri(put_uri, &my_public_key, &hello, &GPI_plugins_find);
550
551   if (NULL != hello) {
552     /* WARNING: this adds the address from URI WITHOUT verification! */
553     if (GNUNET_OK == ret)
554       ac = GNUNET_PEERINFO_add_peer (peerinfo, hello, &add_continuation, NULL);
555     else
556       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
557     GNUNET_free (hello);
558   }
559
560   /* wait 1s to give peerinfo operation a chance to succeed */
561   /* FIXME: current peerinfo API sucks to require this; not to mention
562      that we get no feedback to determine if the operation actually succeeded */
563   return ret;
564 }
565
566
567 /* ************************ Main state machine ********************* */
568
569
570 /**
571  * Main state machine that goes over all options and
572  * runs the next requested function.
573  *
574  * @param cls unused
575  * @param tc scheduler context
576  */
577 static void
578 shutdown_task (void *cls,
579                const struct GNUNET_SCHEDULER_TaskContext *tc)
580 {
581   struct PrintContext *pc;
582   struct AddressRecord *ar;
583   unsigned int i;
584
585   if (NULL != ac)
586   {
587     GNUNET_PEERINFO_add_peer_cancel (ac);
588     ac = NULL;
589   }
590   if (GNUNET_SCHEDULER_NO_TASK != tt)
591   {
592     GNUNET_SCHEDULER_cancel (tt);
593     tt = GNUNET_SCHEDULER_NO_TASK;
594   }
595   if (NULL != pic)
596   {
597     GNUNET_PEERINFO_iterate_cancel (pic);
598     pic = NULL;
599   }
600   while (NULL != (pc = pc_head))
601   {
602     GNUNET_CONTAINER_DLL_remove (pc_head,
603                                  pc_tail,
604                                  pc);
605     for (i=0;i<pc->address_list_size;i++)
606     {
607       ar = &pc->address_list[i];
608       GNUNET_free_non_null (ar->result);
609       if (NULL != ar->atsc)
610       {
611         GNUNET_TRANSPORT_address_to_string_cancel (ar->atsc);
612         ar->atsc = NULL;
613       }
614     }
615     GNUNET_free_non_null (pc->address_list);
616     GNUNET_free (pc);
617   }
618   GPI_plugins_unload ();
619   if (NULL != peerinfo)
620   {
621     GNUNET_PEERINFO_disconnect (peerinfo);
622     peerinfo = NULL;
623   }
624 }
625
626
627 /**
628  * Main function that will be run by the scheduler.
629  *
630  * @param cls closure
631  * @param args remaining command-line arguments
632  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
633  * @param c configuration
634  */
635 static void
636 run (void *cls, char *const *args, const char *cfgfile,
637      const struct GNUNET_CONFIGURATION_Handle *c)
638 {
639   struct GNUNET_CRYPTO_EccPrivateKey *priv;
640   char *fn;
641
642   cfg = c;
643   if ( (NULL != args[0]) &&
644        (NULL == put_uri) &&
645        (args[0] == strcasestr (args[0], "gnunet://hello/")) )
646   {
647     put_uri = GNUNET_strdup (args[0]);
648     args++;
649   }
650   if (NULL != args[0]) 
651   {
652     FPRINTF (stderr, 
653              _("Invalid command line argument `%s'\n"), 
654              args[0]);
655     return;
656   }
657   if (NULL == (peerinfo = GNUNET_PEERINFO_connect (cfg)))
658   {
659     FPRINTF (stderr, "%s",  _("Could not access PEERINFO service.  Exiting.\n"));
660     return;
661   }
662   if ( (GNUNET_YES == get_self) || (GNUNET_YES == get_uri) || (NULL != dump_hello) )
663   {
664     /* load private key */
665     if (GNUNET_OK !=
666         GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER", "PRIVATE_KEY",
667                                                  &fn))
668     {
669       FPRINTF (stderr, _("Could not find option `%s:%s' in configuration.\n"),
670                "GNUNETD", "HOSTKEYFILE");
671       return;
672     }
673     if (NULL == (priv = GNUNET_CRYPTO_ecc_key_create_from_file (fn)))
674     {
675       FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
676       GNUNET_free (fn);
677       return;
678     }
679     GNUNET_free (fn);
680     GNUNET_CRYPTO_ecc_key_get_public (priv, &my_public_key);
681     GNUNET_CRYPTO_ecc_key_free (priv);
682     GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &my_peer_identity.hashPubKey);
683   }
684
685   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
686   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
687                                 &shutdown_task,
688                                 NULL);
689 }
690
691
692 /**
693  * Main state machine that goes over all options and
694  * runs the next requested function.
695  *
696  * @param cls unused
697  * @param tc scheduler context
698  */
699 static void
700 state_machine (void *cls,
701                const struct GNUNET_SCHEDULER_TaskContext *tc)
702 {
703   tt = GNUNET_SCHEDULER_NO_TASK;
704
705   if (NULL != put_uri)
706   {
707     GPI_plugins_load (cfg);
708     if (GNUNET_SYSERR == parse_hello_uri (put_uri))
709     {
710       fprintf (stderr,
711                _("Invalid URI `%s'\n"),
712                put_uri);
713       GNUNET_SCHEDULER_shutdown ();
714     }
715     GNUNET_free (put_uri);
716     put_uri = NULL;
717   }
718   else if (GNUNET_YES == get_info)
719   {
720     get_info = GNUNET_NO;
721     GPI_plugins_load (cfg);
722     pic = GNUNET_PEERINFO_iterate (peerinfo, include_friend_only, NULL,
723                                    TIMEOUT,
724                                    &print_peer_info, NULL);
725   }
726   else if (GNUNET_YES == get_self)
727   {
728     struct GNUNET_CRYPTO_HashAsciiEncoded enc;
729     get_self = GNUNET_NO;
730     GNUNET_CRYPTO_hash_to_enc (&my_peer_identity.hashPubKey, &enc);
731     if (be_quiet)
732       printf ("%s\n", (char *) &enc);
733     else
734       printf (_("I am peer `%s'.\n"), (const char *) &enc);
735         tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
736   }
737   else if (GNUNET_YES == get_uri)
738   {
739     GPI_plugins_load (cfg);
740     pic = GNUNET_PEERINFO_iterate (peerinfo, include_friend_only, &my_peer_identity,
741                                    TIMEOUT, &print_my_uri, NULL);
742     get_uri = GNUNET_NO;
743   }
744   else if (NULL != dump_hello)
745   {
746     pic = GNUNET_PEERINFO_iterate (peerinfo, include_friend_only, &my_peer_identity,
747                                    TIMEOUT, &dump_my_hello, NULL);
748   }
749   else if (GNUNET_YES == default_operation)
750   {
751         /* default operation list all */
752         default_operation = GNUNET_NO;
753         get_info = GNUNET_YES;
754         tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
755   }
756   else
757   {
758         GNUNET_SCHEDULER_shutdown ();
759   }
760         default_operation = GNUNET_NO;
761 }
762
763
764 /**
765  * The main function to obtain peer information.
766  *
767  * @param argc number of arguments from the command line
768  * @param argv command line arguments
769  * @return 0 ok, 1 on error
770  */
771 int
772 main (int argc, char *const *argv)
773 {
774         default_operation = GNUNET_YES;
775   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
776     {'n', "numeric", NULL,
777      gettext_noop ("don't resolve host names"),
778      0, &GNUNET_GETOPT_set_one, &no_resolve},
779     {'q', "quiet", NULL,
780      gettext_noop ("output only the identity strings"),
781      0, &GNUNET_GETOPT_set_one, &be_quiet},
782     {'f', "friends", NULL,
783      gettext_noop ("include friend-only information"),
784      0, &GNUNET_GETOPT_set_one, &include_friend_only},
785     {'s', "self", NULL,
786      gettext_noop ("output our own identity only"),
787      0, &GNUNET_GETOPT_set_one, &get_self},
788     {'i', "info", NULL,
789      gettext_noop ("list all known peers"),
790      0, &GNUNET_GETOPT_set_one, &get_info},
791           {'d', "dump-hello", NULL,
792                  gettext_noop ("dump hello to file"),
793                  1, &GNUNET_GETOPT_set_string, &dump_hello},
794     {'g', "get-hello", NULL,
795      gettext_noop ("also output HELLO uri(s)"),
796      0, &GNUNET_GETOPT_set_one, &get_uri},
797     {'p', "put-hello", "HELLO",
798      gettext_noop ("add given HELLO uri to the database"),
799      1, &GNUNET_GETOPT_set_string, &put_uri},
800     GNUNET_GETOPT_OPTION_END
801   };
802   int ret;
803
804   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
805     return 2;
806
807   ret = (GNUNET_OK ==
808          GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
809                              gettext_noop ("Print information about peers."),
810                              options, &run, NULL)) ? 0 : 1;
811   GNUNET_free ((void*) argv);
812   return ret;
813 }
814
815 /* end of gnunet-peerinfo.c */