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