- adding assertion for name
[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  * Prefix that every HELLO URI must start with.
38  */
39 #define HELLO_URI_PREFIX "gnunet://hello/"
40
41 /**
42  * Structure we use to collect printable address information.
43  */
44 struct PrintContext;
45
46 /**
47  * Record we keep for each printable address.
48  */
49 struct AddressRecord
50 {
51   /**
52    * Current address-to-string context (if active, otherwise NULL).
53    */
54   struct GNUNET_TRANSPORT_AddressToStringContext *atsc;
55
56   /**
57    * Printable address.
58    */
59   char *result;
60   
61   /**
62    * Print context this address record belongs to.
63    */
64   struct PrintContext *pc;
65 };
66
67
68 /**
69  * Structure we use to collect printable address information.
70  */
71 struct PrintContext
72 {
73
74   /**
75    * Kept in DLL.
76    */
77   struct PrintContext *next;
78
79   /**
80    * Kept in DLL.
81    */
82   struct PrintContext *prev;
83
84   /**
85    * Identity of the peer.
86    */
87   struct GNUNET_PeerIdentity peer;
88   
89   /**
90    * List of printable addresses.
91    */
92   struct AddressRecord *address_list;
93
94   /**
95    * Number of completed addresses in 'address_list'.
96    */
97   unsigned int num_addresses;
98
99   /**
100    * Number of addresses allocated in 'address_list'.
101    */
102   unsigned int address_list_size;
103
104   /**
105    * Current offset in 'address_list' (counted down).
106    */
107   unsigned int off;
108
109 };
110
111
112 /**
113  * Context used for building our own URI.
114  */
115 struct GetUriContext
116 {
117   /**
118    * Final URI.
119    */
120   char *uri;
121
122 };
123
124
125 /**
126  * Context for 'add_address_to_hello'.
127  */
128 struct GNUNET_PEERINFO_HelloAddressParsingContext
129 {
130   /**
131    * Position in the URI with the next address to parse.
132    */
133   const char *pos;
134
135   /**
136    * Set to GNUNET_SYSERR to indicate parse errors.
137    */
138   int ret;
139
140 };
141
142
143 /**
144  * Option '-n'
145  */
146 static int no_resolve;
147
148 /**
149  * Option '-q'
150  */
151 static int be_quiet;
152
153 /**
154  * Option '-s'
155  */
156 static int get_self;
157
158 /**
159  * Option 
160  */
161 static int get_uri;
162
163 /**
164  * Option '-i'
165  */
166 static int get_info;
167
168 /**
169  * Option 
170  */
171 static char *put_uri;
172
173 /**
174  * Handle to peerinfo service.
175  */
176 static struct GNUNET_PEERINFO_Handle *peerinfo;
177
178 /**
179  * Configuration handle.
180  */
181 static const struct GNUNET_CONFIGURATION_Handle *cfg;
182
183 /**
184  * Main state machine task (if active).
185  */
186 static GNUNET_SCHEDULER_TaskIdentifier tt;
187
188 /**
189  * Current iterator context (if active, otherwise NULL).
190  */
191 static struct GNUNET_PEERINFO_IteratorContext *pic;
192
193 /**
194  * My peer identity.
195  */
196 static struct GNUNET_PeerIdentity my_peer_identity;
197
198 /**
199  * My public key.
200  */
201 static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
202
203 /**
204  * Head of list of print contexts.
205  */
206 static struct PrintContext *pc_head;
207
208 /**
209  * Tail of list of print contexts.
210  */
211 static struct PrintContext *pc_tail;
212
213
214 /**
215  * Main state machine that goes over all options and
216  * runs the next requested function.
217  *
218  * @param cls unused
219  * @param tc unused
220  */
221 static void
222 state_machine (void *cls,
223                const struct GNUNET_SCHEDULER_TaskContext *tc);
224
225
226 /* ********************* 'get_info' ******************* */
227
228 /**
229  * Print the collected address information to the console and free 'pc'.
230  *
231  * @param pc printing context
232  */
233 static void
234 dump_pc (struct PrintContext *pc)
235 {
236   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
237   unsigned int i;
238
239   GNUNET_CRYPTO_hash_to_enc (&pc->peer.hashPubKey, &enc);
240   printf (_("Peer `%s'\n"), 
241           (const char *) &enc);
242   for (i = 0; i < pc->num_addresses; i++)
243   {
244     if (NULL != pc->address_list[i].result)
245     {
246       printf ("\t%s\n", pc->address_list[i].result);
247       GNUNET_free (pc->address_list[i].result);
248     }
249   }
250   printf ("\n");
251   GNUNET_free_non_null (pc->address_list);
252   GNUNET_CONTAINER_DLL_remove (pc_head,
253                                pc_tail,
254                                pc);
255   GNUNET_free (pc);
256   if ( (NULL == pc_head) &&
257        (NULL == pic) )
258     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);  
259 }
260
261
262 /* ************************* list all known addresses **************** */
263
264
265 /**
266  * Function to call with a human-readable format of an address
267  *
268  * @param cls closure
269  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
270  */
271 static void
272 process_resolved_address (void *cls, const char *address)
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   pc->num_addresses++;
285   if (pc->num_addresses == pc->address_list_size)
286     dump_pc (pc);
287 }
288
289
290 /**
291  * Iterator callback to go over all addresses and count them.
292  *
293  * @param cls 'struct PrintContext' with 'off' to increment
294  * @param address the address
295  * @param expiration expiration time
296  * @return GNUNET_OK to keep the address and continue
297  */
298 static int
299 count_address (void *cls, const struct GNUNET_HELLO_Address *address,
300                struct GNUNET_TIME_Absolute expiration)
301 {
302   struct PrintContext *pc = cls;
303
304   pc->off++;
305   return GNUNET_OK;
306 }
307
308
309 /**
310  * Iterator callback to go over all addresses.
311  *
312  * @param cls closure
313  * @param address the address
314  * @param expiration expiration time
315  * @return GNUNET_OK to keep the address and continue
316  */
317 static int
318 print_address (void *cls, const struct GNUNET_HELLO_Address *address,
319                struct GNUNET_TIME_Absolute expiration)
320 {
321   struct PrintContext *pc = cls;
322   struct AddressRecord *ar;
323
324   GNUNET_assert (0 < pc->off);
325   ar = &pc->address_list[--pc->off];
326   ar->pc = pc;
327   ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg, address, no_resolve,
328                                                  GNUNET_TIME_relative_multiply
329                                                  (GNUNET_TIME_UNIT_SECONDS, 10),
330                                                  &process_resolved_address, ar);
331   return GNUNET_OK;
332 }
333
334
335 /**
336  * Print information about the peer.
337  * Currently prints the GNUNET_PeerIdentity and the transport address.
338  *
339  * @param cls the 'struct PrintContext'
340  * @param peer identity of the peer 
341  * @param hello addresses of the peer
342  * @param err_msg error message
343  */
344 static void
345 print_peer_info (void *cls, const struct GNUNET_PeerIdentity *peer,
346                  const struct GNUNET_HELLO_Message *hello, const char *err_msg)
347 {
348   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
349   struct PrintContext *pc;
350
351   if (peer == NULL)
352   {
353     pic = NULL; /* end of iteration */
354     if (err_msg != NULL)
355     {
356       FPRINTF (stderr, 
357                _("Error in communication with PEERINFO service: %s\n"),
358                err_msg);
359     }
360     if (NULL == pc_head)
361       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
362     return;
363   }
364   if ((GNUNET_YES == be_quiet) || (NULL == hello))
365   {
366     GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc);
367     printf ("%s\n", (const char *) &enc);
368     return;
369   }
370   pc = GNUNET_malloc (sizeof (struct PrintContext));
371   GNUNET_CONTAINER_DLL_insert (pc_head,
372                                pc_tail, 
373                                pc);
374   pc->peer = *peer;
375   GNUNET_HELLO_iterate_addresses (hello, 
376                                   GNUNET_NO, 
377                                   &count_address, 
378                                   pc);
379   if (0 == pc->off)
380   {
381     dump_pc (pc);
382     return;
383   }
384   pc->address_list_size = pc->off;
385   pc->address_list = GNUNET_malloc (sizeof (struct AddressRecord) * pc->off);
386   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, 
387                                   &print_address, pc);
388 }
389
390
391 /* ************************* GET URI ************************** */
392
393
394 /**
395  * Function that is called on each address of this peer.
396  * Expands the corresponding URI string.
397  *
398  * @param cls the 'GetUriContext'
399  * @param address address to add
400  * @param expiration expiration time for the address
401  * @return GNUNET_OK (continue iteration).
402  */
403 static int
404 compose_uri (void *cls, const struct GNUNET_HELLO_Address *address,
405              struct GNUNET_TIME_Absolute expiration)
406 {
407   struct GetUriContext *guc = cls;
408   struct GNUNET_TRANSPORT_PluginFunctions *papi;
409   const char *addr;
410   char *ret;
411   char tbuf[16];
412   struct tm *t;
413   time_t seconds;
414
415   papi = GPI_plugins_find (address->transport_name);
416   if (papi == NULL)
417   {
418     /* Not an error - we might just not have the right plugin. */
419     return GNUNET_OK;
420   }
421   if (NULL == papi->address_to_string)
422   {
423     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
424                 "URI conversion not implemented for plugin `%s'\n",
425                 address->transport_name);
426     return GNUNET_OK;
427   }
428   addr = papi->address_to_string (papi->cls, address->address, address->address_length);
429   if ( (addr == NULL) || (strlen(addr) == 0) )
430     return GNUNET_OK;
431   seconds = expiration.abs_value / 1000;
432   t = gmtime (&seconds);
433   GNUNET_assert (0 != strftime (tbuf, sizeof (tbuf),
434                                 "%Y%m%d%H%M%S",
435                                 t));
436   GNUNET_asprintf (&ret,
437                    "%s!%s!%s!%s",
438                    guc->uri,
439                    tbuf,
440                    address->transport_name, 
441                    addr);
442   GNUNET_free (guc->uri);
443   guc->uri = ret;
444   return GNUNET_OK;
445 }
446
447
448 /**
449  * Print URI of the peer.
450  *
451  * @param cls the 'struct GetUriContext'
452  * @param peer identity of the peer (unused)
453  * @param hello addresses of the peer
454  * @param err_msg error message
455  */
456 static void
457 print_my_uri (void *cls, const struct GNUNET_PeerIdentity *peer,
458               const struct GNUNET_HELLO_Message *hello, 
459               const char *err_msg)
460 {
461   struct GetUriContext *guc = cls;
462
463   if (peer == NULL)
464   {
465     pic = NULL;
466     if (err_msg != NULL)
467       FPRINTF (stderr,
468                _("Error in communication with PEERINFO service: %s\n"), 
469                err_msg);
470     GNUNET_free_non_null (guc->uri);
471     GNUNET_free (guc);  
472     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
473     return;
474   } 
475   if (NULL != hello)
476     GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &compose_uri, guc);   
477   printf ("%s\n", (const char *) guc->uri);
478 }
479
480
481 /* ************************* import HELLO by URI ********************* */
482
483
484 /**
485  * We're building a HELLO.  Parse the next address from the
486  * parsing context and append it.
487  *
488  * @param cls the 'struct GNUNET_PEERINFO_HelloAddressParsingContext'
489  * @param max number of bytes available for HELLO construction
490  * @param buffer where to copy the next address (in binary format)
491  * @return number of bytes added to buffer
492  */ 
493 static size_t
494 add_address_to_hello (void *cls, size_t max, void *buffer)
495 {
496   struct GNUNET_PEERINFO_HelloAddressParsingContext *ctx = cls;
497   const char *tname;
498   const char *address;
499   const char *end;
500   char *plugin_name;
501   struct tm expiration_time;
502   time_t expiration_seconds;
503   struct GNUNET_TIME_Absolute expire;
504   struct GNUNET_TRANSPORT_PluginFunctions *papi;
505   void *addr;
506   size_t addr_len;
507   struct GNUNET_HELLO_Address haddr;
508   size_t ret;
509
510   if (NULL == ctx->pos)
511     return 0;
512   if ('!' != ctx->pos[0])
513   {
514     ctx->ret = GNUNET_SYSERR;
515     return 0;
516   }
517   ctx->pos++;
518   memset (&expiration_time, 0, sizeof (expiration_time));
519   tname = strptime (ctx->pos,
520                     "%Y%m%d%H%M%S",
521                     &expiration_time);
522   if (NULL == tname)
523   {
524     ctx->ret = GNUNET_SYSERR;
525     return 0;
526   }
527   expiration_seconds = mktime (&expiration_time);
528   if (expiration_seconds == (time_t) -1)
529   {
530     ctx->ret = GNUNET_SYSERR;
531     return 0;
532   }
533   expire.abs_value = expiration_seconds * 1000;
534   if ('!' != tname[0])
535   {
536     ctx->ret = GNUNET_SYSERR;
537     return 0;
538   }
539   tname++;
540   address = strchr (tname, (int) '!');
541   if (NULL == address)
542   {
543     ctx->ret = GNUNET_SYSERR;
544     return 0;
545   }
546   address++;
547   end = strchr (address, (int) '!');
548   if (NULL == end)
549   {
550     ctx->pos = NULL;
551     end = address + strlen (address);
552   }
553   else
554   {
555     ctx->pos = end;
556   }
557   plugin_name = GNUNET_strndup (tname, address - tname);  
558   papi = GPI_plugins_find (plugin_name);
559   if (NULL == papi)
560   {
561     /* Not an error - we might just not have the right plugin.
562      * Skip this part, advance to the next one and recurse.
563      * But only if this is not the end of string.
564      */
565     GNUNET_free (plugin_name);
566     return 0;
567   }
568   if (NULL == papi->string_to_address)
569   {
570     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
571                 _("Plugin `%s' does not support URIs yet\n"),
572                 ctx->pos);
573     GNUNET_free (plugin_name);
574     return 0;
575   }
576   if (GNUNET_OK !=
577       papi->string_to_address (papi->cls, 
578                                address,
579                                end - address,
580                                &addr,
581                                &addr_len))
582   {
583     GNUNET_free (plugin_name);  
584     return GNUNET_SYSERR;
585   }
586   /* address.peer is unset - not used by add_address() */
587   haddr.address_length = addr_len;
588   haddr.address = addr;
589   haddr.transport_name = plugin_name;
590   ret = GNUNET_HELLO_add_address (&haddr, expire, buffer, max);
591   GNUNET_free (addr);
592   GNUNET_free (plugin_name);
593   return ret;
594 }
595
596
597 /**
598  * Parse the PUT URI given at the command line and add it to our peerinfo 
599  * database.
600  *
601  * @param put_uri URI string to parse
602  * @return GNUNET_OK on success, GNUNET_SYSERR if the URI was invalid, GNUNET_NO on other errors
603  */
604 static int
605 parse_hello_uri (const char *put_uri)
606 {
607   const char *pks;
608   const char *exc;
609   struct GNUNET_HELLO_Message *hello;
610   struct GNUNET_PEERINFO_HelloAddressParsingContext ctx;
611
612   if (0 != strncmp (put_uri,
613                     HELLO_URI_PREFIX,
614                     strlen (HELLO_URI_PREFIX)))
615     return GNUNET_SYSERR;
616   pks = &put_uri[strlen (HELLO_URI_PREFIX)];
617   exc = strstr (pks, "!");
618   if (GNUNET_OK != GNUNET_STRINGS_string_to_data (pks,
619                                                   (NULL == exc) ? strlen (pks) : (exc - pks),
620                                                   (unsigned char *) &my_public_key, 
621                                                   sizeof (my_public_key)))
622     return GNUNET_SYSERR;
623   ctx.pos = exc;
624   ctx.ret = GNUNET_OK;
625   hello = GNUNET_HELLO_create (&my_public_key, &add_address_to_hello, &ctx);
626
627   if (NULL != hello)
628   {
629     /* WARNING: this adds the address from URI WITHOUT verification! */
630     if (GNUNET_OK == ctx.ret)
631       GNUNET_PEERINFO_add_peer (peerinfo, hello);
632     GNUNET_free (hello);
633   }
634
635   /* wait 1s to give peerinfo operation a chance to succeed */
636   /* FIXME: current peerinfo API sucks to require this; not to mention
637      that we get no feedback to determine if the operation actually succeeded */
638   tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
639                                      &state_machine, NULL);
640   return ctx.ret;
641 }
642
643
644 /* ************************ Main state machine ********************* */
645
646
647 /**
648  * Main state machine that goes over all options and
649  * runs the next requested function.
650  *
651  * @param cls unused
652  * @param tc scheduler context
653  */
654 static void
655 shutdown_task (void *cls,
656                const struct GNUNET_SCHEDULER_TaskContext *tc)
657 {
658   struct PrintContext *pc;
659   struct AddressRecord *ar;
660   unsigned int i;
661
662   if (GNUNET_SCHEDULER_NO_TASK != tt)
663   {
664     GNUNET_SCHEDULER_cancel (tt);
665     tt = GNUNET_SCHEDULER_NO_TASK;
666   }
667   if (NULL != pic)
668   {
669     GNUNET_PEERINFO_iterate_cancel (pic);
670     pic = NULL;
671   }
672   while (NULL != (pc = pc_head))
673   {
674     GNUNET_CONTAINER_DLL_remove (pc_head,
675                                  pc_tail,
676                                  pc);
677     for (i=0;i<pc->address_list_size;i++)
678     {
679       ar = &pc->address_list[i];
680       GNUNET_free_non_null (ar->result);
681       if (NULL != ar->atsc)
682       {
683         GNUNET_TRANSPORT_address_to_string_cancel (ar->atsc);
684         ar->atsc = NULL;
685       }
686     }
687     GNUNET_free_non_null (pc->address_list);
688     GNUNET_free (pc);
689   }
690   GPI_plugins_unload ();
691   if (NULL != peerinfo)
692   {
693     GNUNET_PEERINFO_disconnect (peerinfo);
694     peerinfo = NULL;
695   }
696 }
697
698
699 /**
700  * Main function that will be run by the scheduler.
701  *
702  * @param cls closure
703  * @param args remaining command-line arguments
704  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
705  * @param c configuration
706  */
707 static void
708 run (void *cls, char *const *args, const char *cfgfile,
709      const struct GNUNET_CONFIGURATION_Handle *c)
710 {
711   struct GNUNET_CRYPTO_RsaPrivateKey *priv;
712   char *fn;
713
714   cfg = c;
715   if (args[0] != NULL)
716   {
717     FPRINTF (stderr, _("Invalid command line argument `%s'\n"), args[0]);
718     return;
719   }
720   peerinfo = GNUNET_PEERINFO_connect (cfg);
721   if (peerinfo == NULL)
722   {
723     FPRINTF (stderr, "%s",  _("Could not access PEERINFO service.  Exiting.\n"));
724     return;
725   }
726   if ( (GNUNET_YES == get_self) || (GNUNET_YES == get_uri) )
727   {
728     /* load private key */
729     if (GNUNET_OK !=
730         GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
731                                                  &fn))
732     {
733       FPRINTF (stderr, _("Could not find option `%s:%s' in configuration.\n"),
734                "GNUNETD", "HOSTKEYFILE");
735       return;
736     }
737
738     if (NULL == (priv = GNUNET_CRYPTO_rsa_key_create_from_file (fn)))
739     {
740       FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
741       GNUNET_free (fn);
742       return;
743     }
744     GNUNET_free (fn);
745     GNUNET_CRYPTO_rsa_key_get_public (priv, &my_public_key);
746     GNUNET_CRYPTO_rsa_key_free (priv);
747     GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &my_peer_identity.hashPubKey);
748   }
749
750   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
751   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
752                                 &shutdown_task,
753                                 NULL);
754 }
755
756
757 /**
758  * Main state machine that goes over all options and
759  * runs the next requested function.
760  *
761  * @param cls unused
762  * @param tc scheduler context
763  */
764 static void
765 state_machine (void *cls,
766                const struct GNUNET_SCHEDULER_TaskContext *tc)
767 {
768   tt = GNUNET_SCHEDULER_NO_TASK;
769
770   if (NULL != put_uri)
771   {
772     GPI_plugins_load (cfg);
773     if (GNUNET_SYSERR == parse_hello_uri (put_uri))
774       fprintf (stderr,
775                _("Invalid URI `%s'\n"),
776                put_uri);    
777     GNUNET_free (put_uri);
778     put_uri = NULL;
779     return;
780   }
781   if (GNUNET_YES == get_info)
782   {
783     get_info = GNUNET_NO;
784     GPI_plugins_load (cfg);
785     pic = GNUNET_PEERINFO_iterate (peerinfo, NULL,
786                                    GNUNET_TIME_UNIT_FOREVER_REL, 
787                                    &print_peer_info, NULL);
788     return;
789   }
790   if (GNUNET_YES == get_self)
791   {
792     struct GNUNET_CRYPTO_HashAsciiEncoded enc;
793
794     get_self = GNUNET_NO;
795     GNUNET_CRYPTO_hash_to_enc (&my_peer_identity.hashPubKey, &enc);
796     if (be_quiet)
797       printf ("%s\n", (char *) &enc);
798     else
799       printf (_("I am peer `%s'.\n"), (const char *) &enc);
800   }
801   if (GNUNET_YES == get_uri)
802   {
803     struct GetUriContext *guc;
804     char *pkey;
805
806     guc = GNUNET_malloc (sizeof (struct GetUriContext));
807     pkey = GNUNET_CRYPTO_rsa_public_key_to_string (&my_public_key);
808     GNUNET_asprintf (&guc->uri,
809                      "%s%s",
810                      HELLO_URI_PREFIX,
811                      pkey);
812     GNUNET_free (pkey);
813     GPI_plugins_load (cfg);
814     pic = GNUNET_PEERINFO_iterate (peerinfo, &my_peer_identity,
815                                    GNUNET_TIME_UNIT_FOREVER_REL,
816                                    &print_my_uri, guc);
817     get_uri = GNUNET_NO;
818     return;
819   }
820   GNUNET_SCHEDULER_shutdown ();
821 }
822
823
824 /**
825  * The main function to obtain peer information.
826  *
827  * @param argc number of arguments from the command line
828  * @param argv command line arguments
829  * @return 0 ok, 1 on error
830  */
831 int
832 main (int argc, char *const *argv)
833 {
834   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
835     {'n', "numeric", NULL,
836      gettext_noop ("don't resolve host names"),
837      0, &GNUNET_GETOPT_set_one, &no_resolve},
838     {'q', "quiet", NULL,
839      gettext_noop ("output only the identity strings"),
840      0, &GNUNET_GETOPT_set_one, &be_quiet},
841     {'s', "self", NULL,
842      gettext_noop ("output our own identity only"),
843      0, &GNUNET_GETOPT_set_one, &get_self},
844     {'i', "info", NULL,
845      gettext_noop ("list all known peers"),
846      0, &GNUNET_GETOPT_set_one, &get_info},
847     {'g', "get-hello", NULL,
848      gettext_noop ("also output HELLO uri(s)"),
849      0, &GNUNET_GETOPT_set_one, &get_uri},
850     {'p', "put-hello", "HELLO",
851      gettext_noop ("add given HELLO uri to the database"),
852      1, &GNUNET_GETOPT_set_string, &put_uri},
853     GNUNET_GETOPT_OPTION_END
854   };
855   return (GNUNET_OK ==
856           GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
857                               gettext_noop ("Print information about peers."),
858                               options, &run, NULL)) ? 0 : 1;
859 }
860
861 /* end of gnunet-peerinfo.c */