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