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