2a7b785ee786376a7fb3c16fa9a234d71dd7ccf8
[oweals/gnunet.git] / src / peerinfo-tool / gnunet-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-2014, 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file peerinfo-tool/gnunet-peerinfo.c
21  * @brief Print information about other known peers.
22  * @author Christian Grothoff
23  * @author Matthias Wachs
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_hello_lib.h"
28 #include "gnunet_transport_service.h"
29 #include "gnunet_transport_hello_service.h"
30 #include "gnunet_peerinfo_service.h"
31 #include "gnunet-peerinfo_plugins.h"
32
33 /**
34  * How long until we time out during address lookup?
35  */
36 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
37
38 /**
39  * Structure we use to collect printable address information.
40  */
41 struct PrintContext;
42
43 /**
44  * Record we keep for each printable address.
45  */
46 struct AddressRecord
47 {
48   /**
49    * Current address-to-string context (if active, otherwise NULL).
50    */
51   struct GNUNET_TRANSPORT_AddressToStringContext *atsc;
52
53   /**
54    * Address expiration time
55    */
56   struct GNUNET_TIME_Absolute expiration;
57
58   /**
59    * Printable address.
60    */
61   char *result;
62
63   /**
64    * Print context this address record belongs to.
65    */
66   struct PrintContext *pc;
67 };
68
69
70 /**
71  * Structure we use to collect printable address information.
72  */
73 struct PrintContext
74 {
75
76   /**
77    * Kept in DLL.
78    */
79   struct PrintContext *next;
80
81   /**
82    * Kept in DLL.
83    */
84   struct PrintContext *prev;
85
86   /**
87    * Identity of the peer.
88    */
89   struct GNUNET_PeerIdentity peer;
90
91   /**
92    * List of printable addresses.
93    */
94   struct AddressRecord *address_list;
95
96   /**
97    * Number of completed addresses in @e address_list.
98    */
99   unsigned int num_addresses;
100
101   /**
102    * Number of addresses allocated in @e address_list.
103    */
104   unsigned int address_list_size;
105
106   /**
107    * Current offset in @e address_list (counted down).
108    */
109   unsigned int off;
110
111   /**
112    * Hello was friend only, #GNUNET_YES or #GNUNET_NO
113    */
114   int friend_only;
115
116 };
117
118
119 /**
120  * Option '-n'
121  */
122 static int no_resolve;
123
124 /**
125  * Option '-q'
126  */
127 static int be_quiet;
128
129 /**
130  * Option '-f'
131  */
132 static int include_friend_only;
133
134 /**
135  * Option '-s'
136  */
137 static int get_self;
138
139 /**
140  * Option
141  */
142 static int get_uri;
143
144 /**
145  * Option
146  */
147 static int default_operation;
148
149 /**
150  * Option '-i'
151  */
152 static int get_info;
153
154 /**
155  * Option
156  */
157 static char *put_uri;
158
159 /**
160  * Option -d
161  */
162 static char *dump_hello;
163
164 /**
165  * Handle to peerinfo service.
166  */
167 static struct GNUNET_PEERINFO_Handle *peerinfo;
168
169 /**
170  * Configuration handle.
171  */
172 static const struct GNUNET_CONFIGURATION_Handle *cfg;
173
174 /**
175  * Main state machine task (if active).
176  */
177 static struct GNUNET_SCHEDULER_Task *tt;
178
179 /**
180  * Pending #GNUNET_TRANSPORT_hello_get() operation.
181  */
182 static struct GNUNET_TRANSPORT_HelloGetHandle *gh;
183
184 /**
185  * Current iterator context (if active, otherwise NULL).
186  */
187 static struct GNUNET_PEERINFO_IteratorContext *pic;
188
189 /**
190  * My peer identity.
191  */
192 static struct GNUNET_PeerIdentity my_peer_identity;
193
194 /**
195  * Head of list of print contexts.
196  */
197 static struct PrintContext *pc_head;
198
199 /**
200  * Tail of list of print contexts.
201  */
202 static struct PrintContext *pc_tail;
203
204 /**
205  * Handle to current #GNUNET_PEERINFO_add_peer() operation.
206  */
207 static struct GNUNET_MQ_Envelope *ac;
208
209 /**
210  * Hello of this peer (if initialized).
211  */
212 static struct GNUNET_HELLO_Message *my_hello;
213
214
215 /**
216  * Main state machine that goes over all options and
217  * runs the next requested function.
218  *
219  * @param cls unused
220  */
221 static void
222 state_machine (void *cls);
223
224
225 /* ********************* 'get_info' ******************* */
226
227 /**
228  * Print the collected address information to the console and free @a pc.
229  *
230  * @param pc printing context
231  */
232 static void
233 dump_pc (struct PrintContext *pc)
234 {
235   unsigned int i;
236
237   printf (_("%sPeer `%s'\n"),
238           (GNUNET_YES == pc->friend_only) ? "F2F: " : "",
239           GNUNET_i2s_full (&pc->peer));
240   for (i = 0; i < pc->num_addresses; i++)
241   {
242     if (NULL != pc->address_list[i].result)
243     {
244       printf (_("\tExpires: %s \t %s\n"),
245               GNUNET_STRINGS_absolute_time_to_string (pc->address_list[i].expiration),
246               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,
259                                    NULL);
260 }
261
262
263 /* ************************* list all known addresses **************** */
264
265
266 /**
267  * Function to call with a human-readable format of an address
268  *
269  * @param cls closure
270  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
271  * @param res result of the address to string conversion:
272  *        if #GNUNET_OK: address was valid (conversion to
273  *                       string might still have failed)
274  *        if #GNUNET_SYSERR: address is invalid
275  */
276 static void
277 process_resolved_address (void *cls,
278                           const char *address,
279                           int res)
280 {
281   struct AddressRecord *ar = cls;
282   struct PrintContext *pc = ar->pc;
283
284   if (NULL != address)
285   {
286     if (0 != strlen (address))
287     {
288       if (NULL != ar->result)
289         GNUNET_free (ar->result);
290       ar->result = GNUNET_strdup (address);
291     }
292     return;
293   }
294   ar->atsc = NULL;
295   if (GNUNET_SYSERR == res)
296     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
297                 _("Failure: Cannot convert address to string for peer `%s'\n"),
298                 GNUNET_i2s (&ar->pc->peer));
299   pc->num_addresses++;
300   if (pc->num_addresses == pc->address_list_size)
301     dump_pc (pc);
302 }
303
304
305 /**
306  * Iterator callback to go over all addresses and count them.
307  *
308  * @param cls `struct PrintContext *` with `off` to increment
309  * @param address the address
310  * @param expiration expiration time
311  * @return #GNUNET_OK to keep the address and continue
312  */
313 static int
314 count_address (void *cls,
315                const struct GNUNET_HELLO_Address *address,
316                struct GNUNET_TIME_Absolute expiration)
317 {
318   struct PrintContext *pc = cls;
319
320   pc->off++;
321   return GNUNET_OK;
322 }
323
324
325 /**
326  * Iterator callback to go over all addresses.
327  *
328  * @param cls closure
329  * @param address the address
330  * @param expiration expiration time
331  * @return #GNUNET_OK to keep the address and continue
332  */
333 static int
334 print_address (void *cls,
335                const struct GNUNET_HELLO_Address *address,
336                struct GNUNET_TIME_Absolute expiration)
337 {
338   struct PrintContext *pc = cls;
339   struct AddressRecord *ar;
340
341   GNUNET_assert (0 < pc->off);
342   ar = &pc->address_list[--pc->off];
343   ar->pc = pc;
344   ar->expiration = expiration;
345   GNUNET_asprintf (&ar->result,
346                    "%s:%u:%u",
347                    address->transport_name,
348                    address->address_length,
349                    address->local_info);
350   ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg,
351                                                  address,
352                                                  no_resolve,
353                                                  TIMEOUT,
354                                                  &process_resolved_address, ar);
355   return GNUNET_OK;
356 }
357
358
359 /**
360  * Print information about the peer.  Currently prints the `struct
361  * GNUNET_PeerIdentity` and the transport address.
362  *
363  * @param cls the `struct PrintContext *`
364  * @param peer identity of the peer
365  * @param hello addresses of the peer
366  * @param err_msg error message
367  */
368 static void
369 print_peer_info (void *cls,
370                  const struct GNUNET_PeerIdentity *peer,
371                  const struct GNUNET_HELLO_Message *hello,
372                  const char *err_msg)
373 {
374   struct PrintContext *pc;
375   int friend_only;
376
377   if (NULL == peer)
378   {
379     pic = NULL; /* end of iteration */
380     if (NULL != err_msg)
381     {
382       FPRINTF (stderr,
383                _("Error in communication with PEERINFO service: %s\n"),
384                err_msg);
385     }
386     if (NULL == pc_head)
387       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
388     return;
389   }
390   friend_only = GNUNET_NO;
391   if (NULL != hello)
392         friend_only = GNUNET_HELLO_is_friend_only (hello);
393   if ( (GNUNET_YES == be_quiet) ||
394        (NULL == hello) )
395   {
396     printf ("%s%s\n",
397             (GNUNET_YES == friend_only) ? "F2F: " : "",
398             GNUNET_i2s_full (peer));
399     return;
400   }
401   pc = GNUNET_new (struct PrintContext);
402   GNUNET_CONTAINER_DLL_insert (pc_head,
403                                pc_tail,
404                                pc);
405   pc->peer = *peer;
406   pc->friend_only = friend_only;
407   GNUNET_HELLO_iterate_addresses (hello,
408                                   GNUNET_NO,
409                                   &count_address,
410                                   pc);
411   if (0 == pc->off)
412   {
413     dump_pc (pc);
414     return;
415   }
416   pc->address_list_size = pc->off;
417   pc->address_list = GNUNET_malloc (sizeof (struct AddressRecord) * pc->off);
418   GNUNET_HELLO_iterate_addresses (hello,
419                                   GNUNET_NO,
420                                   &print_address,
421                                   pc);
422 }
423
424 /* ************************* DUMP Hello  ************************** */
425
426 /**
427  * Count the number of addresses in the HELLO.
428  *
429  * @param cls pointer to an `int *` used for the counter
430  * @param address an address to count
431  * @param expiration (unused)
432  * @return #GNUNET_OK
433  */
434 static int
435 count_addr (void *cls,
436             const struct GNUNET_HELLO_Address *address,
437             struct GNUNET_TIME_Absolute expiration)
438 {
439   int *c = cls;
440
441   (*c)++;
442   return GNUNET_OK;
443 }
444
445
446 /**
447  * Write HELLO of my peer to a file.
448  *
449  * @param cls the `struct GetUriContext *`
450  * @param peer identity of the peer (unused)
451  * @param hello addresses of the peer
452  * @param err_msg error message
453  */
454 static void
455 dump_my_hello ()
456 {
457   unsigned int size;
458   unsigned int c_addr;
459
460   size = GNUNET_HELLO_size (my_hello);
461   if (0 == size)
462   {
463     FPRINTF (stderr,
464              _("Failure: Received invalid %s\n"),
465              "HELLO");
466     return;
467   }
468   if (GNUNET_SYSERR ==
469       GNUNET_DISK_fn_write (dump_hello,
470                             my_hello,
471                             size,
472                             GNUNET_DISK_PERM_USER_READ |
473                             GNUNET_DISK_PERM_USER_WRITE |
474                             GNUNET_DISK_PERM_GROUP_READ |
475                             GNUNET_DISK_PERM_OTHER_READ))
476   {
477     FPRINTF (stderr,
478              _("Failed to write HELLO with %u bytes to file `%s'\n"),
479              size,
480              dump_hello);
481     if (0 != UNLINK (dump_hello))
482       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
483                                 GNUNET_ERROR_TYPE_BULK,
484                                 "unlink",
485                                 dump_hello);
486
487   }
488   c_addr = 0;
489   GNUNET_HELLO_iterate_addresses (my_hello,
490                                   GNUNET_NO,
491                                   count_addr,
492                                   &c_addr);
493
494   if (! be_quiet)
495   {
496     FPRINTF (stderr,
497              _("Wrote %s HELLO containing %u addresses with %u bytes to file `%s'\n"),
498              (GNUNET_YES == GNUNET_HELLO_is_friend_only (my_hello)) ? "friend-only": "public",
499              c_addr,
500              size,
501              dump_hello);
502   }
503   GNUNET_free (dump_hello);
504   dump_hello = NULL;
505 }
506
507
508 /* ************************* GET URI ************************** */
509
510
511 /**
512  * Print URI of the peer.
513  *
514  * @param cls the `struct GetUriContext *`
515  * @param peer identity of the peer (unused)
516  * @param hello addresses of the peer
517  * @param err_msg error message
518  */
519 static void
520 print_my_uri (void *cls,
521               const struct GNUNET_PeerIdentity *peer,
522               const struct GNUNET_HELLO_Message *hello,
523               const char *err_msg)
524 {
525   char *uri;
526
527   if (NULL == peer)
528   {
529     pic = NULL;
530     if (NULL != err_msg)
531       FPRINTF (stderr,
532                _("Error in communication with PEERINFO service: %s\n"),
533                err_msg);
534     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
535     return;
536   }
537
538   if (NULL == hello)
539     return;
540   uri = GNUNET_HELLO_compose_uri (hello,
541                                   &GPI_plugins_find);
542   if (NULL != uri)
543   {
544     printf ("%s\n",
545             (const char *) uri);
546     GNUNET_free (uri);
547   }
548 }
549
550
551 /* ************************* import HELLO by URI ********************* */
552
553
554 /**
555  * Continuation called from #GNUNET_PEERINFO_add_peer()
556  *
557  * @param cls closure, NULL
558  */
559 static void
560 add_continuation (void *cls)
561 {
562   ac = NULL;
563   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
564 }
565
566
567 /**
568  * Parse the PUT URI given at the command line and add it to our peerinfo
569  * database.
570  *
571  * @param put_uri URI string to parse
572  * @return #GNUNET_OK on success,
573  *         #GNUNET_SYSERR if the URI was invalid,
574  *         #GNUNET_NO on other errors
575  */
576 static int
577 parse_hello_uri (const char *put_uri)
578 {
579   struct GNUNET_HELLO_Message *hello = NULL;
580
581   int ret = GNUNET_HELLO_parse_uri (put_uri,
582                                     &my_peer_identity.public_key,
583                                     &hello,
584                                     &GPI_plugins_find);
585
586   if (NULL != hello)
587   {
588     /* WARNING: this adds the address from URI WITHOUT verification! */
589     if (GNUNET_OK == ret)
590       ac = GNUNET_PEERINFO_add_peer (peerinfo,
591                                      hello,
592                                      &add_continuation,
593                                      NULL);
594     else
595       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
596     GNUNET_free (hello);
597   }
598   return ret;
599 }
600
601
602 /* ************************ Main state machine ********************* */
603
604
605 /**
606  * Main state machine that goes over all options and
607  * runs the next requested function.
608  *
609  * @param cls unused
610  */
611 static void
612 shutdown_task (void *cls)
613 {
614   struct PrintContext *pc;
615   struct AddressRecord *ar;
616   unsigned int i;
617
618   if (NULL != ac)
619   {
620     GNUNET_MQ_send_cancel (ac);
621     ac = NULL;
622   }
623   if (NULL != tt)
624   {
625     GNUNET_SCHEDULER_cancel (tt);
626     tt = NULL;
627   }
628   if (NULL != pic)
629   {
630     GNUNET_PEERINFO_iterate_cancel (pic);
631     pic = NULL;
632   }
633   if (NULL != gh)
634   {
635     GNUNET_TRANSPORT_hello_get_cancel (gh);
636     gh = NULL;
637   }
638   while (NULL != (pc = pc_head))
639   {
640     GNUNET_CONTAINER_DLL_remove (pc_head,
641                                  pc_tail,
642                                  pc);
643     for (i=0;i<pc->address_list_size;i++)
644     {
645       ar = &pc->address_list[i];
646       GNUNET_free_non_null (ar->result);
647       if (NULL != ar->atsc)
648       {
649         GNUNET_TRANSPORT_address_to_string_cancel (ar->atsc);
650         ar->atsc = NULL;
651       }
652     }
653     GNUNET_free_non_null (pc->address_list);
654     GNUNET_free (pc);
655   }
656   GPI_plugins_unload ();
657   if (NULL != peerinfo)
658   {
659     GNUNET_PEERINFO_disconnect (peerinfo);
660     peerinfo = NULL;
661   }
662   if (NULL != my_hello)
663   {
664     GNUNET_free (my_hello);
665     my_hello = NULL;
666   }
667 }
668
669
670 /**
671  * Function called with our peer's HELLO message.
672  * Used to obtain our peer's public key.
673  *
674  * @param cls NULL
675  * @param hello the HELLO message
676  */
677 static void
678 hello_callback (void *cls,
679                 const struct GNUNET_MessageHeader *hello)
680 {
681   if (NULL == hello)
682   {
683     fprintf (stderr,
684              "Failed to get my own HELLO from this peer!\n");
685     GNUNET_SCHEDULER_shutdown ();
686     return;
687   }
688   my_hello = (struct GNUNET_HELLO_Message *) GNUNET_copy_message (hello);
689   GNUNET_assert (GNUNET_OK ==
690                  GNUNET_HELLO_get_id (my_hello,
691                                       &my_peer_identity));
692   GNUNET_TRANSPORT_hello_get_cancel (gh);
693   gh = NULL;
694   if (NULL != dump_hello)
695     dump_my_hello ();
696   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
697 }
698
699
700 /**
701  * Main function that will be run by the scheduler.
702  *
703  * @param cls closure
704  * @param args remaining command-line arguments
705  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
706  * @param c configuration
707  */
708 static void
709 run (void *cls,
710      char *const *args,
711      const char *cfgfile,
712      const struct GNUNET_CONFIGURATION_Handle *c)
713 {
714   cfg = c;
715   if ( (NULL != args[0]) &&
716        (NULL == put_uri) &&
717        (args[0] == strcasestr (args[0],
718                                "gnunet://hello/")) )
719   {
720     put_uri = GNUNET_strdup (args[0]);
721     args++;
722   }
723   if (NULL != args[0])
724   {
725     FPRINTF (stderr,
726              _("Invalid command line argument `%s'\n"),
727              args[0]);
728     return;
729   }
730   if (NULL == (peerinfo = GNUNET_PEERINFO_connect (cfg)))
731   {
732     FPRINTF (stderr,
733              "%s",
734              "Could not access PEERINFO service.  Exiting.\n");
735     return;
736   }
737   if ( (GNUNET_YES == get_self) ||
738        (GNUNET_YES == get_uri) ||
739        (NULL != dump_hello) )
740   {
741     gh = GNUNET_TRANSPORT_hello_get (cfg,
742                                      GNUNET_TRANSPORT_AC_ANY,
743                                      &hello_callback,
744                                      NULL);
745   }
746   else
747   {
748     tt = GNUNET_SCHEDULER_add_now (&state_machine,
749                                    NULL);
750   }
751   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
752                                  NULL);
753 }
754
755
756 /**
757  * Main state machine that goes over all options and
758  * runs the next requested function.
759  *
760  * @param cls unused
761  */
762 static void
763 state_machine (void *cls)
764 {
765   tt = NULL;
766
767   if (NULL != put_uri)
768   {
769     GPI_plugins_load (cfg);
770     if (GNUNET_SYSERR == parse_hello_uri (put_uri))
771     {
772       fprintf (stderr,
773                _("Invalid URI `%s'\n"),
774                put_uri);
775       GNUNET_SCHEDULER_shutdown ();
776     }
777     GNUNET_free (put_uri);
778     put_uri = NULL;
779   }
780   else if (GNUNET_YES == get_info)
781   {
782     get_info = GNUNET_NO;
783     GPI_plugins_load (cfg);
784     pic = GNUNET_PEERINFO_iterate (peerinfo,
785                                    include_friend_only,
786                                    NULL,
787                                    &print_peer_info,
788                                    NULL);
789   }
790   else if (GNUNET_YES == get_self)
791   {
792     get_self = GNUNET_NO;
793     if (be_quiet)
794       printf ("%s\n",
795               GNUNET_i2s_full (&my_peer_identity));
796     else
797       printf (_("I am peer `%s'.\n"),
798               GNUNET_i2s_full (&my_peer_identity));
799     tt = GNUNET_SCHEDULER_add_now (&state_machine,
800                                    NULL);
801   }
802   else if (GNUNET_YES == get_uri)
803   {
804     GPI_plugins_load (cfg);
805     pic = GNUNET_PEERINFO_iterate (peerinfo,
806                                    include_friend_only,
807                                    &my_peer_identity,
808                                    &print_my_uri,
809                                    NULL);
810     get_uri = GNUNET_NO;
811   }
812   else if (GNUNET_YES == default_operation)
813   {
814     /* default operation list all */
815     default_operation = GNUNET_NO;
816     get_info = GNUNET_YES;
817     tt = GNUNET_SCHEDULER_add_now (&state_machine,
818                                    NULL);
819   }
820   else
821   {
822     GNUNET_SCHEDULER_shutdown ();
823   }
824   default_operation = GNUNET_NO;
825 }
826
827
828 /**
829  * The main function to obtain peer information.
830  *
831  * @param argc number of arguments from the command line
832  * @param argv command line arguments
833  * @return 0 ok, 1 on error
834  */
835 int
836 main (int argc, char *const *argv)
837 {
838   struct GNUNET_GETOPT_CommandLineOption options[] = {
839     GNUNET_GETOPT_option_flag ('n',
840                                   "numeric",
841                                   gettext_noop ("don't resolve host names"),
842                                   &no_resolve),
843
844     GNUNET_GETOPT_option_flag ('q',
845                                   "quiet",
846                                   gettext_noop ("output only the identity strings"),
847                                   &be_quiet),
848     GNUNET_GETOPT_option_flag ('f',
849                                   "friends",
850                                   gettext_noop ("include friend-only information"),
851                                   &include_friend_only),
852
853     GNUNET_GETOPT_option_flag ('s',
854                                   "self",
855                                   gettext_noop ("output our own identity only"),
856                                   &get_self),
857     
858     GNUNET_GETOPT_option_flag ('i',
859                                   "info",
860                                   gettext_noop ("list all known peers"),
861                                   &get_info),
862
863     GNUNET_GETOPT_option_string ('d',
864                                  "dump-hello",
865                                  NULL,
866                                  gettext_noop ("dump hello to file"),
867                                  &dump_hello),
868
869     GNUNET_GETOPT_option_flag ('g',
870                                   "get-hello",
871                                   gettext_noop ("also output HELLO uri(s)"),
872                                   &get_uri),
873
874     GNUNET_GETOPT_option_string ('p',
875                                  "put-hello",
876                                  "HELLO",
877                                  gettext_noop ("add given HELLO uri to the database"),
878                                  &put_uri),
879
880     GNUNET_GETOPT_OPTION_END
881   };
882   int ret;
883
884   default_operation = GNUNET_YES;
885   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc,
886                                                  argv,
887                                                  &argc,
888                                                  &argv))
889     return 2;
890
891   ret = (GNUNET_OK ==
892          GNUNET_PROGRAM_run (argc,
893                              argv,
894                              "gnunet-peerinfo",
895                              gettext_noop ("Print information about peers."),
896                              options,
897                              &run,
898                              NULL)) ? 0 : 1;
899   GNUNET_free ((void*) argv);
900   return ret;
901 }
902
903 /* end of gnunet-peerinfo.c */