vminko: implementing BINDTO option for hostlist service (#2140)
[oweals/gnunet.git] / src / peerinfo-tool / gnunet-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001-2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file peerinfo-tool/gnunet-peerinfo.c
23  * @brief Print information about other known peers.
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_crypto_lib.h"
28 #include "gnunet_configuration_lib.h"
29 #include "gnunet_getopt_lib.h"
30 #include "gnunet_peerinfo_service.h"
31 #include "gnunet_transport_service.h"
32 #include "gnunet_program_lib.h"
33 #include "gnunet_transport_plugin.h"
34 #include "gnunet-peerinfo_plugins.h"
35
36
37 /**
38  * Structure we use to collect printable address information.
39  */
40 struct PrintContext
41 {
42   /**
43    * Identity of the peer.
44    */
45   struct GNUNET_PeerIdentity peer;
46   
47   /**
48    * List of printable addresses.
49    */
50   char **address_list;
51
52   /**
53    * Number of addresses in 'address_list'.
54    */
55   unsigned int num_addresses;
56
57   /**
58    * Current offset in 'address_list'
59    */
60   uint32_t off;
61
62   /**
63    * URI (FIXME: overloaded struct!)
64    */
65   char *uri;
66
67   /**
68    * Length of 'uri' (FIXME: not nice)
69    */
70   size_t uri_len;
71 };
72
73
74 /**
75  * Context used for building our own URI.
76  */
77 struct GetUriContext
78 {
79   /**
80    * Final URI.
81    */
82   char *uri;
83
84   struct PrintContext *pc;
85 };
86
87
88 /**
89  * FIXME.
90  */
91 struct GNUNET_PEERINFO_HelloAddressParsingContext
92 {
93   /**
94    * FIXME.
95    */
96   char *tmp;
97   
98   /**
99    * FIXME.
100    */
101   char *pos;
102
103   /**
104    * FIXME.
105    */
106   size_t tmp_len;
107 };
108
109
110 /**
111  * Option '-n'
112  */
113 static int no_resolve;
114
115 /**
116  * Option '-q'
117  */
118 static int be_quiet;
119
120 /**
121  * Option '-s'
122  */
123 static int get_self;
124
125 /**
126  * Option 
127  */
128 static int get_uri;
129
130 /**
131  * Option '-i'
132  */
133 static int get_info;
134
135 /**
136  * Option 
137  */
138 static char *put_uri;
139
140 /**
141  * Handle to peerinfo service.
142  */
143 static struct GNUNET_PEERINFO_Handle *peerinfo;
144
145 /**
146  * Configuration handle.
147  */
148 static const struct GNUNET_CONFIGURATION_Handle *cfg;
149
150 /**
151  * Main state machine task (if active).
152  */
153 static GNUNET_SCHEDULER_TaskIdentifier tt;
154
155 /**
156  * Current iterator context (if active, otherwise NULL).
157  */
158 static struct GNUNET_PEERINFO_IteratorContext *pic;
159
160 /**
161  * Current address-to-string context (if active, otherwise NULL).
162  */
163 static struct GNUNET_TRANSPORT_AddressToStringContext *atsc;
164
165 /**
166  * My peer identity.
167  */
168 static struct GNUNET_PeerIdentity my_peer_identity;
169
170 /**
171  * My public key.
172  */
173 static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
174
175
176 /**
177  * Main state machine that goes over all options and
178  * runs the next requested function.
179  *
180  * @param cls unused
181  * @param tc unused
182  */
183 static void
184 state_machine (void *cls,
185                const struct GNUNET_SCHEDULER_TaskContext *tc);
186
187
188 /* ********************* 'get_info' ******************* */
189
190 /**
191  * Print the collected address information to the console and free 'pc'.
192  *
193  * @param pc printing context
194  */
195 static void
196 dump_pc (struct PrintContext *pc)
197 {
198   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
199   unsigned int i;
200
201   GNUNET_CRYPTO_hash_to_enc (&pc->peer.hashPubKey, &enc);
202   printf (_("Peer `%s'\n"), (const char *) &enc);
203   for (i = 0; i < pc->num_addresses; i++)
204   {
205     printf ("\t%s\n", pc->address_list[i]);
206     GNUNET_free (pc->address_list[i]);
207   }
208   printf ("\n");
209   GNUNET_array_grow (pc->address_list, pc->num_addresses, 0);
210   GNUNET_free (pc);
211 }
212
213
214 /* ************************* list all known addresses **************** */
215
216
217 /**
218  * Function to call with a human-readable format of an address
219  *
220  * @param cls closure
221  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
222  */
223 static void
224 process_resolved_address (void *cls, const char *address)
225 {
226   struct PrintContext *pc = cls;
227
228   atsc = NULL;
229   if (address == NULL)
230   {
231     pc->off--;
232     if (pc->off == 0)
233       dump_pc (pc);
234     return;
235   }
236   GNUNET_array_append (pc->address_list, pc->num_addresses,
237                        GNUNET_strdup (address));
238 }
239
240
241 /**
242  * Iterator callback to go over all addresses and count them.
243  *
244  * @param cls 'struct PrintContext' with 'off' to increment
245  * @param address the address
246  * @param expiration expiration time
247  * @return GNUNET_OK to keep the address and continue
248  */
249 static int
250 count_address (void *cls, const struct GNUNET_HELLO_Address *address,
251                struct GNUNET_TIME_Absolute expiration)
252 {
253   struct PrintContext *pc = cls;
254
255   pc->off++;
256   return GNUNET_OK;
257 }
258
259
260 /**
261  * Iterator callback to go over all addresses.
262  *
263  * @param cls closure
264  * @param address the address
265  * @param expiration expiration time
266  * @return GNUNET_OK to keep the address and continue
267  */
268 static int
269 print_address (void *cls, const struct GNUNET_HELLO_Address *address,
270                struct GNUNET_TIME_Absolute expiration)
271 {
272   struct PrintContext *pc = cls;
273
274   // FIXME: this is called many times in parallel!
275   atsc = GNUNET_TRANSPORT_address_to_string (cfg, address, no_resolve,
276                                              GNUNET_TIME_relative_multiply
277                                              (GNUNET_TIME_UNIT_SECONDS, 10),
278                                              &process_resolved_address, pc);
279   return GNUNET_OK;
280 }
281
282
283 /**
284  * Print information about the peer.
285  * Currently prints the GNUNET_PeerIdentity and the IP.
286  * Could of course do more (e.g. resolve via DNS).
287  */
288 static void
289 print_peer_info (void *cls, const struct GNUNET_PeerIdentity *peer,
290                  const struct GNUNET_HELLO_Message *hello, const char *err_msg)
291 {
292   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
293   struct PrintContext *pc;
294
295   if (peer == NULL)
296   {
297     if (err_msg != NULL)
298       FPRINTF (stderr, "%s",  _("Error in communication with PEERINFO service\n"));
299     // FIXME: this doesn't mean we're fully done with the printing!
300     // (as the a2s calls happen asynchronously!)
301     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
302     return;
303   }
304   if ((be_quiet) || (NULL == hello))
305   {
306     GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc);
307     printf ("%s\n", (const char *) &enc);
308     return;
309   }
310   pc = GNUNET_malloc (sizeof (struct PrintContext));
311   pc->peer = *peer;
312   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &count_address, pc);
313   if (0 == pc->off)
314   {
315     dump_pc (pc);
316     return;
317   }
318   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &print_address, pc);
319 }
320
321
322 /* ************************* GET URI ************************** */
323
324
325 /**
326  * Function that is called on each address of this peer.
327  * Expands the corresponding URI string.
328  *
329  * @param cls the 'GetUriContext'
330  * @param address address to add
331  * @param expiration expiration time for the address
332  * @return GNUNET_OK (continue iteration).
333  */
334 static int
335 compose_uri (void *cls, const struct GNUNET_HELLO_Address *address,
336              struct GNUNET_TIME_Absolute expiration)
337 {
338   struct GetUriContext *guc = cls;
339   struct GNUNET_TRANSPORT_PluginFunctions *papi;
340   const char *addr;
341   char *ret;
342   struct tm *t;
343   time_t seconds;
344
345   papi = GPI_plugins_find (address->transport_name);
346   if (papi == NULL)
347   {
348     /* Not an error - we might just not have the right plugin. */
349     return GNUNET_OK;
350   }
351   if (NULL == papi->address_to_string)
352   {
353     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354                 "URI conversion not implemented for plugin `%s'\n",
355                 address->transport_name);
356     return GNUNET_OK;
357   }
358   addr = papi->address_to_string (papi->cls, address->address, address->address_length);
359   if ( (addr == NULL) || (strlen(addr) == 0) )
360     return GNUNET_OK;
361   seconds = expiration.abs_value / 1000;
362   t = gmtime (&seconds);
363   GNUNET_asprintf (&ret,
364                    "%s!%04u%02u%02u%02u%02u%02u!%s!%s",
365                    guc->uri,
366                    t->tm_year,
367                    t->tm_mon, 
368                    t->tm_mday, 
369                    t->tm_hour,
370                    t->tm_min,
371                    t->tm_sec,
372                    address->transport_name, 
373                    addr);
374   GNUNET_free (guc->uri);
375   guc->uri = ret;
376   return GNUNET_OK;
377 }
378
379
380 /**
381  * Print URI of the peer.
382  *
383  * @param cls the 'struct GetUriContext'
384  * @param peer identity of the peer (unused)
385  * @param hello addresses of the peer
386  * @param err_msg error message
387  */
388 static void
389 print_my_uri (void *cls, const struct GNUNET_PeerIdentity *peer,
390               const struct GNUNET_HELLO_Message *hello, 
391               const char *err_msg)
392 {
393   struct GetUriContext *guc = cls;
394
395   if (peer == NULL)
396   {
397     if (err_msg != NULL)
398       FPRINTF (stderr,
399                _("Error in communication with PEERINFO service: %s\n"), 
400                err_msg);
401   } 
402   else
403   {
404     if (NULL != hello)
405       GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &compose_uri, guc);   
406     printf ("%s\n", (const char *) guc->uri);
407   }
408   GNUNET_free (guc->uri);
409   GNUNET_free (guc);  
410   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
411 }
412
413
414 /* ************************* import HELLO by URI ********************* */
415
416
417 static size_t
418 add_addr_to_hello (void *cls, size_t max, void *buffer)
419 {
420   struct tm expiration_time;
421   char buf[5];
422   long l;
423   time_t expiration_seconds;
424   struct GNUNET_TIME_Absolute expire;
425
426   struct GNUNET_PEERINFO_HelloAddressParsingContext *ctx = cls;
427   char *exp1, *exp2;
428   struct GNUNET_TRANSPORT_PluginFunctions *papi;
429   void *addr;
430   size_t addr_len;
431
432   /* End of string */
433   if (ctx->pos - ctx->tmp == ctx->tmp_len)
434     return 0;
435
436   /* Parsed past the end of string, OR wrong format */
437   if ((ctx->pos - ctx->tmp > ctx->tmp_len) || ctx->pos[0] != '!')
438   {
439     GNUNET_break (0);
440     return 0;
441   }
442
443   /* Not enough bytes (3 for three '!', 14 for expiration date, and
444    * at least 1 for type and 1 for address (1-byte long address is a joke,
445    * but it is not completely unrealistic. Zero-length address is.
446    */
447   if (ctx->tmp_len - (ctx->pos - ctx->tmp) < 1 /*!*/ * 3 + 14 + /* at least */ 2)
448   {
449     GNUNET_break (0);
450     return 0;
451   }
452   /* Go past the first '!', now we're on expiration date */
453   ctx->pos += 1;
454   /* Its length is known, so check for the next '!' right away */
455   if (ctx->pos[14] != '!')
456   {
457     GNUNET_break (0);
458     return 0;
459   }
460
461   memset (&expiration_time, 0, sizeof (struct tm));
462
463   /* This is FAR more strict than strptime(ctx->pos, "%Y%m%d%H%M%S", ...); */
464   /* FIXME: make it a separate function, since expiration is specified to every address */
465 #define GETNDIGITS(n,cond) \
466   strncpy (buf, &ctx->pos[0], n); \
467   buf[n] = '\0'; \
468   errno = 0; \
469   l = strtol (buf, NULL, 10); \
470   if (errno != 0 || cond) \
471   { \
472     GNUNET_break (0); \
473     return 0; \
474   } \
475   ctx->pos += n;
476
477   GETNDIGITS (4, l < 1900)
478   expiration_time.tm_year = l - 1900;
479
480   GETNDIGITS (2, l < 1 || l > 12)
481   expiration_time.tm_mon = l;
482
483   GETNDIGITS (2, l < 1 || l > 31)
484   expiration_time.tm_mday = l;
485
486   GETNDIGITS (2, l < 0 || l > 23)
487   expiration_time.tm_hour = l;
488
489   GETNDIGITS (2, l < 0 || l > 59)
490   expiration_time.tm_min = l;
491
492   /* 60 - with a leap second */
493   GETNDIGITS (2, l < 0 || l > 60)
494   expiration_time.tm_sec = l;
495
496   expiration_time.tm_isdst = -1;
497
498 #undef GETNDIGITS
499
500   expiration_seconds = mktime (&expiration_time);
501   if (expiration_seconds == (time_t) -1)
502   {
503     GNUNET_break (0);
504     return 0;
505   }
506   expire.abs_value = expiration_seconds * 1000;
507
508   /* Now we're at '!', advance to the transport type */
509   ctx->pos += 1;
510
511   /* Find the next '!' that separates transport type from
512    * the address
513    */
514   exp1 = strstr (ctx->pos, "!");
515   if (exp1 == NULL)
516   {
517     GNUNET_break (0);
518     return 0;
519   }
520   /* We need it 0-terminated */
521   exp1[0] = '\0';
522   /* Find the '!' that separates address from the next record.
523    * It might not be there, if this is the last record.
524    */
525   exp2 = strstr (&exp1[1], "!");
526   if (exp2 == NULL)
527     exp2 = &ctx->tmp[ctx->tmp_len];
528
529   papi = GPI_plugins_find (ctx->pos);
530   if (papi == NULL)
531   {
532     /* Not an error - we might just not have the right plugin.
533      * Skip this part, advance to the next one and recurse.
534      * But only if this is not the end of string.
535      */
536     ctx->pos = exp2 + 1;
537     if (ctx->pos - ctx->tmp >= ctx->tmp_len)
538       return 0;
539     return add_addr_to_hello (cls, max, buffer);
540   }
541   if (NULL == papi->string_to_address)
542   {
543     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
544                 _("Plugin `%s' does not support URIs yet\n"),
545                 ctx->pos);
546     ctx->pos = exp2 + 1;
547     if (ctx->pos - ctx->tmp >= ctx->tmp_len)
548       return 0;
549     return add_addr_to_hello (cls, max, buffer);
550   }
551   if ((papi->string_to_address != NULL) && (GNUNET_OK ==
552       papi->string_to_address (papi->cls, &exp1[1], exp2 - &exp1[1], &addr,
553       &addr_len)))
554   {
555     struct GNUNET_HELLO_Address address;
556     int ret;
557
558     /* address.peer is unset - not used by add_address() */
559     address.address_length = addr_len;
560     address.address = addr;
561     address.transport_name = ctx->pos;
562     ret = GNUNET_HELLO_add_address (&address, expire, buffer, max);
563     GNUNET_free (addr);
564     ctx->pos = exp2;
565     return ret;
566   }
567   return 0;
568 }
569
570
571 static void
572 parse_hello (const struct GNUNET_CONFIGURATION_Handle *c,
573              const char *put_uri)
574 {
575   int r;
576   char *scheme_part = NULL;
577   char *path_part = NULL;
578   char *exc;
579   int std_result;
580   struct GNUNET_HELLO_Message *hello;
581   struct GNUNET_PEERINFO_HelloAddressParsingContext ctx;
582
583   r = GNUNET_STRINGS_parse_uri (put_uri, &scheme_part, (const char **) &path_part);
584   if (r == GNUNET_NO)
585     return;
586   if (scheme_part == NULL || strcmp (scheme_part, "gnunet://") != 0)
587   {
588     GNUNET_free_non_null (scheme_part);
589     return;
590   }
591   GNUNET_free (scheme_part);
592
593   if (strncmp (path_part, "hello/", 6) != 0)
594     return;
595
596   path_part = &path_part[6];
597   ctx.tmp = GNUNET_strdup (path_part);
598   ctx.tmp_len = strlen (path_part);
599   exc = strstr (ctx.tmp, "!");
600   if (exc == NULL)
601     exc = ctx.tmp + ctx.tmp_len;
602   ctx.pos = exc;
603
604   std_result = GNUNET_STRINGS_string_to_data (ctx.tmp, exc - ctx.tmp,
605       (unsigned char *) &my_public_key, sizeof (my_public_key));
606   if (std_result != GNUNET_OK)
607   {
608     GNUNET_free (ctx.tmp);
609     return;
610   }
611
612   hello = GNUNET_HELLO_create (&my_public_key, add_addr_to_hello, &ctx);
613   GNUNET_free (ctx.tmp);
614
615   /* WARNING: this adds the address from URI WITHOUT verification! */
616   GNUNET_PEERINFO_add_peer (peerinfo, hello);
617   GNUNET_free (hello);
618   /* wait 1s to give peerinfo operation a chance to succeed */
619   tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
620                                      &state_machine, NULL);
621 }
622
623
624 /* ************************ Main state machine ********************* */
625
626
627 /**
628  * Main state machine that goes over all options and
629  * runs the next requested function.
630  *
631  * @param cls unused
632  * @param tc scheduler context
633  */
634 static void
635 shutdown_task (void *cls,
636                const struct GNUNET_SCHEDULER_TaskContext *tc)
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   if (NULL != atsc)
649   {
650     GNUNET_TRANSPORT_address_to_string_cancel (atsc);
651     atsc = NULL;
652   }
653   GPI_plugins_unload ();
654   GNUNET_PEERINFO_disconnect (peerinfo);
655   peerinfo = NULL;
656 }
657
658
659 /**
660  * Main function that will be run by the scheduler.
661  *
662  * @param cls closure
663  * @param args remaining command-line arguments
664  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
665  * @param c configuration
666  */
667 static void
668 run (void *cls, char *const *args, const char *cfgfile,
669      const struct GNUNET_CONFIGURATION_Handle *c)
670 {
671   struct GNUNET_CRYPTO_RsaPrivateKey *priv;
672   char *fn;
673
674   cfg = c;
675   if (args[0] != NULL)
676   {
677     FPRINTF (stderr, _("Invalid command line argument `%s'\n"), args[0]);
678     return;
679   }
680   peerinfo = GNUNET_PEERINFO_connect (cfg);
681   if (peerinfo == NULL)
682   {
683     FPRINTF (stderr, "%s",  _("Could not access PEERINFO service.  Exiting.\n"));
684     return;
685   }
686   if ( (GNUNET_YES == get_self) || (GNUNET_YES == get_uri) )
687   {
688     /* load private key */
689     if (GNUNET_OK !=
690         GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
691                                                  &fn))
692     {
693       FPRINTF (stderr, _("Could not find option `%s:%s' in configuration.\n"),
694                "GNUNETD", "HOSTKEYFILE");
695       return;
696     }
697
698     if (NULL == (priv = GNUNET_CRYPTO_rsa_key_create_from_file (fn)))
699     {
700       FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
701       GNUNET_free (fn);
702       return;
703     }
704     GNUNET_free (fn);
705     GNUNET_CRYPTO_rsa_key_get_public (priv, &my_public_key);
706     fprintf (stderr, "PK: `%s\n", 
707              GNUNET_CRYPTO_rsa_public_key_to_string (&my_public_key));
708     GNUNET_CRYPTO_rsa_key_free (priv);
709     GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &my_peer_identity.hashPubKey);
710   }
711
712   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
713   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
714                                 &shutdown_task,
715                                 NULL);
716 }
717
718
719 /**
720  * Main state machine that goes over all options and
721  * runs the next requested function.
722  *
723  * @param cls unused
724  * @param tc scheduler context
725  */
726 static void
727 state_machine (void *cls,
728                const struct GNUNET_SCHEDULER_TaskContext *tc)
729 {
730   tt = GNUNET_SCHEDULER_NO_TASK;
731
732   if (NULL != put_uri)
733   {
734     GPI_plugins_load (cfg);
735     parse_hello (cfg, put_uri);
736     put_uri = NULL;
737     return;
738   }
739   if (GNUNET_YES == get_info)
740   {
741     get_info = GNUNET_NO;
742     GPI_plugins_load (cfg);
743     GNUNET_PEERINFO_iterate (peerinfo, NULL,
744                              GNUNET_TIME_relative_multiply
745                              (GNUNET_TIME_UNIT_SECONDS, 5), &print_peer_info,
746                              NULL);
747     return;
748   }
749   if (GNUNET_YES == get_self)
750   {
751     struct GNUNET_CRYPTO_HashAsciiEncoded enc;
752
753     get_self = GNUNET_NO;
754     GNUNET_CRYPTO_hash_to_enc (&my_peer_identity.hashPubKey, &enc);
755     if (be_quiet)
756       printf ("%s\n", (char *) &enc);
757     else
758       printf (_("I am peer `%s'.\n"), (const char *) &enc);
759   }
760   if (GNUNET_YES == get_uri)
761   {
762     struct GetUriContext *guc;
763     char *pkey;
764
765     guc = GNUNET_malloc (sizeof (struct GetUriContext));
766     pkey = GNUNET_CRYPTO_rsa_public_key_to_string (&my_public_key);
767     GNUNET_asprintf (&guc->uri,
768                      "gnunet://hello/%s",
769                      pkey);
770     GNUNET_free (pkey);
771     GPI_plugins_load (cfg);
772     pic = GNUNET_PEERINFO_iterate (peerinfo, &my_peer_identity,
773                                    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5),
774                                    &print_my_uri, guc);
775     get_uri = GNUNET_NO;
776     return;
777   }
778   GNUNET_SCHEDULER_shutdown ();
779 }
780
781
782 /**
783  * The main function to obtain peer information.
784  *
785  * @param argc number of arguments from the command line
786  * @param argv command line arguments
787  * @return 0 ok, 1 on error
788  */
789 int
790 main (int argc, char *const *argv)
791 {
792   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
793     {'n', "numeric", NULL,
794      gettext_noop ("don't resolve host names"),
795      0, &GNUNET_GETOPT_set_one, &no_resolve},
796     {'q', "quiet", NULL,
797      gettext_noop ("output only the identity strings"),
798      0, &GNUNET_GETOPT_set_one, &be_quiet},
799     {'s', "self", NULL,
800      gettext_noop ("output our own identity only"),
801      0, &GNUNET_GETOPT_set_one, &get_self},
802     {'i', "info", NULL,
803      gettext_noop ("list all known peers"),
804      0, &GNUNET_GETOPT_set_one, &get_info},
805     {'g', "get-hello", NULL,
806      gettext_noop ("also output HELLO uri(s)"),
807      0, &GNUNET_GETOPT_set_one, &get_uri},
808     {'p', "put-hello", "HELLO",
809      gettext_noop ("add given HELLO uri to the database"),
810      1, &GNUNET_GETOPT_set_string, &put_uri},
811     GNUNET_GETOPT_OPTION_END
812   };
813   return (GNUNET_OK ==
814           GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
815                               gettext_noop ("Print information about peers."),
816                               options, &run, NULL)) ? 0 : 1;
817 }
818
819 /* end of gnunet-peerinfo.c */