- expire old keys adaptively
[oweals/gnunet.git] / src / ats-tool / gnunet-ats.c
1 /*
2  This file is part of GNUnet.
3  (C) 2009--2013 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 ats-tool/gnunet-ats.c
23  * @brief ATS command line tool
24  * @author Matthias Wachs
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_ats_service.h"
29 #include "gnunet_transport_service.h"
30
31 /**
32  * FIXME
33  */
34 #define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 5)
35
36 /**
37  * FIXME
38  */
39 #define BIG_M_STRING "unlimited"
40
41 /**
42  * Final status code.
43  */
44 static int ret;
45
46 /**
47  * FIXME
48  */
49 static int results;
50
51 /**
52  * FIXME
53  */
54 static int resolve_addresses_numeric;
55
56 /**
57  * FIXME
58  */
59 static int receive_done;
60
61 /**
62  * For which peer should we change preference values?
63  */
64 static char *pid_str;
65
66 /**
67  * FIXME
68  */
69 static char *type_str;
70
71 /**
72  * FIXME
73  */
74 static unsigned int value;
75
76 /**
77  * FIXME
78  */
79 static int pending;
80
81 /**
82  * Print verbose ATS information
83  */
84 static int verbose;
85
86 /**
87  * List only addresses currently used (active)
88  */
89 static int op_list_used;
90
91 /**
92  * List all addresses
93  */
94 static int op_list_all;
95
96 /**
97  * List all addresses
98  */
99 static int op_set_pref;
100
101 /**
102  * Print quotas configured
103  */
104 static int op_print_quotas;
105
106 /**
107  * Monitor addresses used
108  */
109 static int op_monitor;
110
111 /**
112  * FIXME
113  */
114 static struct GNUNET_ATS_PerformanceHandle *ph;
115
116 /**
117  * FIXME
118  */
119 static struct GNUNET_ATS_AddressListHandle *alh;
120
121 /**
122  * FIXME
123  */
124 static struct GNUNET_CONFIGURATION_Handle *cfg;
125
126 /**
127  * FIXME
128  */
129 static GNUNET_SCHEDULER_TaskIdentifier end_task;
130
131 /**
132  * FIXME
133  */
134 static struct GNUNET_CONTAINER_MultiPeerMap *addresses;
135
136
137 /**
138  * Structure used to remember all pending address resolutions.
139  * We keep address information in here while we talk to transport
140  * to map the address to a string.
141  */
142 struct PendingResolutions
143 {
144   /**
145    * Kept in a DLL.
146    */
147   struct PendingResolutions *next;
148
149   /**
150    * Kept in a DLL.
151    */
152   struct PendingResolutions *prev;
153
154   /**
155    * Copy of the address we are resolving.
156    */
157   struct GNUNET_HELLO_Address *address;
158
159   /**
160    * Handle to the transport request to convert the address
161    * to a string.
162    */
163   struct GNUNET_TRANSPORT_AddressToStringContext *tats_ctx;
164
165   /**
166    * Array of performance data.
167    */
168   struct GNUNET_ATS_Information *ats;
169
170   /**
171    * Length of the @e ats array.
172    */
173   uint32_t ats_count;
174
175   /**
176    * Amount of outbound bandwidth assigned by ATS.
177    */
178   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
179
180   /**
181    * Amount of inbound bandwidth assigned by ATS.
182    */
183   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
184
185   /**
186    * Is this an active address?
187    */
188   int active;
189 };
190
191
192 /**
193  * Information we keep for an address.  Used to avoid
194  * printing the same data multiple times.
195  */
196 struct ATSAddress
197 {
198   /**
199    * Address information.
200    */
201   struct GNUNET_HELLO_Address *address;
202
203   /**
204    * Current outbound bandwidth.
205    */
206   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
207
208   /**
209    * Current inbound bandwidth.
210    */
211   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
212
213   /**
214    * Is this an active address?
215    */
216   int active;
217
218 };
219
220
221 /**
222  * Head of list of pending resolution requests.
223  */
224 static struct PendingResolutions *head;
225
226 /**
227  * Tail of list of pending resolution requests.
228  */
229 static struct PendingResolutions *tail;
230
231
232 /**
233  * Free address corresponding to a given peer.
234  *
235  * @param cls NULL
236  * @param key peer identity
237  * @param value the `struct ATSAddress *` to be freed
238  * @return #GNUNET_YES (always)
239  */
240 static int
241 free_addr_it (void *cls,
242               const struct GNUNET_PeerIdentity *key,
243               void *value)
244 {
245   struct ATSAddress *a = value;
246
247   GNUNET_break (GNUNET_OK ==
248                 GNUNET_CONTAINER_multipeermap_remove (addresses,
249                                                       key,
250                                                       a));
251   GNUNET_HELLO_address_free (a->address);
252   GNUNET_free (a);
253   return GNUNET_OK;
254 }
255
256
257 /**
258  * Task run on shutdown.
259  *
260  * @param cls NULL
261  * @param tc scheduler context
262  */
263 static void
264 end (void *cls,
265      const struct GNUNET_SCHEDULER_TaskContext *tc)
266 {
267   struct PendingResolutions * pr;
268   struct PendingResolutions * next;
269   unsigned int pending;
270
271   if (NULL != alh)
272   {
273     GNUNET_ATS_performance_list_addresses_cancel (alh);
274     alh = NULL;
275   }
276
277   if (NULL != ph)
278   {
279     GNUNET_ATS_performance_done (ph);
280     ph = NULL;
281   }
282
283   pending = 0;
284   next = head;
285   while (NULL != (pr = next))
286   {
287     next = pr->next;
288     GNUNET_CONTAINER_DLL_remove(head, tail, pr);
289     GNUNET_TRANSPORT_address_to_string_cancel (pr->tats_ctx);
290     GNUNET_free(pr->address);
291     GNUNET_free(pr);
292     pending++;
293   }
294   GNUNET_CONTAINER_multipeermap_iterate (addresses,
295                                          &free_addr_it,
296                                          NULL);
297   GNUNET_CONTAINER_multipeermap_destroy (addresses);
298   addresses = NULL;
299
300   if (0 < pending)
301     FPRINTF (stderr,
302              _("%u address resolutions had a timeout\n"),
303              pending);
304   if (op_list_used || op_list_all)
305     FPRINTF (stderr,
306              _("ATS returned results for %u addresses\n"),
307              results);
308   ret = 0;
309 }
310
311
312 /**
313  * Function to call with a textual representation of an address.  This
314  * function will be called several times with different possible
315  * textual representations, and a last time with @address being NULL
316  * to signal the end of the iteration.  Note that @address NULL
317  * always is the last call, regardless of the value in @a res.
318  *
319  * @param cls closure, a `struct PendingResolutions *`
320  * @param address NULL on end of iteration,
321  *        otherwise 0-terminated printable UTF-8 string,
322  *        in particular an empty string if @a res is #GNUNET_NO
323  * @param res result of the address to string conversion:
324  *        if #GNUNET_OK: conversion successful
325  *        if #GNUNET_NO: address was invalid (or not supported)
326  *        if #GNUNET_SYSERR: communication error (IPC error)
327  */
328 static void
329 transport_addr_to_str_cb (void *cls,
330                           const char *address,
331                           int res)
332 {
333   struct PendingResolutions *pr = cls;
334   char *ats_str;
335   char *ats_tmp;
336   char *ats_prop_arr[GNUNET_ATS_PropertyCount] = GNUNET_ATS_PropertyStrings;
337   char *ats_prop_value;
338   unsigned int c;
339   uint32_t ats_type;
340   uint32_t ats_value;
341   uint32_t network;
342
343   if (NULL == address)
344   {
345     /* We're done */
346     GNUNET_CONTAINER_DLL_remove(head, tail, pr);
347     GNUNET_free(pr->address);
348     GNUNET_free(pr);
349     pending--;
350
351     if ((GNUNET_YES == receive_done) && (0 == pending))
352     {
353       /* All messages received and no resolutions pending*/
354       if (end_task != GNUNET_SCHEDULER_NO_TASK)
355         GNUNET_SCHEDULER_cancel (end_task);
356       end_task = GNUNET_SCHEDULER_add_now (end, NULL);
357     }
358     return;
359   }
360   switch (res)
361   {
362   case GNUNET_SYSERR:
363     FPRINTF (stderr,
364              "Failed to convert address for peer `%s' plugin `%s' length %lu to string (communication error)\n",
365              GNUNET_i2s (&pr->address->peer),
366              pr->address->transport_name,
367              pr->address->address_length);
368     return;
369   case GNUNET_NO:
370     FPRINTF (stderr,
371              "Failed to convert address for peer `%s' plugin `%s' length %lu to string (address invalid or not supported)\n",
372              GNUNET_i2s (&pr->address->peer),
373              pr->address->transport_name,
374              pr->address->address_length);
375     return;
376   case GNUNET_OK:
377     /* continues below */
378     break;
379   default:
380     GNUNET_break (0);
381     return;
382   }
383
384   ats_str = GNUNET_strdup (pr->active ? _("active ") : _("inactive "));
385   network = GNUNET_ATS_NET_UNSPECIFIED;
386   for (c = 0; c < pr->ats_count; c++)
387   {
388     ats_tmp = ats_str;
389
390     ats_type = ntohl (pr->ats[c].type);
391     ats_value = ntohl (pr->ats[c].value);
392
393     if (ats_type > GNUNET_ATS_PropertyCount)
394     {
395       FPRINTF (stderr,
396                "Invalid ATS property type %u %u for address %s\n",
397                ats_type,
398                pr->ats[c].type,
399                address);
400       continue;
401     }
402
403     switch (ats_type)
404     {
405     case GNUNET_ATS_NETWORK_TYPE:
406       if (ats_value > GNUNET_ATS_NetworkTypeCount)
407       {
408         GNUNET_break(0);
409         continue;
410       }
411       network = ats_value;
412       GNUNET_asprintf (&ats_prop_value,
413                        "%s",
414                        GNUNET_ATS_print_network_type (ats_value));
415       break;
416     default:
417       GNUNET_asprintf (&ats_prop_value, "%u", ats_value);
418       break;
419     }
420     if ((verbose) && (ats_type < GNUNET_ATS_PropertyCount))
421     {
422       GNUNET_asprintf (&ats_str,
423                        "%s%s=%s, ",
424                        ats_tmp,
425                        ats_prop_arr[ats_type],
426                        ats_prop_value);
427       GNUNET_free(ats_tmp);
428     }
429     GNUNET_free(ats_prop_value);
430   }
431
432   FPRINTF (stderr,
433            _("Peer `%s' plugin `%s', address `%s', `%s' bw out: %u Bytes/s, bw in %u Bytes/s, %s\n"),
434            GNUNET_i2s (&pr->address->peer),
435            pr->address->transport_name,
436            address,
437            GNUNET_ATS_print_network_type (network),
438            ntohl (pr->bandwidth_out.value__),
439            ntohl (pr->bandwidth_in.value__),
440            ats_str);
441   GNUNET_free (ats_str);
442 }
443
444
445 /**
446  * Closure for #find_address_it().
447  */
448 struct AddressFindCtx
449 {
450   /**
451    * Address we are looking for.
452    */
453   const struct GNUNET_HELLO_Address *src;
454
455   /**
456    * Where to write the `struct ATSAddress` if we found one that matches.
457    */
458   struct ATSAddress *res;
459 };
460
461
462 /**
463  * Find address corresponding to a given peer.
464  *
465  * @param cls the `struct AddressFindCtx *`
466  * @param key peer identity
467  * @param value the `struct ATSAddress *` for an existing address
468  * @return #GNUNET_NO if we found a match, #GNUNET_YES if not
469  */
470 static int
471 find_address_it (void *cls,
472                  const struct GNUNET_PeerIdentity *key,
473                  void *value)
474 {
475   struct AddressFindCtx *actx = cls;
476   struct ATSAddress *exist = value;
477
478   if (0 == GNUNET_HELLO_address_cmp (actx->src, exist->address))
479   {
480     actx->res = exist;
481     return GNUNET_NO;
482   }
483   return GNUNET_YES;
484 }
485
486
487
488 /**
489  * Signature of a function that is called with QoS information about an address.
490  *
491  * @param cls closure (NULL)
492  * @param address the address, NULL if ATS service was disconnected
493  * @param active #GNUNET_YES if this address is actively used
494  *        to maintain a connection to a peer;
495  *        #GNUNET_NO if the address is not actively used;
496  *        #GNUNET_SYSERR if this address is no longer available for ATS
497  * @param bandwidth_out assigned outbound bandwidth for the connection
498  * @param bandwidth_in assigned inbound bandwidth for the connection
499  * @param ats performance data for the address (as far as known)
500  * @param ats_count number of performance records in @a ats
501  */
502 static void
503 ats_perf_mon_cb (void *cls,
504                  const struct GNUNET_HELLO_Address *address,
505                  int active,
506                  struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
507                  struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
508                  const struct GNUNET_ATS_Information *ats,
509                  uint32_t ats_count)
510 {
511   struct PendingResolutions *pr;
512   struct PendingResolutions *cur;
513   struct PendingResolutions *next;
514
515   if (NULL == address)
516   {
517     /* ATS service temporarily disconnected, remove current state */
518     next = head;
519     for (cur = next; NULL != cur; cur = next)
520     {
521       next = cur->next;
522       GNUNET_CONTAINER_DLL_remove (head, tail, cur);
523       GNUNET_TRANSPORT_address_to_string_cancel (cur->tats_ctx);
524       GNUNET_HELLO_address_free (cur->address);
525       GNUNET_free (cur);
526     }
527
528     GNUNET_CONTAINER_multipeermap_iterate (addresses,
529                                            &free_addr_it,
530                                            NULL);
531     return;
532   }
533   if (GNUNET_SYSERR == active)
534   {
535     /* remove address */
536     struct AddressFindCtx actx;
537
538     actx.src = address;
539     actx.res = NULL;
540     GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
541                                                 &address->peer,
542                                                 &find_address_it,
543                                                 &actx);
544     if (NULL == actx.res)
545     {
546       GNUNET_break (0);
547       return;
548     }
549     GNUNET_break (GNUNET_OK ==
550                   GNUNET_CONTAINER_multipeermap_remove (addresses,
551                                                         &address->peer,
552                                                         actx.res));
553     FPRINTF (stderr,
554              _("Removed address of peer `%s' with plugin `%s'\n"),
555              GNUNET_i2s (&address->peer),
556              actx.res->address->transport_name);
557     GNUNET_HELLO_address_free (actx.res);
558     GNUNET_free (actx.res);
559     return;
560   }
561
562   if (GNUNET_NO == verbose)
563   {
564     struct AddressFindCtx actx;
565     struct ATSAddress *a;
566
567     actx.src = address;
568     actx.res = NULL;
569     GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
570                                                 &address->peer,
571                                                 &find_address_it,
572                                                 &actx);
573     if ((NULL != actx.res))
574     {
575       if ((bandwidth_in.value__ == actx.res->bandwidth_in.value__) &&
576           (bandwidth_out.value__ == actx.res->bandwidth_out.value__) &&
577           (active == actx.res->active))
578       {
579         return; /* Nothing to do here */
580       }
581       else
582       {
583         actx.res->bandwidth_in = bandwidth_in;
584         actx.res->bandwidth_out = bandwidth_out;
585       }
586     }
587     else
588     {
589       a = GNUNET_new (struct ATSAddress);
590
591       a->address = GNUNET_HELLO_address_copy(address);
592       a->bandwidth_in = bandwidth_in;
593       a->bandwidth_out = bandwidth_out;
594       a->active = active;
595       GNUNET_CONTAINER_multipeermap_put (addresses,
596                                          &address->peer,
597                                          a,
598                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
599     }
600   }
601
602   pr = GNUNET_malloc (sizeof (struct PendingResolutions) +
603       ats_count * sizeof (struct GNUNET_ATS_Information));
604
605   pr->ats_count = ats_count;
606   pr->ats = (struct GNUNET_ATS_Information *) &pr[1];
607   if (ats_count > 0)
608     memcpy (pr->ats, ats, ats_count * sizeof(struct GNUNET_ATS_Information));
609   pr->address = GNUNET_HELLO_address_copy (address);
610   pr->bandwidth_in = bandwidth_in;
611   pr->bandwidth_out = bandwidth_out;
612   pr->active = active;
613   pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
614                                                      resolve_addresses_numeric,
615                                                      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
616                                                      &transport_addr_to_str_cb,
617                                                      pr);
618   GNUNET_CONTAINER_DLL_insert (head, tail, pr);
619   results++;
620   pending++;
621 }
622
623
624 /**
625  * Signature of a function that is called with QoS information about an address.
626  *
627  * @param cls closure (NULL)
628  * @param address the address, NULL if ATS service was disconnected
629  * @param active is this address actively used to maintain a connection
630           to a peer
631  * @param bandwidth_out assigned outbound bandwidth for the connection
632  * @param bandwidth_in assigned inbound bandwidth for the connection
633  * @param ats performance data for the address (as far as known)
634  * @param ats_count number of performance records in @a ats
635  */
636 static void
637 ats_perf_cb (void *cls,
638              const struct GNUNET_HELLO_Address *address,
639              int active,
640              struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
641              struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
642              const struct GNUNET_ATS_Information *ats,
643              uint32_t ats_count)
644 {
645   struct PendingResolutions *pr;
646
647   if (NULL == address)
648   {
649     /* All messages received */
650     receive_done = GNUNET_YES;
651     alh = NULL;
652     if (0 == pending)
653     {
654       /* All messages received and no resolutions pending*/
655       if (end_task != GNUNET_SCHEDULER_NO_TASK)
656         GNUNET_SCHEDULER_cancel (end_task);
657       end_task = GNUNET_SCHEDULER_add_now (end, NULL);
658     }
659     return;
660   }
661
662   pr = GNUNET_malloc (sizeof (struct PendingResolutions) +
663       ats_count * sizeof (struct GNUNET_ATS_Information));
664
665   pr->ats_count = ats_count;
666   pr->ats = (struct GNUNET_ATS_Information *) &pr[1];
667   if (ats_count > 0)
668     memcpy (pr->ats, ats, ats_count * sizeof(struct GNUNET_ATS_Information));
669   pr->address = GNUNET_HELLO_address_copy (address);
670   pr->bandwidth_in = bandwidth_in;
671   pr->bandwidth_out = bandwidth_out;
672   pr->active = active;
673   pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
674                                                      resolve_addresses_numeric,
675                                                      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
676                                                      &transport_addr_to_str_cb, pr);
677   GNUNET_CONTAINER_DLL_insert (head, tail, pr);
678   results++;
679   pending++;
680 }
681
682
683 /**
684  * Print information about the quotas configured for the various
685  * network scopes.
686  *
687  * @param cfg configuration to obtain quota information from
688  * @return total number of ATS network types known
689  */
690 static unsigned int
691 print_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg)
692 {
693   char *network_str[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkTypeString;
694   char * entry_in = NULL;
695   char * entry_out = NULL;
696   char * quota_out_str;
697   char * quota_in_str;
698   unsigned long long int quota_out;
699   unsigned long long int quota_in;
700   int c;
701
702   for (c = 0; (c < GNUNET_ATS_NetworkTypeCount); c++)
703   {
704
705     GNUNET_asprintf (&entry_out,
706                      "%s_QUOTA_OUT",
707                      network_str[c]);
708     GNUNET_asprintf (&entry_in,
709                      "%s_QUOTA_IN",
710                      network_str[c]);
711
712     /* quota out */
713     if (GNUNET_OK ==
714         GNUNET_CONFIGURATION_get_value_string (cfg,
715                                                "ats",
716                                                entry_out,
717                                                &quota_out_str))
718     {
719       if (0 == strcmp (quota_out_str, BIG_M_STRING)
720           || (GNUNET_SYSERR ==
721               GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str,
722                                                   &quota_out)))
723         quota_out = UINT32_MAX;
724
725       GNUNET_free(quota_out_str);
726       GNUNET_asprintf (&quota_out_str, "%llu", quota_out);
727     }
728     else
729     {
730       FPRINTF (stderr,
731                "Outbound quota for network `%11s' not configured!\n",
732                network_str[c]);
733       GNUNET_asprintf (&quota_out_str, "-");
734     }
735     GNUNET_free(entry_out);
736
737     /* quota in */
738     if (GNUNET_OK ==
739         GNUNET_CONFIGURATION_get_value_string (cfg,
740                                                "ats",
741                                                entry_in,
742                                                &quota_in_str))
743     {
744       if (0 == strcmp (quota_in_str, BIG_M_STRING)
745           || (GNUNET_SYSERR ==
746               GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, &quota_in)))
747         quota_in = UINT32_MAX;
748       GNUNET_free(quota_in_str);
749       GNUNET_asprintf (&quota_in_str, "%llu", quota_in);
750     }
751     else
752     {
753       FPRINTF (stderr,
754                "Inbound quota for network `%11s' not configured!\n",
755                network_str[c]);
756       GNUNET_asprintf (&quota_in_str, "-");
757     }
758     GNUNET_free(entry_in);
759
760     FPRINTF (stderr,
761              _("Quota for network `%11s' (in/out): %10s / %10s\n"),
762              network_str[c],
763              quota_in_str,
764              quota_out_str);
765     GNUNET_free(quota_out_str);
766     GNUNET_free(quota_in_str);
767   }
768   return GNUNET_ATS_NetworkTypeCount;
769 }
770
771
772 /**
773  * Function called with the result from the test if ATS is
774  * running.  Runs the actual main logic.
775  *
776  * @param cls the `struct GNUNET_CONFIGURATION_Handle *`
777  * @param result result of the test, #GNUNET_YES if ATS is running
778  */
779 static void
780 testservice_ats (void *cls,
781                  int result)
782 {
783   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
784   struct GNUNET_PeerIdentity pid;
785   unsigned int c;
786   unsigned int type;
787
788   addresses = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_YES);
789
790   if (GNUNET_YES != result)
791   {
792     FPRINTF (stderr,
793              _("Service `%s' is not running\n"),
794              "ats");
795     return;
796   }
797
798   results = 0;
799
800   if (NULL != pid_str)
801   {
802     if (GNUNET_OK
803         != GNUNET_CRYPTO_eddsa_public_key_from_string (pid_str,
804             strlen (pid_str), &pid.public_key))
805     {
806       FPRINTF (stderr,
807                _("Failed to parse peer identity `%s'\n"),
808                pid_str);
809       return;
810     }
811   }
812
813   c = op_list_all + op_list_used + op_monitor + op_set_pref;
814   if ((1 < c))
815   {
816     FPRINTF (stderr,
817              _("Please select one operation : %s or %s or %s or %s or %s\n"),
818              "--used",
819              "--all",
820              "--monitor",
821              "--preference",
822              "--quotas");
823     return;
824   }
825   if ((0 == c))
826     op_list_used = GNUNET_YES; /* set default */
827   if (op_print_quotas)
828   {
829     ret = print_quotas (cfg);
830     return;
831   }
832   if (op_list_all)
833   {
834     ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
835     if (NULL == ph)
836     {
837       FPRINTF (stderr,
838                "%s",
839                _("Cannot connect to ATS service, exiting...\n"));
840       return;
841     }
842
843     alh = GNUNET_ATS_performance_list_addresses (ph,
844                                                  (NULL == pid_str) ? NULL : &pid,
845                                                  GNUNET_YES,
846                                                  &ats_perf_cb, NULL);
847     if (NULL == alh)
848     {
849       FPRINTF (stderr,
850                "%s",
851                _("Cannot issue request to ATS service, exiting...\n"));
852       end_task = GNUNET_SCHEDULER_add_now (&end, NULL);
853       return;
854     }
855     end_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
856                                              &end,
857                                              NULL);
858   }
859   else if (op_list_used)
860   {
861     ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
862     if (NULL == ph)
863       FPRINTF (stderr,
864                "%s",
865                _("Cannot connect to ATS service, exiting...\n"));
866
867     alh = GNUNET_ATS_performance_list_addresses (ph,
868                                                  (NULL == pid_str)
869                                                  ? NULL
870                                                  : &pid,
871                                                  GNUNET_NO,
872                                                  &ats_perf_cb, NULL);
873     if (NULL == alh)
874     {
875       FPRINTF (stderr,
876                "%s",
877                _("Cannot issue request to ATS service, exiting...\n"));
878       end_task = GNUNET_SCHEDULER_add_now (&end, NULL);
879       return;
880     }
881     end_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
882                                              &end,
883                                              NULL);
884   }
885   else if (op_monitor)
886   {
887     ph = GNUNET_ATS_performance_init (cfg,
888                                       &ats_perf_mon_cb,
889                                       NULL);
890     if (NULL == ph)
891       FPRINTF (stderr,
892                "%s",
893                _("Cannot connect to ATS service, exiting...\n"));
894     end_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
895                                              &end,
896                                              NULL);
897
898   }
899   else if (op_set_pref)
900   {
901     if (NULL == type_str)
902     {
903       FPRINTF (stderr,
904                "%s",
905                _("No preference type given!\n"));
906       return;
907     }
908     if (NULL == pid_str)
909     {
910       FPRINTF (stderr,
911                "%s",
912                _("No peer given!\n"));
913       return;
914     }
915
916     for (c = 0; c < strlen (type_str); c++)
917     {
918       if (isupper (type_str[c]))
919         type_str[c] = tolower (type_str[c]);
920     }
921
922     if (0 == strcasecmp ("latency", type_str))
923       type = GNUNET_ATS_PREFERENCE_LATENCY;
924     else if (0 == strcasecmp ("bandwidth", type_str))
925       type = GNUNET_ATS_PREFERENCE_BANDWIDTH;
926     else
927     {
928       FPRINTF (stderr,
929                "%s",
930                _("Valid type required\n"));
931       return;
932     }
933
934     /* set */
935     ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
936     if (NULL == ph)
937       FPRINTF (stderr,
938                "%s",
939                _("Cannot connect to ATS service, exiting...\n"));
940
941     GNUNET_ATS_performance_change_preference (ph, &pid, type, (double) value,
942                                               GNUNET_ATS_PREFERENCE_END);
943
944     end_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
945                                              &end,
946                                              NULL);
947   }
948   ret = 1;
949 }
950
951
952 /**
953  * Main function that will be run by the scheduler.
954  *
955  * @param cls closure
956  * @param args remaining command-line arguments
957  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
958  * @param my_cfg configuration
959  */
960 static void
961 run (void *cls,
962      char * const *args,
963      const char *cfgfile,
964      const struct GNUNET_CONFIGURATION_Handle *my_cfg)
965 {
966   cfg = (struct GNUNET_CONFIGURATION_Handle *) my_cfg;
967   GNUNET_CLIENT_service_test ("ats", cfg, TIMEOUT,
968                               &testservice_ats,
969                               (void *) cfg);
970 }
971
972
973 /**
974  * The main function.
975  *
976  * @param argc number of arguments from the command line
977  * @param argv command line arguments
978  * @return 0 ok, 1 on error
979  */
980 int
981 main (int argc,
982       char * const *argv)
983 {
984   int res;
985
986   resolve_addresses_numeric = GNUNET_NO;
987   op_monitor = GNUNET_NO;
988   op_list_all = GNUNET_NO;
989   op_list_used = GNUNET_NO;
990   op_set_pref = GNUNET_NO;
991   pending = 0;
992   receive_done = GNUNET_NO;
993   type_str = NULL;
994
995   static const struct GNUNET_GETOPT_CommandLineOption options[] =
996   {
997   { 'u', "used", NULL,
998       gettext_noop ("get list of active addresses currently used"), 0,
999       &GNUNET_GETOPT_set_one, &op_list_used },
1000   { 'a', "all", NULL, gettext_noop ("get list of all active addresses"), 0,
1001       &GNUNET_GETOPT_set_one, &op_list_all },
1002   { 'n', "numeric", NULL,
1003       gettext_noop ("do not resolve IP addresses to hostnames"), 0,
1004       &GNUNET_GETOPT_set_one, &resolve_addresses_numeric },
1005   { 'm', "monitor", NULL, gettext_noop ("monitor mode"), 0,
1006       &GNUNET_GETOPT_set_one, &op_monitor },
1007   { 'p', "preference", NULL, gettext_noop ("set preference for the given peer"),
1008       0, &GNUNET_GETOPT_set_one, &op_set_pref },
1009   { 'q', "quotas", NULL, gettext_noop ("print all configured quotas"), 0,
1010       &GNUNET_GETOPT_set_one, &op_print_quotas },
1011   { 'i', "id", "TYPE", gettext_noop ("peer id"), 1, &GNUNET_GETOPT_set_string,
1012       &pid_str },
1013   { 't', "type", "TYPE",
1014       gettext_noop ("preference type to set: latency | bandwidth"), 1,
1015       &GNUNET_GETOPT_set_string, &type_str },
1016   { 'k', "value", "VALUE", gettext_noop ("preference value"), 1,
1017       &GNUNET_GETOPT_set_uint, &value },
1018   { 'V', "verbose", NULL,
1019       gettext_noop ("verbose output (include ATS address properties)"), 0,
1020       &GNUNET_GETOPT_set_one, &verbose }, GNUNET_GETOPT_OPTION_END };
1021
1022   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1023     return 2;
1024
1025   res = GNUNET_PROGRAM_run (argc, argv, "gnunet-ats",
1026                             gettext_noop ("Print information about ATS state"),
1027                             options,
1028                             &run, NULL);
1029   GNUNET_free_non_null(pid_str);
1030   GNUNET_free_non_null(type_str);
1031   GNUNET_free((void *) argv);
1032
1033   if (GNUNET_OK == res)
1034     return ret;
1035   else
1036     return 1;
1037
1038 }
1039
1040 /* end of gnunet-ats.c */