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